diff mbox series

[for,6.2,12/49] bsd-user: implement path searching

Message ID 20210807214242.82385-13-imp@bsdimp.com
State New
Headers show
Series bsd-user updates to run hello world | expand

Commit Message

Warner Losh Aug. 7, 2021, 9:42 p.m. UTC
Use the PATH to find the executable given a bare argument.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
---
 bsd-user/bsdload.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-
 bsd-user/qemu.h    |  3 +-
 2 files changed, 74 insertions(+), 2 deletions(-)

Comments

Richard Henderson Aug. 8, 2021, 5:11 a.m. UTC | #1
On 8/7/21 11:42 AM, Warner Losh wrote:
> +        path = g_strdup(p);
> +        if (path == NULL) {

Only returns null when the input is null, which you've already eliminated.

> +static bool find_in_path(char *path, const char *filename, char *retpath,
> +                         size_t rpsize)
> +{
> +    const char *d;
> +
> +    while ((d = strsep(&path, ":")) != NULL) {
> +        if (*d == '\0') {
> +            d = ".";
> +        }
> +        if (snprintf(retpath, rpsize, "%s/%s", d, filename) >= (int)rpsize) {
> +            continue;
> +        }
> +        if (is_there((const char *)retpath)) {
> +            return true;
> +        }
> +    }
> +    return false;

Hmm.  Fixed size retpath buffer isn't ideal.
Any reason not to use g_find_program_in_path?

I note that we don't search the path at all in linux-user/.


r~
Kyle Evans Aug. 8, 2021, 5:48 a.m. UTC | #2
On Sat, Aug 7, 2021 at 10:11 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 8/7/21 11:42 AM, Warner Losh wrote:
> > +        path = g_strdup(p);
> > +        if (path == NULL) {
>
> Only returns null when the input is null, which you've already eliminated.
>
> > +static bool find_in_path(char *path, const char *filename, char *retpath,
> > +                         size_t rpsize)
> > +{
> > +    const char *d;
> > +
> > +    while ((d = strsep(&path, ":")) != NULL) {
> > +        if (*d == '\0') {
> > +            d = ".";
> > +        }
> > +        if (snprintf(retpath, rpsize, "%s/%s", d, filename) >= (int)rpsize) {
> > +            continue;
> > +        }
> > +        if (is_there((const char *)retpath)) {
> > +            return true;
> > +        }
> > +    }
> > +    return false;
>
> Hmm.  Fixed size retpath buffer isn't ideal.
> Any reason not to use g_find_program_in_path?
>

g_find_program_in_path may work well here, as well...

> I note that we don't search the path at all in linux-user/.
>

IIRC imgact_binmisc will have the resolved path but preserve argv as
it should have been were it not emulated, so we have to re-evaluate
the PATH search here because we try to be faithful to the context.

Thanks,

Kyle Evans
Warner Losh Aug. 8, 2021, 5:22 p.m. UTC | #3
On Sat, Aug 7, 2021 at 11:49 PM Kyle Evans <kevans@freebsd.org> wrote:

> On Sat, Aug 7, 2021 at 10:11 PM Richard Henderson
> <richard.henderson@linaro.org> wrote:
> >
> > On 8/7/21 11:42 AM, Warner Losh wrote:
> > > +        path = g_strdup(p);
> > > +        if (path == NULL) {
> >
> > Only returns null when the input is null, which you've already
> eliminated.
> >
> > > +static bool find_in_path(char *path, const char *filename, char
> *retpath,
> > > +                         size_t rpsize)
> > > +{
> > > +    const char *d;
> > > +
> > > +    while ((d = strsep(&path, ":")) != NULL) {
> > > +        if (*d == '\0') {
> > > +            d = ".";
> > > +        }
> > > +        if (snprintf(retpath, rpsize, "%s/%s", d, filename) >=
> (int)rpsize) {
> > > +            continue;
> > > +        }
> > > +        if (is_there((const char *)retpath)) {
> > > +            return true;
> > > +        }
> > > +    }
> > > +    return false;
> >
> > Hmm.  Fixed size retpath buffer isn't ideal.
> > Any reason not to use g_find_program_in_path?
> >
>
> g_find_program_in_path may work well here, as well...
>

Quite well:  1 file changed, 5 insertions(+), 42 deletions(-) so a nice
reduction in code when the unnecessary stuff is removed. I'll have
that in v2 of the series.

> I note that we don't search the path at all in linux-user/.
> >
>
> IIRC imgact_binmisc will have the resolved path but preserve argv as
> it should have been were it not emulated, so we have to re-evaluate
> the PATH search here because we try to be faithful to the context.
>

I just looked at the imgact_binmisc code, and that's what it does, so
that sounds reasonable to me as well.

Warner
diff mbox series

Patch

diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c
index 379015c744..f8030d72bc 100644
--- a/bsd-user/bsdload.c
+++ b/bsd-user/bsdload.c
@@ -139,17 +139,88 @@  abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
     return sp;
 }
 
+static bool is_there(const char *candidate)
+{
+    struct stat fin;
+
+    /* XXX work around access(2) false positives for superuser */
+    if (access(candidate, X_OK) == 0 && stat(candidate, &fin) == 0 &&
+            S_ISREG(fin.st_mode) && (getuid() != 0 ||
+                (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
+        return true;
+    }
+
+    return false;
+}
+
+static bool find_in_path(char *path, const char *filename, char *retpath,
+                         size_t rpsize)
+{
+    const char *d;
+
+    while ((d = strsep(&path, ":")) != NULL) {
+        if (*d == '\0') {
+            d = ".";
+        }
+        if (snprintf(retpath, rpsize, "%s/%s", d, filename) >= (int)rpsize) {
+            continue;
+        }
+        if (is_there((const char *)retpath)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 int loader_exec(const char *filename, char **argv, char **envp,
                 struct target_pt_regs *regs, struct image_info *infop,
                 struct bsd_binprm *bprm)
 {
+    char *p, *path = NULL, fullpath[PATH_MAX];
     int retval, i;
 
     bprm->p = TARGET_PAGE_SIZE * MAX_ARG_PAGES;
     for (i = 0; i < MAX_ARG_PAGES; i++) {       /* clear page-table */
         bprm->page[i] = NULL;
     }
-    retval = open(filename, O_RDONLY);
+
+    if (strchr(filename, '/') != NULL) {
+        path = realpath(filename, fullpath);
+        if (path == NULL) {
+            /* Failed to resolve. */
+            return -1;
+        }
+        if (!is_there(path)) {
+            return -1;
+        }
+        retval = open(path, O_RDONLY);
+        bprm->fullpath = g_strdup(path);
+    } else {
+        p = getenv("PATH");
+        if (p == NULL) {
+            return -1;
+        }
+
+        path = g_strdup(p);
+        if (path == NULL) {
+            fprintf(stderr, "Out of memory\n");
+            return -1;
+        }
+
+        if (!find_in_path(path, filename, fullpath, sizeof(fullpath))) {
+            return -1;
+        }
+        retval = open(fullpath, O_RDONLY);
+        bprm->fullpath = g_strdup(fullpath);
+
+        g_free(path);
+    }
+
+    /* bprm->fullpath must be populated. */
+    if (bprm->fullpath == NULL) {
+        fprintf(stderr, "Out of memory\n");
+        return -1;
+    }
     if (retval < 0) {
         return retval;
     }
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 5237e35f9c..6b601ce4b5 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -124,7 +124,8 @@  struct bsd_binprm {
         int argc, envc;
         char **argv;
         char **envp;
-        char *filename;         /* Name of binary */
+        char *filename;         /* (Given) Name of binary */
+        char *fullpath;         /* Full path of binary */
 };
 
 void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);