]> TLD Linux GIT Repositories - packages/qemu.git/blob - qemu-user-execve.patch
- updated to 6.2.0
[packages/qemu.git] / qemu-user-execve.patch
1 Discussion:
2 https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/
3
4 https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7
5
6 https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7#commitcomment-17193923
7 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.
8
9 [1] https://patchwork.ozlabs.org/patch/569452/
10 [2] https://patchwork.ozlabs.org/patch/573877/
11 [3] https://patchwork.ozlabs.org/patch/582756/
12
13 From patchwork Mon Feb 15 05:51:47 2016
14 Content-Type: text/plain; charset="utf-8"
15 MIME-Version: 1.0
16 Content-Transfer-Encoding: 7bit
17 Subject: [v3] linux-user: add option to intercept execve() syscalls
18 From: Petros Angelatos <petrosagg@resin.io>
19 X-Patchwork-Id: 582756
20 Message-Id: <1455515507-26877-1-git-send-email-petrosagg@resin.io>
21 To: qemu-devel@nongnu.org
22 Cc: lucas.kaldstrom@hotmail.co.uk, peter.maydell@linaro.org,
23  riku.voipio@iki.fi, 
24  laurent@vivier.eu, Petros Angelatos <petrosagg@resin.io>
25 Date: Sun, 14 Feb 2016 21:51:47 -0800
26
27 In order for one to use QEMU user mode emulation under a chroot, it is
28 required to use binfmt_misc. This can be avoided by QEMU never doing a
29 raw execve() to the host system.
30
31 Introduce a new option, -execve, that uses the current QEMU interpreter
32 to intercept execve().
33
34 qemu_execve() will prepend the interpreter path , similar to what
35 binfmt_misc would do, and then pass the modified execve() to the host.
36
37 It is necessary to parse hashbang scripts in that function otherwise
38 the kernel will try to run the interpreter of a script without QEMU and
39 get an invalid exec format error.
40
41 Signed-off-by: Petros Angelatos <petrosagg@resin.io>
42 Tested-by: Laurent Vivier <laurent@vivier.eu>
43 Reviewed-by: Laurent Vivier <laurent@vivier.eu>
44 ---
45 v3 changes:
46         - rebase the patchset against current code
47
48 diff -urNpa qemu-6.2.0.orig/linux-user/main.c qemu-6.2.0/linux-user/main.c
49 --- qemu-6.2.0.orig/linux-user/main.c   2022-01-23 13:51:24.451951509 +0100
50 +++ qemu-6.2.0/linux-user/main.c        2022-01-23 13:53:07.574160010 +0100
51 @@ -119,6 +119,7 @@ static void usage(int exitcode);
52  
53  static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
54  const char *qemu_uname_release;
55 +const char *qemu_execve_path;
56  
57  /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
58     we allocate a bigger stack. Need a better solution, for example
59 @@ -338,6 +339,11 @@ static void handle_arg_guest_base(const
60      have_guest_base = true;
61  }
62  
63 +static void handle_arg_execve(const char *arg)
64 +{
65 +    qemu_execve_path = strdup(arg);
66 +}
67 +
68  static void handle_arg_reserved_va(const char *arg)
69  {
70      char *p;
71 @@ -440,6 +446,8 @@ static const struct qemu_argument arg_ta
72       "uname",      "set qemu uname release string to 'uname'"},
73      {"B",          "QEMU_GUEST_BASE",  true,  handle_arg_guest_base,
74       "address",    "set guest_base address to 'address'"},
75 +    {"execve",     "QEMU_EXECVE",      true,   handle_arg_execve,
76 +     "path",       "use interpreter at 'path' when a process calls execve()"},
77      {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
78       "size",       "reserve 'size' bytes for guest virtual address space"},
79      {"d",          "QEMU_LOG",         true,  handle_arg_log,
80 diff -urNpa qemu-6.2.0.orig/linux-user/syscall.c qemu-6.2.0/linux-user/syscall.c
81 --- qemu-6.2.0.orig/linux-user/syscall.c        2022-01-23 13:51:24.453951513 +0100
82 +++ qemu-6.2.0/linux-user/syscall.c     2022-01-23 13:53:07.576160014 +0100
83 @@ -8072,6 +8072,109 @@ static target_timer_t get_timer_id(abi_l
84      return timerid;
85  }
86  
87 +#define BINPRM_BUF_SIZE 128
88 +
89 +/* qemu_execve() Must return target values and target errnos. */
90 +static abi_long qemu_execve(char *filename, char *argv[],
91 +                  char *envp[])
92 +{
93 +    char *i_arg = NULL, *i_name = NULL;
94 +    char **new_argp;
95 +    int argc, fd, ret, i, offset = 3;
96 +    char *cp;
97 +    char buf[BINPRM_BUF_SIZE];
98 +
99 +    for (argc = 0; argv[argc] != NULL; argc++) {
100 +        /* nothing */ ;
101 +    }
102 +
103 +    fd = open(filename, O_RDONLY);
104 +    if (fd == -1) {
105 +        return -ENOENT;
106 +    }
107 +
108 +    ret = read(fd, buf, BINPRM_BUF_SIZE);
109 +    if (ret == -1) {
110 +        close(fd);
111 +        return -ENOENT;
112 +    }
113 +
114 +    close(fd);
115 +
116 +    /* adapted from the kernel
117 +     * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
118 +     */
119 +    if ((buf[0] == '#') && (buf[1] == '!')) {
120 +        /*
121 +         * This section does the #! interpretation.
122 +         * Sorta complicated, but hopefully it will work.  -TYT
123 +         */
124 +
125 +        buf[BINPRM_BUF_SIZE - 1] = '\0';
126 +        cp = strchr(buf, '\n');
127 +        if (cp == NULL) {
128 +            cp = buf+BINPRM_BUF_SIZE-1;
129 +        }
130 +        *cp = '\0';
131 +        while (cp > buf) {
132 +            cp--;
133 +            if ((*cp == ' ') || (*cp == '\t')) {
134 +                *cp = '\0';
135 +            } else {
136 +                break;
137 +            }
138 +        }
139 +        for (cp = buf+2; (*cp == ' ') || (*cp == '\t'); cp++) {
140 +            /* nothing */ ;
141 +        }
142 +        if (*cp == '\0') {
143 +            return -ENOEXEC; /* No interpreter name found */
144 +        }
145 +        i_name = cp;
146 +        i_arg = NULL;
147 +        for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
148 +            /* nothing */ ;
149 +        }
150 +        while ((*cp == ' ') || (*cp == '\t')) {
151 +            *cp++ = '\0';
152 +        }
153 +        if (*cp) {
154 +            i_arg = cp;
155 +        }
156 +
157 +        if (i_arg) {
158 +            offset = 5;
159 +        } else {
160 +            offset = 4;
161 +        }
162 +    }
163 +
164 +    new_argp = alloca((argc + offset + 1) * sizeof(void *));
165 +
166 +    /* Copy the original arguments with offset */
167 +    for (i = 0; i < argc; i++) {
168 +        new_argp[i + offset] = argv[i];
169 +    }
170 +
171 +    new_argp[0] = strdup(qemu_execve_path);
172 +    new_argp[1] = strdup("-0");
173 +    new_argp[offset] = filename;
174 +    new_argp[argc + offset] = NULL;
175 +
176 +    if (i_name) {
177 +        new_argp[2] = i_name;
178 +        new_argp[3] = i_name;
179 +
180 +        if (i_arg) {
181 +            new_argp[4] = i_arg;
182 +        }
183 +    } else {
184 +        new_argp[2] = argv[0];
185 +    }
186 +
187 +    return get_errno(safe_execve(qemu_execve_path, new_argp, envp));
188 +}
189 +
190  static int target_to_host_cpu_mask(unsigned long *host_mask,
191                                     size_t host_size,
192                                     abi_ulong target_addr,
193 @@ -8585,7 +8688,12 @@ static abi_long do_syscall1(void *cpu_en
194               * before the execve completes and makes it the other
195               * program's problem.
196               */
197 -            ret = get_errno(safe_execve(p, argp, envp));
198 +            if (qemu_execve_path && *qemu_execve_path) {
199 +                ret = get_errno(qemu_execve(p, argp, envp));
200 +            } else {
201 +                ret = get_errno(safe_execve(p, argp, envp));
202 +            }
203 +
204              unlock_user(p, arg1, 0);
205  
206              goto execve_end;
207 diff -urNpa qemu-6.2.0.orig/linux-user/user-internals.h qemu-6.2.0/linux-user/user-internals.h
208 --- qemu-6.2.0.orig/linux-user/user-internals.h 2022-01-23 13:51:24.450951507 +0100
209 +++ qemu-6.2.0/linux-user/user-internals.h      2022-01-23 13:54:10.373286987 +0100
210 @@ -28,6 +28,7 @@ void init_task_state(TaskState *ts);
211  void task_settid(TaskState *);
212  void stop_all_tasks(void);
213  extern const char *qemu_uname_release;
214 +extern const char *qemu_execve_path;
215  extern unsigned long mmap_min_addr;
216  
217  typedef struct IOCTLEntry IOCTLEntry;