[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [XEN PATCH 1/1] tools/libxl: search PATH for QEMU if `QEMU_XEN_PATH` is not absolute



On Mon, Dec 23, 2024 at 11:17:53AM +0800, Hongbo wrote:
> `QEMU_XEN_PATH` will be configured as `qemu-system-i386` with no clue where, 
> if
> `--with-system-qemu` is set without giving a path (as matched in the case 
> `yes`
> but not `*`). However, the existence of the executable is checked by 
> `access()`,
> that will not look for anywhere in $PATH but the current directory. And since 
> it
> is possible for `qemu-system-i386` (or any other configured values) to be
> executed from PATH later, we'd better find that in PATH and return the full 
> path
> for the caller to check against.
> 
> Signed-off-by: Hongbo <hehongbo@xxxxxxxx>
> 
> ---
> 
> This patch is from the maintenance team of the Xen Project Hypervisor at 
> NixOS.
> We encountered this and thought it was an edge case, and came up with this 
> while
> maintaining the package and module of the Hypervisor at NixOS.
> 
> According to `xen.git/tools/configure.ac`, `QEMU_XEN_PATH` will be configured 
> as
> `qemu-system-i386` (relative) if `--with-system-qemu` is set (as `yes`) but
> without an absolute path. However, it will execute `qemu-system-i386` from the
> `PATH` only if a file is called `qemu-system-i386` in the *current directory*.
> That is because the existence of the device model executable file, in this 
> case
> `qemu-system-i386`, is checked via `access()` without concatenating it with
> current PATHs. And `access()` is not tailored for executables, it will not
> search for the PATHs for us.
> 
> See `libxl__domain_build_info_setdefault()` at
> `xen.git/tools/libs/light/libxl_create.c`. It reads `dm` from
> `libxl__domain_device_model()` and then uses `access()` on it. If that fails, 
> it
> will modify the `device_model_version` to qemu-traditional. Then, in
> `libxl__spawn_local_dm()` at `xen.git/tools/lib/light/libxl_dm.c`, it reads 
> from
> `libxl__domain_device_model()` again, and `access()` is used again to detect 
> the
> file's existence. In my investigations, if I comment out these 2 existence
> checks then it will run `qemu-system-i386` from the current PATH without 
> issues.
> I guess if it's not blocked by those 2 checks, it will finally reach
> `libxl__exec()`. Then, inside the `libxl__exec()` the device model executable
> will be executed by `execvp()`, which can certainly call the executable from
> both absolute paths and current PATHs.
> 
> Since the device model executable will be checked twice, both of which will 
> call
> `libxl__domain_device_model()` to get its location, I think the preferred
> solution would be patching the `libxl__domain_device_model()` function itself 
> to
> tell if we're referring to an executable in PATHs, and resolve to the full 
> path
> of it for the caller to check against.
> 
> It's indeed an edge case. But why would we need this? Because in Nix (the
> package manager) and NixOS, we use Nix expressions to declare dependencies on
> the dependents, and we ran into the issue of circular dependency - to build
> QEMU with Xen support, we should give the Xen header and libraries into the
> building process of QEMU, that makes Xen (`pkgs.xen`) a dependency of QEMU
> (`pkgs.qemu_xen`), which prevents us from using `pkgs.qemu_xen` in the 
> building
> process of Xen, and in `--with-system-qemu=` argument in particular. It is 
> very
> different compared to those distros and package managers that follow the
> Filesystem Hierarchy Standard (FHS), in which Xen can be built with
> `--with-system-qemu=` points to a non-existent FHS location of
> `qemu-system-i386`, and then use these Xen libraries from the artifacts to 
> build
> QEMU afterward. So we decide to build Xen with `--with-system-qemu` but not
> including an executable path, taking advantage of the fact that 
> `QEMU_XEN_PATH`
> can be configured as a relative `qemu-system-i386` when omitted, as declared 
> as
> the `yes` case in `xen.git/tools/configure.ac`, and that results in we finding
> the aforementioned "current directory" issue, and submitting this patch.

Thanks. Sounds good, and also the feature seems half-way there already
with the default to a binary name and the use of execvp. The patch looks
mostly fine, I still have a few comments.

> In the patch, I'm using the existence of slash (`/`) to tell if 
> `QEMU_XEN_PATH`
> is relative, and begin to search in PATH if it is. I'm sort of iffy on this,
> would it make more sense if we do this on inputs starting with a slash 
> instead?
> And should we notify the user if it's not found anywhere in the PATH thus
> proceeding with the value configured in `QEMU_XEN_PATH` as-is?

Well, on one hand, that's how execvp() works, if there's a '/' in the
path, search in $PATH is skipped. But that would be a buggy scenario
because when you reboot a guest, the working directory isn't the same
anymore, at least with `xl`. So if `pwd`/dir/qemu-dm exist when you
execute `xl create`, but then run `reboot` in a guest, there's an "xl
daemon" that would try to reboot the guest with /dir/qemu-dm because
`pwd` would be just /.

So I think checking that QEMU_XEN_PATH is absolute would be enough here.

> Let me know if it's appropriate and if further changes are needed.
> 
> Best regards,
> Hongbo
> 
> ---
> CC: Anthony PERARD <anthony.perard@xxxxxxxxxx>
> CC: Juergen Gross <jgross@xxxxxxxx>
> ---
>  tools/libs/light/libxl_dm.c | 34 +++++++++++++++++++++++++++++++++-
>  1 file changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c
> index 1f2f5bd97a..db05f20a5b 100644
> --- a/tools/libs/light/libxl_dm.c
> +++ b/tools/libs/light/libxl_dm.c
> @@ -332,7 +332,39 @@ const char *libxl__domain_device_model(libxl__gc *gc,
>              dm = libxl__abs_path(gc, "qemu-dm", 
> libxl__private_bindir_path());
>              break;
>          case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
> -            dm = qemu_xen_path(gc);
> +            const char *configured_dm = qemu_xen_path(gc);

Here, `clang` complained about that new variable, it expect an
expression. Can you put the "case" in a block {} ?

> +            if (strchr(configured_dm, '/'))

As discuste before, just checking for absolute path should be good
enough here.

> +                dm = libxl__strdup(gc, configured_dm);

The function used to return the value from qemu_xen_path(gc), so you can
keep returning `configured_dm`, no need for an strdup, here or the two
other strdup(configured_dm) below.

Also as the `else` part have a block {}, it would be better to have a
block {} for the "true" part of the `if` as well.


> +            else
> +            {
> +                const char *path_env = getenv("PATH");
> +                if (!path_env)
> +                    dm = libxl__strdup(gc, configured_dm);
> +                else
> +                {
> +                    char *path_dup = libxl__strdup(gc, path_env);
> +                    char *saveptr;
> +
> +                    char *path = strtok_r(path_dup, ":", &saveptr);
> +                    char fullpath[PATH_MAX];
> +                    bool dm_found = false;
> +                    while (path)
> +                    {
> +                        snprintf(fullpath, sizeof(fullpath), "%s/%s", path,
> +                                 configured_dm);
> +                        if (access(fullpath, X_OK) == 0)
> +                        {
> +                            dm = libxl__strdup(gc, fullpath);
> +                            dm_found = true;
> +                            break;
> +                        }
> +                        path = strtok_r(NULL, ":", &saveptr);
> +                    }
> +
> +                    if (!dm_found)
> +                        dm = libxl__strdup(gc, configured_dm);
> +                }
> +            }
>              break;
>          default:
>              LOG(ERROR, "invalid device model version %d",

Thanks,

-- 

Anthony Perard | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.