|
48 | 48 | #include "debug/log.h" |
49 | 49 | #include "debug/syscall-hist.h" |
50 | 50 |
|
| 51 | +/* Linux clone flags. Shared by the fork-child TID-sync emulation below and |
| 52 | + * sys_clone further down. |
| 53 | + */ |
| 54 | +#define LINUX_CLONE_VM 0x00000100 |
| 55 | +#define LINUX_CLONE_VFORK 0x00004000 |
| 56 | +#define LINUX_CLONE_THREAD 0x00010000 |
| 57 | +#define LINUX_CLONE_SETTLS 0x00080000 |
| 58 | +#define LINUX_CLONE_PARENT_SETTID 0x00100000 |
| 59 | +#define LINUX_CLONE_CHILD_CLEARTID 0x00200000 |
| 60 | +#define LINUX_CLONE_CHILD_SETTID 0x01000000 |
| 61 | +/* LINUX_SIGCHLD defined in syscall_signal.h (included above) */ |
| 62 | + |
51 | 63 | /* fork_child_main. */ |
52 | 64 |
|
53 | 65 | static int fork_child_vfork_notify_fd = -1; |
@@ -166,7 +178,8 @@ int fork_child_main(int ipc_fd, |
166 | 178 | close(ipc_fd); |
167 | 179 | return 1; |
168 | 180 | } |
169 | | - if (guest_init_from_shm(&g, shm_fd, hdr.guest_size, hdr.ipa_bits) < 0) { |
| 181 | + if (guest_init_from_shm(&g, shm_fd, hdr.guest_size, hdr.ipa_bits, |
| 182 | + hdr.shm_is_clone != 0) < 0) { |
170 | 183 | log_error("fork-child: guest_init_from_shm failed"); |
171 | 184 | close(ipc_fd); |
172 | 185 | return 1; |
@@ -363,6 +376,30 @@ int fork_child_main(int ipc_fd, |
363 | 376 | */ |
364 | 377 | thread_register_main(vcpu, vexit, hdr.child_pid, regs.sp_el1); |
365 | 378 |
|
| 379 | + /* Emulate CLONE_CHILD_SETTID for the fork child. glibc's fork wrapper |
| 380 | + * passes CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID so the child's TCB |
| 381 | + * caches its own TID; without the SETTID write the child keeps the parent's |
| 382 | + * cached TID and modern glibc trips stack-canary / TLS checks ("stack |
| 383 | + * smashing detected"). The write goes through guest memory, valid for both |
| 384 | + * the CoW and region-copy paths. A faulting ctid_gva is the guest's own bad |
| 385 | + * pointer: warn and continue, matching how the kernel ignores a |
| 386 | + * child_tidptr fault. |
| 387 | + * |
| 388 | + * CLONE_CHILD_CLEARTID is deliberately not honored here. The clear-and-wake |
| 389 | + * on exit only matters to an in-process joiner waiting on the futex (that |
| 390 | + * is how the worker-thread exit path serves pthread_join). A fork child is |
| 391 | + * a separate process with its own address space, so its ctid lives in |
| 392 | + * memory no other process can observe -- the parent reaps it via |
| 393 | + * wait4/SIGCHLD, not a cross-process futex. Registering clear_child_tid |
| 394 | + * would be inert. |
| 395 | + */ |
| 396 | + if (hdr.clone_flags & LINUX_CLONE_CHILD_SETTID) { |
| 397 | + int32_t tid32 = (int32_t) hdr.child_pid; |
| 398 | + if (guest_write_small(&g, hdr.ctid_gva, &tid32, sizeof(tid32)) < 0) |
| 399 | + log_warn("fork-child: CHILD_SETTID write to 0x%llx failed", |
| 400 | + (unsigned long long) hdr.ctid_gva); |
| 401 | + } |
| 402 | + |
366 | 403 | /* Re-publish identity into the child's shim-globals cache: the CoW / region |
367 | 404 | * copy inherits the parent's pid/uid values, and the shim's identity fast |
368 | 405 | * path would otherwise return the parent's pid to the child. Identity is |
@@ -420,16 +457,6 @@ int fork_child_main(int ipc_fd, |
420 | 457 |
|
421 | 458 | /* sys_clone. */ |
422 | 459 |
|
423 | | -/* Linux clone flags */ |
424 | | -#define LINUX_CLONE_VM 0x00000100 |
425 | | -#define LINUX_CLONE_VFORK 0x00004000 |
426 | | -#define LINUX_CLONE_THREAD 0x00010000 |
427 | | -#define LINUX_CLONE_SETTLS 0x00080000 |
428 | | -#define LINUX_CLONE_PARENT_SETTID 0x00100000 |
429 | | -#define LINUX_CLONE_CHILD_CLEARTID 0x00200000 |
430 | | -#define LINUX_CLONE_CHILD_SETTID 0x01000000 |
431 | | -/* LINUX_SIGCHLD defined in syscall_signal.h (included above) */ |
432 | | - |
433 | 460 | /* Namespace flags. elfuse implements no namespace isolation. Both sys_clone and |
434 | 461 | * sys_clone3 reject them. |
435 | 462 | */ |
@@ -1528,6 +1555,10 @@ int64_t sys_clone(hv_vcpu_t vcpu, |
1528 | 1555 | .rosetta_entry = g->rosetta_entry, |
1529 | 1556 | .kbuf_gpa = g->kbuf_gpa, |
1530 | 1557 | .ttbr1 = g->ttbr1, |
| 1558 | + .clone_flags = |
| 1559 | + flags & (LINUX_CLONE_CHILD_SETTID | LINUX_CLONE_CHILD_CLEARTID), |
| 1560 | + .ctid_gva = ctid_gva, |
| 1561 | + .shm_is_clone = (snapshot_shm_fd >= 0) ? 1 : 0, |
1531 | 1562 | }; |
1532 | 1563 | if (fork_ipc_write_all(ipc_sock, &hdr, sizeof(hdr)) < 0) { |
1533 | 1564 | log_error("clone: failed to send header"); |
|
0 commit comments