qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH] linux-user: add option to intercept execve() sy


From: Petros Angelatos
Subject: Re: [Qemu-devel] [PATCH] linux-user: add option to intercept execve() syscalls
Date: Fri, 22 Jan 2016 02:01:49 -0800

>> diff --git a/linux-user/main.c b/linux-user/main.c
>> index ee12035..5951279 100644
>> --- a/linux-user/main.c
>> +++ b/linux-user/main.c
>> @@ -79,6 +79,7 @@ static void usage(int exitcode);
>>
>>  static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
>>  const char *qemu_uname_release;
>> +const char *qemu_execve_path;
>>
>>  /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
>>     we allocate a bigger stack. Need a better solution, for example
>> @@ -3828,6 +3829,11 @@ static void handle_arg_guest_base(const char *arg)
>>      have_guest_base = 1;
>>  }
>>
>> +static void handle_arg_execve(const char *arg)
>> +{
>> +    qemu_execve_path = strdup(arg);
>
> I think you can use the parameter just as an on/off switch and
> realpath(argv[0]) as qemu_execve_path.
>
> I don't see any reason to use other binary than the one in use.

This was my initial approach too, but argv[0] can be just the filename
like "qemu-arm-static". And while I could add extra logic to look this
up in the PATH, someone could run it from a completely different
location. Then I looked for a way to get the path of the current
executable but every platform has its own way of doing that and I
didn't want to add all these cases.

https://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe

>> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
>> index 0cbace4..d0b5442 100644
>> --- a/linux-user/syscall.c
>> +++ b/linux-user/syscall.c
>> @@ -5854,6 +5854,109 @@ static target_timer_t get_timer_id(abi_long arg)
>>      return timerid;
>>  }
>>
>> +#define BINPRM_BUF_SIZE 128
>
> This is defined in <linux/binfmts.h>

Got it, I'll add this header and remove the definition.

>
>> +/* qemu_execve() Must return target values and target errnos. */
>> +static abi_long qemu_execve(char *filename, char *argv[],
>> +                  char *envp[])
>> +{
>> +    char *i_arg = NULL, *i_name = NULL;
>> +    char **new_argp;
>> +    int argc, fd, ret, i, offset = 3;
>> +    char *cp;
>> +    char buf[BINPRM_BUF_SIZE];
>> +
>> +    for (argc = 0; argv[argc] != NULL; argc++) {
>> +        /* nothing */ ;
>> +    }
>> +
>> +    fd = open(filename, O_RDONLY);
>> +    if (fd == -1) {
>> +        return -ENOENT;
>
> return -errno; ?

Will fix in v2

>> +    ret = read(fd, buf, BINPRM_BUF_SIZE);
>> +    if (ret == -1) {
>> +        close(fd);
>> +        return -ENOENT;
>
> return -errno; ?

Will fix in v2

>> +    }
>> +
>> +    close(fd);
>> +
>> +    /* adapted from the kernel
>> +     * 
>> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
>> +     */
>> +    if ((buf[0] == '#') && (buf[1] == '!')) {
>
> what happens if read() < 2 ?

Hm, the easy option is for qemu_execve to return ENOEXEC or EIO.
Otherwise I could retry the read N times? I'm not sure how to handle
this if we don't want to return an error.

>> +    /* Copy the original arguments with offset */
>> +    for (i = 0; i < argc; i++) {
>> +        new_argp[i + offset] = argv[i];
>> +    }
>> +
>> +    new_argp[0] = strdup(qemu_execve_path);
>> +    new_argp[1] = strdup("-0");
>> +    new_argp[offset] = filename;
>> +    new_argp[argc + offset] = NULL;
>> +
>> +    if (i_name) {
>> +        new_argp[2] = i_name;
>> +        new_argp[3] = i_name;
>> +
>> +        if (i_arg) {
>> +            new_argp[4] = i_arg;
>> +        }
>> +    } else {
>> +        new_argp[2] = argv[0];
>> +    }
>> +
>> +    return get_errno(execve(qemu_execve_path, new_argp, envp));
>
> duplicate get_errno() with the caller.

I'll add the logic proposed bellow in this function and remove the
duplicate get_errno() from the caller.

>>  /* do_syscall() should always have a single exit point at the end so
>>     that actions, such as logging of syscall results, can be performed.
>>     All errnos that do_syscall() returns must be -TARGET_<errcode>. */
>> @@ -6113,7 +6216,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
>> arg1,
>>
>>              if (!(p = lock_user_string(arg1)))
>>                  goto execve_efault;
>> -            ret = get_errno(execve(p, argp, envp));
>> +
>> +            if (qemu_execve_path && *qemu_execve_path) {
>> +                ret = get_errno(qemu_execve(p, argp, envp));
>> +            } else {
>> +                ret = get_errno(execve(p, argp, envp));
>> +            }
>> +
>
> what do you think of:
>
>     ret = qemu_execve(p, argp, envp);
>
> and in qemu_execve():
>
>     if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
>         return get_errno(execve(p, argp, envp));
>     }
>
> so all the logic is in the function.

Sounds good, I'll include this in v2

Since I'm new to this style of contribution I have a couple of
questions. Is it ok that I deleted part of the patch for my reply to
code review, or should I have replied inline without deleting
anything? Should I send v2 after we resolve all the issues here or
should I send a v2 with proposed fixes but lacking the ones pending
replies?

Thanks,
Petros



reply via email to

[Prev in Thread] Current Thread [Next in Thread]