]>
Commit | Line | Data |
---|---|---|
84d41c5e KT |
1 | /* |
2 | * FreeBSD process related emulation code | |
3 | * | |
4 | * Copyright (c) 2013-15 Stacey D. Son | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | #include "qemu/osdep.h" | |
20 | ||
21 | #include <sys/param.h> | |
22 | #include <sys/queue.h> | |
23 | #include <sys/sysctl.h> | |
24 | struct kinfo_proc; | |
25 | #include <libprocstat.h> | |
26 | ||
27 | #include "qemu.h" | |
28 | ||
29 | /* | |
30 | * Get the filename for the given file descriptor. | |
31 | * Note that this may return NULL (fail) if no longer cached in the kernel. | |
32 | */ | |
86327290 | 33 | static char * |
84d41c5e KT |
34 | get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len) |
35 | { | |
36 | char *ret = NULL; | |
37 | unsigned int cnt; | |
38 | struct procstat *procstat = NULL; | |
39 | struct kinfo_proc *kp = NULL; | |
40 | struct filestat_list *head = NULL; | |
41 | struct filestat *fst; | |
42 | ||
43 | procstat = procstat_open_sysctl(); | |
44 | if (procstat == NULL) { | |
45 | goto out; | |
46 | } | |
47 | ||
48 | kp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &cnt); | |
49 | if (kp == NULL) { | |
50 | goto out; | |
51 | } | |
52 | ||
53 | head = procstat_getfiles(procstat, kp, 0); | |
54 | if (head == NULL) { | |
55 | goto out; | |
56 | } | |
57 | ||
58 | STAILQ_FOREACH(fst, head, next) { | |
59 | if (fd == fst->fs_fd) { | |
60 | if (fst->fs_path != NULL) { | |
61 | (void)strlcpy(filename, fst->fs_path, len); | |
62 | ret = filename; | |
63 | } | |
64 | break; | |
65 | } | |
66 | } | |
67 | ||
68 | out: | |
69 | if (head != NULL) { | |
70 | procstat_freefiles(procstat, head); | |
71 | } | |
72 | if (kp != NULL) { | |
73 | procstat_freeprocs(procstat, kp); | |
74 | } | |
75 | if (procstat != NULL) { | |
76 | procstat_close(procstat); | |
77 | } | |
78 | return ret; | |
79 | } | |
80 | ||
86327290 SS |
81 | /* |
82 | * execve/fexecve | |
83 | */ | |
84 | abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp, | |
85 | abi_ulong guest_envp, int do_fexec) | |
86 | { | |
87 | char **argp, **envp, **qargp, **qarg1, **qarg0, **qargend; | |
88 | int argc, envc; | |
89 | abi_ulong gp; | |
90 | abi_ulong addr; | |
91 | char **q; | |
92 | int total_size = 0; | |
93 | void *p; | |
94 | abi_long ret; | |
95 | ||
96 | argc = 0; | |
97 | for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) { | |
98 | if (get_user_ual(addr, gp)) { | |
99 | return -TARGET_EFAULT; | |
100 | } | |
101 | if (!addr) { | |
102 | break; | |
103 | } | |
104 | argc++; | |
105 | } | |
106 | envc = 0; | |
107 | for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) { | |
108 | if (get_user_ual(addr, gp)) { | |
109 | return -TARGET_EFAULT; | |
110 | } | |
111 | if (!addr) { | |
112 | break; | |
113 | } | |
114 | envc++; | |
115 | } | |
116 | ||
117 | qarg0 = argp = g_new0(char *, argc + 9); | |
9bfba08a | 118 | /* save the first argument for the emulator */ |
86327290 SS |
119 | *argp++ = (char *)getprogname(); |
120 | qargp = argp; | |
121 | *argp++ = (char *)getprogname(); | |
122 | qarg1 = argp; | |
123 | envp = g_new0(char *, envc + 1); | |
124 | for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) { | |
125 | if (get_user_ual(addr, gp)) { | |
126 | ret = -TARGET_EFAULT; | |
127 | goto execve_end; | |
128 | } | |
129 | if (!addr) { | |
130 | break; | |
131 | } | |
132 | *q = lock_user_string(addr); | |
133 | if (*q == NULL) { | |
134 | ret = -TARGET_EFAULT; | |
135 | goto execve_end; | |
136 | } | |
137 | total_size += strlen(*q) + 1; | |
138 | } | |
139 | *q++ = NULL; | |
140 | qargend = q; | |
141 | ||
142 | for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) { | |
143 | if (get_user_ual(addr, gp)) { | |
144 | ret = -TARGET_EFAULT; | |
145 | goto execve_end; | |
146 | } | |
147 | if (!addr) { | |
148 | break; | |
149 | } | |
150 | *q = lock_user_string(addr); | |
151 | if (*q == NULL) { | |
152 | ret = -TARGET_EFAULT; | |
153 | goto execve_end; | |
154 | } | |
155 | total_size += strlen(*q) + 1; | |
156 | } | |
157 | *q = NULL; | |
158 | ||
159 | /* | |
160 | * This case will not be caught by the host's execve() if its | |
161 | * page size is bigger than the target's. | |
162 | */ | |
163 | if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) { | |
164 | ret = -TARGET_E2BIG; | |
165 | goto execve_end; | |
166 | } | |
167 | ||
168 | if (do_fexec) { | |
169 | if (((int)path_or_fd > 0 && | |
170 | is_target_elf_binary((int)path_or_fd)) == 1) { | |
171 | char execpath[PATH_MAX]; | |
172 | ||
173 | /* | |
174 | * The executable is an elf binary for the target | |
175 | * arch. execve() it using the emulator if we can | |
176 | * determine the filename path from the fd. | |
177 | */ | |
178 | if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath, | |
179 | sizeof(execpath)) != NULL) { | |
180 | memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); | |
181 | qarg1[1] = qarg1[0]; | |
182 | qarg1[0] = (char *)"-0"; | |
183 | qarg1 += 2; | |
184 | qargend += 2; | |
185 | *qarg1 = execpath; | |
186 | #ifndef DONT_INHERIT_INTERP_PREFIX | |
187 | memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); | |
188 | *qarg1++ = (char *)"-L"; | |
189 | *qarg1++ = (char *)interp_prefix; | |
190 | #endif | |
191 | ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); | |
192 | } else { | |
193 | /* Getting the filename path failed. */ | |
194 | ret = -TARGET_EBADF; | |
195 | goto execve_end; | |
196 | } | |
197 | } else { | |
198 | ret = get_errno(fexecve((int)path_or_fd, argp, envp)); | |
199 | } | |
200 | } else { | |
201 | int fd; | |
202 | ||
203 | p = lock_user_string(path_or_fd); | |
204 | if (p == NULL) { | |
205 | ret = -TARGET_EFAULT; | |
206 | goto execve_end; | |
207 | } | |
208 | ||
209 | /* | |
210 | * Check the header and see if it a target elf binary. If so | |
211 | * then execute using qemu user mode emulator. | |
212 | */ | |
213 | fd = open(p, O_RDONLY | O_CLOEXEC); | |
214 | if (fd > 0 && is_target_elf_binary(fd) == 1) { | |
215 | close(fd); | |
216 | /* execve() as a target binary using emulator. */ | |
217 | memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); | |
218 | qarg1[1] = qarg1[0]; | |
219 | qarg1[0] = (char *)"-0"; | |
220 | qarg1 += 2; | |
221 | qargend += 2; | |
222 | *qarg1 = (char *)p; | |
223 | #ifndef DONT_INHERIT_INTERP_PREFIX | |
224 | memmove(qarg1 + 2, qarg1, (qargend - qarg1) * sizeof(*qarg1)); | |
225 | *qarg1++ = (char *)"-L"; | |
226 | *qarg1++ = (char *)interp_prefix; | |
227 | #endif | |
228 | ret = get_errno(execve(qemu_proc_pathname, qargp, envp)); | |
229 | } else { | |
230 | close(fd); | |
231 | /* Execve() as a host native binary. */ | |
232 | ret = get_errno(execve(p, argp, envp)); | |
233 | } | |
234 | unlock_user(p, path_or_fd, 0); | |
235 | } | |
236 | ||
237 | execve_end: | |
238 | for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) { | |
239 | if (get_user_ual(addr, gp) || !addr) { | |
240 | break; | |
241 | } | |
242 | unlock_user(*q, addr, 0); | |
243 | } | |
244 | ||
245 | for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) { | |
246 | if (get_user_ual(addr, gp) || !addr) { | |
247 | break; | |
248 | } | |
249 | unlock_user(*q, addr, 0); | |
250 | } | |
251 | ||
252 | g_free(qarg0); | |
253 | g_free(envp); | |
254 | ||
255 | return ret; | |
256 | } | |
257 | ||
dcaa3dfd SS |
258 | #include <sys/procctl.h> |
259 | ||
260 | static abi_long | |
261 | t2h_procctl_cmd(int target_cmd, int *host_cmd) | |
262 | { | |
263 | switch (target_cmd) { | |
264 | case TARGET_PROC_SPROTECT: | |
265 | *host_cmd = PROC_SPROTECT; | |
266 | break; | |
267 | ||
268 | case TARGET_PROC_REAP_ACQUIRE: | |
269 | *host_cmd = PROC_REAP_ACQUIRE; | |
270 | break; | |
271 | ||
272 | case TARGET_PROC_REAP_RELEASE: | |
273 | *host_cmd = PROC_REAP_RELEASE; | |
274 | break; | |
275 | ||
276 | case TARGET_PROC_REAP_STATUS: | |
277 | *host_cmd = PROC_REAP_STATUS; | |
278 | break; | |
279 | ||
280 | case TARGET_PROC_REAP_KILL: | |
281 | *host_cmd = PROC_REAP_KILL; | |
282 | break; | |
283 | ||
284 | default: | |
285 | return -TARGET_EINVAL; | |
286 | } | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
291 | static abi_long | |
292 | h2t_reaper_status(struct procctl_reaper_status *host_rs, | |
293 | abi_ulong target_rs_addr) | |
294 | { | |
295 | struct target_procctl_reaper_status *target_rs; | |
296 | ||
297 | if (!lock_user_struct(VERIFY_WRITE, target_rs, target_rs_addr, 0)) { | |
298 | return -TARGET_EFAULT; | |
299 | } | |
300 | __put_user(host_rs->rs_flags, &target_rs->rs_flags); | |
301 | __put_user(host_rs->rs_children, &target_rs->rs_children); | |
302 | __put_user(host_rs->rs_descendants, &target_rs->rs_descendants); | |
303 | __put_user(host_rs->rs_reaper, &target_rs->rs_reaper); | |
304 | __put_user(host_rs->rs_pid, &target_rs->rs_pid); | |
305 | unlock_user_struct(target_rs, target_rs_addr, 1); | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static abi_long | |
311 | t2h_reaper_kill(abi_ulong target_rk_addr, struct procctl_reaper_kill *host_rk) | |
312 | { | |
313 | struct target_procctl_reaper_kill *target_rk; | |
314 | ||
315 | if (!lock_user_struct(VERIFY_READ, target_rk, target_rk_addr, 1)) { | |
316 | return -TARGET_EFAULT; | |
317 | } | |
318 | __get_user(host_rk->rk_sig, &target_rk->rk_sig); | |
319 | __get_user(host_rk->rk_flags, &target_rk->rk_flags); | |
320 | __get_user(host_rk->rk_subtree, &target_rk->rk_subtree); | |
321 | __get_user(host_rk->rk_killed, &target_rk->rk_killed); | |
322 | __get_user(host_rk->rk_fpid, &target_rk->rk_fpid); | |
323 | unlock_user_struct(target_rk, target_rk_addr, 0); | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
328 | static abi_long | |
329 | h2t_reaper_kill(struct procctl_reaper_kill *host_rk, abi_ulong target_rk_addr) | |
330 | { | |
331 | struct target_procctl_reaper_kill *target_rk; | |
332 | ||
333 | if (!lock_user_struct(VERIFY_WRITE, target_rk, target_rk_addr, 0)) { | |
334 | return -TARGET_EFAULT; | |
335 | } | |
336 | __put_user(host_rk->rk_sig, &target_rk->rk_sig); | |
337 | __put_user(host_rk->rk_flags, &target_rk->rk_flags); | |
338 | __put_user(host_rk->rk_subtree, &target_rk->rk_subtree); | |
339 | __put_user(host_rk->rk_killed, &target_rk->rk_killed); | |
340 | __put_user(host_rk->rk_fpid, &target_rk->rk_fpid); | |
341 | unlock_user_struct(target_rk, target_rk_addr, 1); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static abi_long | |
347 | h2t_procctl_reaper_pidinfo(struct procctl_reaper_pidinfo *host_pi, | |
348 | abi_ulong target_pi_addr) | |
349 | { | |
350 | struct target_procctl_reaper_pidinfo *target_pi; | |
351 | ||
352 | if (!lock_user_struct(VERIFY_WRITE, target_pi, target_pi_addr, 0)) { | |
353 | return -TARGET_EFAULT; | |
354 | } | |
355 | __put_user(host_pi->pi_pid, &target_pi->pi_pid); | |
356 | __put_user(host_pi->pi_subtree, &target_pi->pi_subtree); | |
357 | __put_user(host_pi->pi_flags, &target_pi->pi_flags); | |
358 | unlock_user_struct(target_pi, target_pi_addr, 1); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | abi_long | |
364 | do_freebsd_procctl(void *cpu_env, int idtype, abi_ulong arg2, abi_ulong arg3, | |
365 | abi_ulong arg4, abi_ulong arg5, abi_ulong arg6) | |
366 | { | |
367 | abi_long error = 0, target_rp_pids; | |
368 | void *data; | |
369 | int host_cmd, flags; | |
370 | uint32_t u, target_rp_count; | |
371 | g_autofree union { | |
372 | struct procctl_reaper_status rs; | |
373 | struct procctl_reaper_pids rp; | |
374 | struct procctl_reaper_kill rk; | |
375 | } host; | |
376 | struct target_procctl_reaper_pids *target_rp; | |
377 | id_t id; /* 64-bit */ | |
378 | int target_cmd; | |
379 | abi_ulong target_arg; | |
380 | ||
381 | #if TARGET_ABI_BITS == 32 | |
382 | /* See if we need to align the register pairs. */ | |
383 | if (regpairs_aligned(cpu_env)) { | |
384 | id = (id_t)target_arg64(arg3, arg4); | |
385 | target_cmd = (int)arg5; | |
386 | target_arg = arg6; | |
387 | } else { | |
388 | id = (id_t)target_arg64(arg2, arg3); | |
389 | target_cmd = (int)arg4; | |
390 | target_arg = arg5; | |
391 | } | |
392 | #else | |
393 | id = (id_t)arg2; | |
394 | target_cmd = (int)arg3; | |
395 | target_arg = arg4; | |
396 | #endif | |
397 | ||
398 | error = t2h_procctl_cmd(target_cmd, &host_cmd); | |
399 | if (error) { | |
400 | return error; | |
401 | } | |
402 | switch (host_cmd) { | |
403 | case PROC_SPROTECT: | |
404 | data = &flags; | |
405 | break; | |
406 | ||
407 | case PROC_REAP_ACQUIRE: | |
408 | case PROC_REAP_RELEASE: | |
409 | if (target_arg == 0) { | |
410 | data = NULL; | |
411 | } else { | |
412 | error = -TARGET_EINVAL; | |
413 | } | |
414 | break; | |
415 | ||
416 | case PROC_REAP_STATUS: | |
417 | data = &host.rs; | |
418 | break; | |
419 | ||
420 | case PROC_REAP_GETPIDS: | |
421 | if (!lock_user_struct(VERIFY_READ, target_rp, target_arg, 1)) { | |
422 | return -TARGET_EFAULT; | |
423 | } | |
424 | __get_user(target_rp_count, &target_rp->rp_count); | |
425 | __get_user(target_rp_pids, &target_rp->rp_pids); | |
426 | unlock_user_struct(target_rp, target_arg, 0); | |
427 | host.rp.rp_count = target_rp_count; | |
428 | host.rp.rp_pids = g_try_new(struct procctl_reaper_pidinfo, | |
429 | target_rp_count); | |
430 | ||
431 | if (host.rp.rp_pids == NULL) { | |
432 | error = -TARGET_ENOMEM; | |
433 | } else { | |
434 | data = &host.rp; | |
435 | } | |
436 | break; | |
437 | ||
438 | case PROC_REAP_KILL: | |
439 | error = t2h_reaper_kill(target_arg, &host.rk); | |
440 | break; | |
441 | } | |
442 | ||
443 | if (error) { | |
444 | return error; | |
445 | } | |
446 | error = get_errno(procctl(idtype, id, host_cmd, data)); | |
447 | ||
448 | if (error) { | |
449 | return error; | |
450 | } | |
451 | switch (host_cmd) { | |
452 | case PROC_SPROTECT: | |
453 | if (put_user_s32(flags, target_arg)) { | |
454 | return -TARGET_EFAULT; | |
455 | } | |
456 | break; | |
457 | ||
458 | case PROC_REAP_STATUS: | |
459 | error = h2t_reaper_status(&host.rs, target_arg); | |
460 | break; | |
461 | ||
462 | case PROC_REAP_GETPIDS: | |
463 | /* copyout reaper pidinfo */ | |
464 | for (u = 0; u < target_rp_count; u++) { | |
465 | error = h2t_procctl_reaper_pidinfo(&host.rp.rp_pids[u], | |
466 | target_rp_pids + | |
467 | (u * sizeof(struct target_procctl_reaper_pidinfo))); | |
468 | if (error) { | |
469 | break; | |
470 | } | |
471 | } | |
472 | break; | |
473 | ||
474 | case PROC_REAP_KILL: | |
475 | error = h2t_reaper_kill(&host.rk, target_arg); | |
476 | break; | |
477 | } | |
478 | ||
479 | return error; | |
480 | } |