[PATCH v2 01/11] linux-user: handle /proc/self/exe for execve

YAMAMOTO Takashi posted 11 patches 3 years, 5 months ago
Maintainers: Laurent Vivier <laurent@vivier.eu>
[PATCH v2 01/11] linux-user: handle /proc/self/exe for execve
Posted by YAMAMOTO Takashi 3 years, 5 months ago
It seems somehow common to execve /proc/self/exe in docker
or golang community these days.
At least, moby "reexec" and runc "libcontainer" do that.

Signed-off-by: YAMAMOTO Takashi <yamamoto@midokura.com>
---
 linux-user/syscall.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index c9f812091c..a2b03ecb8b 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8470,6 +8470,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
 #endif
     case TARGET_NR_execve:
         {
+            const char *path;
             char **argp, **envp;
             int argc, envc;
             abi_ulong gp;
@@ -8537,7 +8538,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
              * before the execve completes and makes it the other
              * program's problem.
              */
-            ret = get_errno(safe_execve(p, argp, envp));
+            path = p;
+            if (is_proc_myself(path, "exe")) {
+                path = exec_path;
+            }
+            ret = get_errno(safe_execve(path, argp, envp));
             unlock_user(p, arg1, 0);
 
             goto execve_end;
-- 
2.21.1 (Apple Git-122.3)


Re: [PATCH v2 01/11] linux-user: handle /proc/self/exe for execve
Posted by Laurent Vivier 3 years, 4 months ago
Le 31/05/2021 à 07:50, YAMAMOTO Takashi a écrit :
> It seems somehow common to execve /proc/self/exe in docker
> or golang community these days.
> At least, moby "reexec" and runc "libcontainer" do that.
> 
> Signed-off-by: YAMAMOTO Takashi <yamamoto@midokura.com>
> ---
>  linux-user/syscall.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index c9f812091c..a2b03ecb8b 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -8470,6 +8470,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
>  #endif
>      case TARGET_NR_execve:
>          {
> +            const char *path;
>              char **argp, **envp;
>              int argc, envc;
>              abi_ulong gp;
> @@ -8537,7 +8538,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
>               * before the execve completes and makes it the other
>               * program's problem.
>               */
> -            ret = get_errno(safe_execve(p, argp, envp));
> +            path = p;
> +            if (is_proc_myself(path, "exe")) {
> +                path = exec_path;
> +            }
> +            ret = get_errno(safe_execve(path, argp, envp));
>              unlock_user(p, arg1, 0);
>  
>              goto execve_end;
> 

The problem here is QEMU can fail to execute the file directly.

The binary can be launched with binfmt_misc and the 'O' flag:

     ``O`` - open-binary
            Legacy behavior of binfmt_misc is to pass the full path
            of the binary to the interpreter as an argument. When this flag is
            included, binfmt_misc will open the file for reading and pass its
            descriptor as an argument, instead of the full path, thus allowing
            the interpreter to execute non-readable binaries. This feature
            should be used with care - the interpreter has to be trusted not to
            emit the contents of the non-readable binary.

You should use do_openat() (that resolves the /proc/self/exe path) and fexecve().

Thanks,
Laurent

Re: [PATCH v2 01/11] linux-user: handle /proc/self/exe for execve
Posted by Takashi Yamamoto 3 years, 4 months ago
On Sun, Jun 20, 2021 at 11:14 PM Laurent Vivier <laurent@vivier.eu> wrote:
>
> Le 31/05/2021 à 07:50, YAMAMOTO Takashi a écrit :
> > It seems somehow common to execve /proc/self/exe in docker
> > or golang community these days.
> > At least, moby "reexec" and runc "libcontainer" do that.
> >
> > Signed-off-by: YAMAMOTO Takashi <yamamoto@midokura.com>
> > ---
> >  linux-user/syscall.c | 7 ++++++-
> >  1 file changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> > index c9f812091c..a2b03ecb8b 100644
> > --- a/linux-user/syscall.c
> > +++ b/linux-user/syscall.c
> > @@ -8470,6 +8470,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
> >  #endif
> >      case TARGET_NR_execve:
> >          {
> > +            const char *path;
> >              char **argp, **envp;
> >              int argc, envc;
> >              abi_ulong gp;
> > @@ -8537,7 +8538,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
> >               * before the execve completes and makes it the other
> >               * program's problem.
> >               */
> > -            ret = get_errno(safe_execve(p, argp, envp));
> > +            path = p;
> > +            if (is_proc_myself(path, "exe")) {
> > +                path = exec_path;
> > +            }
> > +            ret = get_errno(safe_execve(path, argp, envp));
> >              unlock_user(p, arg1, 0);
> >
> >              goto execve_end;
> >
>
> The problem here is QEMU can fail to execute the file directly.

i don't understand this sentence. can you explain a bit?

>
> The binary can be launched with binfmt_misc and the 'O' flag:
>
>      ``O`` - open-binary
>             Legacy behavior of binfmt_misc is to pass the full path
>             of the binary to the interpreter as an argument. When this flag is
>             included, binfmt_misc will open the file for reading and pass its
>             descriptor as an argument, instead of the full path, thus allowing
>             the interpreter to execute non-readable binaries. This feature
>             should be used with care - the interpreter has to be trusted not to
>             emit the contents of the non-readable binary.
>
> You should use do_openat() (that resolves the /proc/self/exe path) and fexecve().

i thought there was an issue with the approach. but i don't remember
what it was.
maybe i will retry it.

>
> Thanks,
> Laurent

Re: [PATCH v2 01/11] linux-user: handle /proc/self/exe for execve
Posted by Laurent Vivier 3 years, 4 months ago
Le 21/06/2021 à 04:02, Takashi Yamamoto a écrit :
> On Sun, Jun 20, 2021 at 11:14 PM Laurent Vivier <laurent@vivier.eu> wrote:
>>
>> Le 31/05/2021 à 07:50, YAMAMOTO Takashi a écrit :
>>> It seems somehow common to execve /proc/self/exe in docker
>>> or golang community these days.
>>> At least, moby "reexec" and runc "libcontainer" do that.
>>>
>>> Signed-off-by: YAMAMOTO Takashi <yamamoto@midokura.com>
>>> ---
>>>  linux-user/syscall.c | 7 ++++++-
>>>  1 file changed, 6 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
>>> index c9f812091c..a2b03ecb8b 100644
>>> --- a/linux-user/syscall.c
>>> +++ b/linux-user/syscall.c
>>> @@ -8470,6 +8470,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
>>>  #endif
>>>      case TARGET_NR_execve:
>>>          {
>>> +            const char *path;
>>>              char **argp, **envp;
>>>              int argc, envc;
>>>              abi_ulong gp;
>>> @@ -8537,7 +8538,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
>>>               * before the execve completes and makes it the other
>>>               * program's problem.
>>>               */
>>> -            ret = get_errno(safe_execve(p, argp, envp));
>>> +            path = p;
>>> +            if (is_proc_myself(path, "exe")) {
>>> +                path = exec_path;
>>> +            }
>>> +            ret = get_errno(safe_execve(path, argp, envp));
>>>              unlock_user(p, arg1, 0);
>>>
>>>              goto execve_end;
>>>
>>
>> The problem here is QEMU can fail to execute the file directly.
> 
> i don't understand this sentence. can you explain a bit?

It's related to the text below.
The binary can be executable ('x') but not readable ('r'), so QEMU cannot load it.
It's the purpose of the 'O' flag: kernel opens the file and pass the FD to QEMU to execute it.

Thanks,
Laurent

> 
>>
>> The binary can be launched with binfmt_misc and the 'O' flag:
>>
>>      ``O`` - open-binary
>>             Legacy behavior of binfmt_misc is to pass the full path
>>             of the binary to the interpreter as an argument. When this flag is
>>             included, binfmt_misc will open the file for reading and pass its
>>             descriptor as an argument, instead of the full path, thus allowing
>>             the interpreter to execute non-readable binaries. This feature
>>             should be used with care - the interpreter has to be trusted not to
>>             emit the contents of the non-readable binary.
>>
>> You should use do_openat() (that resolves the /proc/self/exe path) and fexecve().
> 
> i thought there was an issue with the approach. but i don't remember
> what it was.
> maybe i will retry it.
> 
>>
>> Thanks,
>> Laurent