diff --git a/patches/xnu.patch b/patches/xnu.patch index 7b95368..482a3f6 100644 --- a/patches/xnu.patch +++ b/patches/xnu.patch @@ -3221,10 +3221,18 @@ index 0000000..479294a + return -ENOSYS; +} diff --git a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/proc_info.c b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/proc_info.c -index c2f70fb..af904ed 100644 +index c2f70fb..73c2a5a 100644 --- a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/proc_info.c +++ b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/proc_info.c -@@ -83,6 +83,38 @@ long sys_proc_info(uint32_t callnum, int32_t pid, uint32_t flavor, +@@ -35,6 +35,7 @@ + #define LINUX_PR_SET_NAME 15 + + static long _proc_pidinfo(int32_t pid, uint32_t flavor, uint64_t arg, void* buffer, int32_t bufsize); ++static long _proc_listpids(uint32_t type, uint32_t typeinfo, void* buffer, int32_t bufsize); + + extern __SIZE_TYPE__ strlen(const char *s); + extern void *memset(void *s, int c, __SIZE_TYPE__ n); +@@ -83,7 +84,42 @@ long sys_proc_info(uint32_t callnum, int32_t pid, uint32_t flavor, case 2: // proc_pidinfo return _proc_pidinfo(pid, flavor, arg, buffer, bufsize); case 3: // proc_pidfdinfo @@ -3261,8 +3269,160 @@ index c2f70fb..af904ed 100644 + return -ENOTSUP; + } case 1: // proc_listpids ++ // `pid` carries the process type and `flavor` carries the type ++ // info (e.g. the pgid, ppid or uid to match). ++ return _proc_listpids(pid, flavor, buffer, bufsize); default: __simple_printf("sys_proc_info(): Unsupported callnum: %d\n", + callnum); +@@ -827,3 +863,147 @@ bail: + close_internal(fd); + return count * sizeof(uint64_t); + } ++ ++// Returns whether `pid` should be included in a proc_listpids() query of the ++// given `type`, matching the semantics of XNU's bsd/kern/proc_info.c. ++static bool _proc_listpids_match(uint32_t type, uint32_t typeinfo, int32_t pid, ++ int32_t uid) ++{ ++ char path[64], stat[1024]; ++ char* statptr; ++ const char* elem; ++ int32_t ppid, pgrp, tty; ++ ++ switch (type) ++ { ++ case PROC_ALL_PIDS: ++ return true; ++ ++ case PROC_UID_ONLY: ++ case PROC_RUID_ONLY: ++ // Darling's container runs every guest process as our own ++ // user, which is also what _sysctl_proc() reports. ++ return typeinfo == (uint32_t) uid; ++ ++ case PROC_PGRP_ONLY: ++ case PROC_PPID_ONLY: ++ case PROC_TTY_ONLY: ++ break; ++ ++ default: ++ // PROC_KDBG_ONLY and anything else: no process carries the ++ // corresponding state under Darling. ++ return false; ++ } ++ ++ __simple_sprintf(path, "/proc/%d/stat", pid); ++ if (!read_string(path, stat, sizeof(stat))) ++ return false; ++ ++ statptr = stat; ++ skip_stat_elems(&statptr, 1); // pid ++ ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ // comm ++ ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ // state ++ ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ ppid = __simple_atoi(elem, NULL); ++ ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ pgrp = __simple_atoi(elem, NULL); ++ ++ if (type == PROC_PPID_ONLY) ++ return ppid == (int32_t) typeinfo; ++ ++ if (type == PROC_PGRP_ONLY) ++ return pgrp == (int32_t) typeinfo; ++ ++ // PROC_TTY_ONLY: the (Linux-encoded) controlling terminal device. ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ // session ++ ++ if (!(elem = next_stat_elem(&statptr))) ++ return false; ++ tty = __simple_atoi(elem, NULL); ++ ++ return tty != 0 && (uint32_t) tty == typeinfo; ++} ++ ++// Emulates XNU's proc_listpids(), i.e. proc_info(PROC_INFO_CALL_LISTPIDS). ++// Guest pids are Linux pids, so walking /proc gives the same set that the ++// other proc_info flavors and sysctl(KERN_PROC_*) already operate on. ++static long _proc_listpids(uint32_t type, uint32_t typeinfo, void* buffer, int32_t bufsize) ++{ ++ char dents[256]; ++ long basep = 0; ++ int fd, ret; ++ int32_t maxpids, count = 0; ++ static int uid = -1; ++ ++ if (type != PROC_ALL_PIDS && type != PROC_PGRP_ONLY && type != PROC_TTY_ONLY && ++ type != PROC_UID_ONLY && type != PROC_RUID_ONLY && ++ type != PROC_PPID_ONLY && type != PROC_KDBG_ONLY) ++ return -EINVAL; ++ ++ if (buffer && bufsize < (int32_t) sizeof(int)) ++ return -ENOMEM; ++ ++ // A null buffer asks for the size a caller should allocate. ++ maxpids = buffer ? (bufsize / (int32_t) sizeof(int)) : 0; ++ ++ if (uid == -1) ++ uid = sys_getuid(); ++ ++ fd = sys_open_nocancel("/proc", BSD_O_RDONLY | BSD_O_DIRECTORY, 0); ++ if (fd < 0) ++ return fd; ++ ++ while ((ret = sys_getdirentries(fd, (char*) dents, sizeof(dents), &basep)) > 0) ++ { ++ int pos = 0; ++ ++ while (pos < ret) ++ { ++ struct bsd_dirent* dent = (struct bsd_dirent*) &dents[pos]; ++ ++ pos += dent->d_reclen; ++ ++ if (dent->d_type != DT_DIR || !isdigit(dent->d_name[0])) ++ continue; ++ ++ int pid = __simple_atoi(dent->d_name, NULL); ++ ++ if (!_proc_listpids_match(type, typeinfo, pid, uid)) ++ continue; ++ ++ if (buffer) ++ { ++ // Truncate rather than overflow, like XNU does. ++ if (count >= maxpids) ++ goto bail; ++ ++ ((int*) buffer)[count] = pid; ++ } ++ ++ ++count; ++ } ++ } ++ ++bail: ++ close_internal(fd); ++ ++ // XNU reports a little extra headroom so that a size query followed by a ++ // fill call cannot come up short when processes appear in between. ++ if (!buffer) ++ return (long)(count + 20) * sizeof(int); ++ ++ return (long) count * sizeof(int); ++} diff --git a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c index 0a8f1e4..55aadbe 100644 --- a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c