]> git.pld-linux.org Git - packages/qemu.git/blob - qemu-user-execve.patch
use v3 patch from patchwork
[packages/qemu.git] / qemu-user-execve.patch
1 https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/
2 https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7
3
4 https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7#commitcomment-17193923
5 It has gone through review[1][2][3] and I'm waiting for the maintainer of the linux-user subsystem to accept it in his tree.
6
7 [1] https://patchwork.ozlabs.org/patch/569452/
8 [2] https://patchwork.ozlabs.org/patch/573877/
9 [3] https://patchwork.ozlabs.org/patch/582756/
10
11 From patchwork Mon Feb 15 05:51:47 2016
12 Content-Type: text/plain; charset="utf-8"
13 MIME-Version: 1.0
14 Content-Transfer-Encoding: 7bit
15 Subject: [v3] linux-user: add option to intercept execve() syscalls
16 From: Petros Angelatos <petrosagg@resin.io>
17 X-Patchwork-Id: 582756
18 Message-Id: <1455515507-26877-1-git-send-email-petrosagg@resin.io>
19 To: qemu-devel@nongnu.org
20 Cc: lucas.kaldstrom@hotmail.co.uk, peter.maydell@linaro.org,
21  riku.voipio@iki.fi, 
22  laurent@vivier.eu, Petros Angelatos <petrosagg@resin.io>
23 Date: Sun, 14 Feb 2016 21:51:47 -0800
24
25 In order for one to use QEMU user mode emulation under a chroot, it is
26 required to use binfmt_misc. This can be avoided by QEMU never doing a
27 raw execve() to the host system.
28
29 Introduce a new option, -execve, that uses the current QEMU interpreter
30 to intercept execve().
31
32 qemu_execve() will prepend the interpreter path , similar to what
33 binfmt_misc would do, and then pass the modified execve() to the host.
34
35 It is necessary to parse hashbang scripts in that function otherwise
36 the kernel will try to run the interpreter of a script without QEMU and
37 get an invalid exec format error.
38
39 Signed-off-by: Petros Angelatos <petrosagg@resin.io>
40 Tested-by: Laurent Vivier <laurent@vivier.eu>
41 Reviewed-by: Laurent Vivier <laurent@vivier.eu>
42 ---
43 v3 changes:
44         - rebase the patchset against current code
45
46
47  linux-user/main.c    |  36 ++++++++++++++++
48  linux-user/qemu.h    |   1 +
49  linux-user/syscall.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++-
50  3 files changed, 153 insertions(+), 1 deletion(-)
51
52 diff --git a/linux-user/main.c b/linux-user/main.c
53 index e719a2d..0596e6e 100644
54 --- a/linux-user/main.c
55 +++ b/linux-user/main.c
56 @@ -17,6 +17,7 @@
57   *  along with this program; if not, see <http://www.gnu.org/licenses/>.
58   */
59  #include "qemu/osdep.h"
60 +#include <sys/auxv.h>
61  #include <sys/mman.h>
62  #include <sys/syscall.h>
63  #include <sys/resource.h>
64 @@ -75,6 +76,7 @@ static void usage(int exitcode);
65  
66  static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
67  const char *qemu_uname_release;
68 +const char *qemu_execve_path;
69  
70  /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
71     we allocate a bigger stack. Need a better solution, for example
72 @@ -3824,6 +3826,38 @@ static void handle_arg_guest_base(const char *arg)
73      have_guest_base = 1;
74  }
75  
76 +static void handle_arg_execve(const char *arg)
77 +{
78 +    const char *execfn;
79 +    char buf[PATH_MAX];
80 +    char *ret;
81 +    int len;
82 +
83 +    /* try getauxval() */
84 +    execfn = (const char *) getauxval(AT_EXECFN);
85 +
86 +    if (execfn != 0) {
87 +        ret = realpath(execfn, buf);
88 +
89 +        if (ret != NULL) {
90 +            qemu_execve_path = strdup(buf);
91 +            return;
92 +        }
93 +    }
94 +
95 +    /* try /proc/self/exe */
96 +    len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
97 +
98 +    if (len != -1) {
99 +        buf[len] = '\0';
100 +        qemu_execve_path = strdup(buf);
101 +        return;
102 +    }
103 +
104 +    fprintf(stderr, "qemu_execve: unable to determine intepreter's path\n");
105 +    exit(EXIT_FAILURE);
106 +}
107 +
108  static void handle_arg_reserved_va(const char *arg)
109  {
110      char *p;
111 @@ -3909,6 +3943,8 @@ static const struct qemu_argument arg_table[] = {
112       "uname",      "set qemu uname release string to 'uname'"},
113      {"B",          "QEMU_GUEST_BASE",  true,  handle_arg_guest_base,
114       "address",    "set guest_base address to 'address'"},
115 +    {"execve",     "QEMU_EXECVE",      false, handle_arg_execve,
116 +     "",           "use this interpreter when a process calls execve()"},
117      {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
118       "size",       "reserve 'size' bytes for guest virtual address space"},
119      {"d",          "QEMU_LOG",         true,  handle_arg_log,
120 diff --git a/linux-user/qemu.h b/linux-user/qemu.h
121 index bd90cc3..0d9b058 100644
122 --- a/linux-user/qemu.h
123 +++ b/linux-user/qemu.h
124 @@ -140,6 +140,7 @@ void init_task_state(TaskState *ts);
125  void task_settid(TaskState *);
126  void stop_all_tasks(void);
127  extern const char *qemu_uname_release;
128 +extern const char *qemu_execve_path;
129  extern unsigned long mmap_min_addr;
130  
131  /* ??? See if we can avoid exposing so much of the loader internals.  */
132 diff --git a/linux-user/syscall.c b/linux-user/syscall.c
133 index 54ce14a..61b7326 100644
134 --- a/linux-user/syscall.c
135 +++ b/linux-user/syscall.c
136 @@ -99,6 +99,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
137  #include <linux/route.h>
138  #include <linux/filter.h>
139  #include <linux/blkpg.h>
140 +#include <linux/binfmts.h>
141  #include "linux_loop.h"
142  #include "uname.h"
143  
144 @@ -5842,6 +5843,118 @@ static target_timer_t get_timer_id(abi_long arg)
145      return timerid;
146  }
147  
148 +/* qemu_execve() Must return target values and target errnos. */
149 +static abi_long qemu_execve(char *filename, char *argv[],
150 +                  char *envp[])
151 +{
152 +    char *i_arg = NULL, *i_name = NULL;
153 +    char **new_argp;
154 +    int argc, fd, ret, i, offset = 3;
155 +    char *cp;
156 +    char buf[BINPRM_BUF_SIZE];
157 +
158 +    /* normal execve case */
159 +    if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
160 +        return get_errno(execve(filename, argv, envp));
161 +    }
162 +
163 +    for (argc = 0; argv[argc] != NULL; argc++) {
164 +        /* nothing */ ;
165 +    }
166 +
167 +    fd = open(filename, O_RDONLY);
168 +    if (fd == -1) {
169 +        return get_errno(fd);
170 +    }
171 +
172 +    ret = read(fd, buf, BINPRM_BUF_SIZE);
173 +    if (ret == -1) {
174 +        close(fd);
175 +        return get_errno(ret);
176 +    }
177 +
178 +    /* if we have less than 2 bytes, we can guess it is not executable */
179 +    if (ret < 2) {
180 +        close(fd);
181 +        return -host_to_target_errno(ENOEXEC);
182 +    }
183 +
184 +    close(fd);
185 +
186 +    /* adapted from the kernel
187 +     * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
188 +     */
189 +    if ((buf[0] == '#') && (buf[1] == '!')) {
190 +        /*
191 +         * This section does the #! interpretation.
192 +         * Sorta complicated, but hopefully it will work.  -TYT
193 +         */
194 +
195 +        buf[BINPRM_BUF_SIZE - 1] = '\0';
196 +        cp = strchr(buf, '\n');
197 +        if (cp == NULL) {
198 +            cp = buf + BINPRM_BUF_SIZE - 1;
199 +        }
200 +        *cp = '\0';
201 +        while (cp > buf) {
202 +            cp--;
203 +            if ((*cp == ' ') || (*cp == '\t')) {
204 +                *cp = '\0';
205 +            } else {
206 +                break;
207 +            }
208 +        }
209 +        for (cp = buf + 2; (*cp == ' ') || (*cp == '\t'); cp++) {
210 +            /* nothing */ ;
211 +        }
212 +        if (*cp == '\0') {
213 +            return -ENOEXEC; /* No interpreter name found */
214 +        }
215 +        i_name = cp;
216 +        i_arg = NULL;
217 +        for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
218 +            /* nothing */ ;
219 +        }
220 +        while ((*cp == ' ') || (*cp == '\t')) {
221 +            *cp++ = '\0';
222 +        }
223 +        if (*cp) {
224 +            i_arg = cp;
225 +        }
226 +
227 +        if (i_arg) {
228 +            offset = 5;
229 +        } else {
230 +            offset = 4;
231 +        }
232 +    }
233 +
234 +    new_argp = alloca((argc + offset + 1) * sizeof(void *));
235 +
236 +    /* Copy the original arguments with offset */
237 +    for (i = 0; i < argc; i++) {
238 +        new_argp[i + offset] = argv[i];
239 +    }
240 +
241 +    new_argp[0] = strdup(qemu_execve_path);
242 +    new_argp[1] = strdup("-0");
243 +    new_argp[offset] = filename;
244 +    new_argp[argc + offset] = NULL;
245 +
246 +    if (i_name) {
247 +        new_argp[2] = i_name;
248 +        new_argp[3] = i_name;
249 +
250 +        if (i_arg) {
251 +            new_argp[4] = i_arg;
252 +        }
253 +    } else {
254 +        new_argp[2] = argv[0];
255 +    }
256 +
257 +    return get_errno(execve(qemu_execve_path, new_argp, envp));
258 +}
259 +
260  /* do_syscall() should always have a single exit point at the end so
261     that actions, such as logging of syscall results, can be performed.
262     All errnos that do_syscall() returns must be -TARGET_<errcode>. */
263 @@ -6101,7 +6214,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
264  
265              if (!(p = lock_user_string(arg1)))
266                  goto execve_efault;
267 -            ret = get_errno(execve(p, argp, envp));
268 +
269 +            ret = qemu_execve(p, argp, envp);
270 +
271              unlock_user(p, arg1, 0);
272  
273              goto execve_end;
This page took 0.050001 seconds and 3 git commands to generate.