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)
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
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
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
© 2016 - 2026 Red Hat, Inc.