On Tue, Apr 01, 2025 at 08:54:04PM +0200, Michal Sekletar wrote:
> Whenever possible, resolve all symlinks as if the sysroot path were a
> chroot environment. This prevents potential interactions with files from
> the host filesystem.
> 
> Signed-off-by: Michal Sekletar <msekl...@redhat.com>
> ---
>  configure.ac                         |  26 ++++++++++++++++++
>  libdwfl/dwfl_segment_report_module.c |  39 +++++++++++++++++++++------
>  libdwfl/link_map.c                   |  32 +++++++++++++++++++++-
>  tests/run-sysroot.sh                 |  32 ++++++++++++++++++++++
>  tests/testfile-sysroot-link.tar.bz2  | Bin 0 -> 2334030 bytes
>  5 files changed, 120 insertions(+), 9 deletions(-)
>  create mode 100644 tests/testfile-sysroot-link.tar.bz2
> 
> diff --git a/configure.ac b/configure.ac
> index 1679a17d..27ea7153 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -283,6 +283,32 @@ case "$CFLAGS" in
>      ;;
>  esac
>  
> +AC_CACHE_CHECK(
> +  [for openat2 with RESOLVE_IN_ROOT support],
> +  [eu_cv_openat2_RESOLVE_IN_ROOT],
> +  [AC_LINK_IFELSE(
> +    [AC_LANG_PROGRAM(
> +      [[#define _GNU_SOURCE
> +        #include <fcntl.h>
> +        #include <stdlib.h>
> +        #include <unistd.h>
> +        #include <sys/syscall.h>
> +        #include <linux/openat2.h>
> +        #include <stdio.h>
> +      ]], [[
> +        struct open_how how = { .flags = O_RDONLY|O_DIRECTORY, .resolve = 
> RESOLVE_IN_ROOT };
> +        int dfd = open (".", O_PATH);
> +        return syscall (SYS_openat2, dfd, ".", &how, sizeof(how)) < 0;
> +      ]]
> +    )],
> +    [eu_cv_openat2_RESOLVE_IN_ROOT=yes],
> +    [eu_cv_openat2_RESOLVE_IN_ROOT=no]
> +  )]
> +)
> +AS_IF([test "x$eu_cv_openat2_RESOLVE_IN_ROOT" = xyes],
> +      [AC_DEFINE([HAVE_OPENAT2_RESOLVE_IN_ROOT], [1], [Define if openat2 is 
> available])]
> +)
> +
>  dnl enable debugging of branch prediction.
>  AC_ARG_ENABLE([debugpred],
>  AS_HELP_STRING([--enable-debugpred],[build binaries with support to debug 
> branch prediction]),
> diff --git a/libdwfl/dwfl_segment_report_module.c 
> b/libdwfl/dwfl_segment_report_module.c
> index 32f44af8..ee723114 100644
> --- a/libdwfl/dwfl_segment_report_module.c
> +++ b/libdwfl/dwfl_segment_report_module.c
> @@ -37,6 +37,12 @@
>  #include <inttypes.h>
>  #include <fcntl.h>
>  
> +#if HAVE_OPENAT2_RESOLVE_IN_ROOT

This has to be #ifdef like below because HAVE_OPENAT2_RESOLVE_IN_ROOT
is defined only when eu_cv_openat2_RESOLVE_IN_ROOT == yes.

> +#include <linux/openat2.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#endif
> +
>  #include <system.h>
>  
>  
> @@ -784,18 +790,35 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const 
> char *name,
>        sysroot if it is set.  */
>        if (dwfl->sysroot && !executable)
>          {
> -       int r;
> -       char *n;
> +#ifdef HAVE_OPENAT2_RESOLVE_IN_ROOT

Here #ifdef is correct.

> +       int sysrootfd;
> +       struct open_how how = {
> +         .flags = O_RDONLY,
> +         .resolve = RESOLVE_IN_ROOT,
> +       };
> +
> +       sysrootfd = open (dwfl->sysroot, O_DIRECTORY|O_PATH);
> +       if (sysrootfd < 0)
> +         return -1;
>  
> -       r = asprintf (&n, "%s%s", dwfl->sysroot, name);
> -       if (r > 0)
> +       fd = syscall (SYS_openat2, sysrootfd, name, &how, sizeof(how));
> +       close (sysrootfd);
> +
> +       if (fd < 0 && errno == ENOSYS)
> +#endif
>           {
> -           fd = open (n, O_RDONLY);
> -           free (n);
> +           int r;
> +           char *n;
> +           r = asprintf (&n, "%s%s", dwfl->sysroot, name);
> +           if (r > 0)
> +             {
> +               fd = open (n, O_RDONLY);
> +               free (n);
> +             }
>           }
> -        }
> +     }
>        else
> -       fd = open (name, O_RDONLY);
> +     fd = open (name, O_RDONLY);
>  
>        if (fd >= 0)
>       {
> diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
> index 8ab14862..c52ed041 100644
> --- a/libdwfl/link_map.c
> +++ b/libdwfl/link_map.c
> @@ -34,6 +34,12 @@
>  
>  #include <fcntl.h>
>  
> +#if HAVE_OPENAT2_RESOLVE_IN_ROOT

It has to be #ifdef here, too.

> +#include <linux/openat2.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +#endif
> +
>  /* This element is always provided and always has a constant value.
>     This makes it an easy thing to scan for to discern the format.  */
>  #define PROBE_TYPE   AT_PHENT
> @@ -418,7 +424,30 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t 
> elfdata,
>         /* This code is mostly inlined dwfl_report_elf.  */
>         char *sysroot_name = NULL;
>         const char *sysroot = dwfl->sysroot;
> +       int fd;
> +
> +#ifdef HAVE_OPENAT2_RESOLVE_IN_ROOT

Here #ifdef is correct.

> +       if (sysroot)
> +         {
> +           int sysrootfd;
> +
> +           struct open_how how = {
> +             .flags = O_RDONLY,
> +             .resolve = RESOLVE_IN_ROOT,
> +           };
>  
> +           sysrootfd = open (sysroot, O_DIRECTORY|O_PATH);
> +           if (sysrootfd < 0)
> +             return -1;
> +
> +           fd = syscall (SYS_openat2, sysrootfd, name, &how, sizeof(how));
> +           close (sysrootfd);
> +
> +           /* Fallback to regular open() is openat2 is not available. */
> +           if (fd < 0 && errno == ENOSYS)
> +             fd = open (name, O_RDONLY);
> +         }
> +#else
>         /* Don't use the sysroot if the path is already inside it.  */
>         bool name_in_sysroot = sysroot && startswith (name, sysroot);
>  

The old code has this startswith check so that the path name is prepended
with sysroot before open() only if that path name is not inside the
sysroot tree already.

I don't see an equivalent for this check in the new code (and the fallback
doesn't prepend sysroot at all), so, unless I'm missing something obvious,
the new code is likely to fail when the path name is inside the sysroot
tree.


-- 
ldv

Reply via email to