From 4f85cfe824a0cc01870992851bad43f89eabccf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 4 Nov 2025 14:59:41 +0000 Subject: [PATCH] fix(darwin): extend IPC cleanup to message queues and semaphores Previously, only shared memory segments were cleaned up. This could lead to leaked message queues and semaphore sets when builds use System V IPC, exhausting kernel IPC limits over time. This commit extends the cleanup to all three System V IPC types: 1. Shared memory segments 2. Message queues 3. Semaphores Additionally, we stop removing IPC objects during iteration, as it could corrupt the kernel's iterator state and cause some objects to be skipped. The new implementation uses a two-pass approach where we list first and then remove them in a separate pass. The IPC IDs are now extracted during iteration using actual system calls (shmget, msgget, semget) rather than being looked up later, ensuring the objects exist when we capture their IDs. --- .../unix/build/darwin-derivation-builder.cc | 110 +++++++++++++++--- 1 file changed, 91 insertions(+), 19 deletions(-) diff --git a/src/libstore/unix/build/darwin-derivation-builder.cc b/src/libstore/unix/build/darwin-derivation-builder.cc index 2ec3ea845..701da9513 100644 --- a/src/libstore/unix/build/darwin-derivation-builder.cc +++ b/src/libstore/unix/build/darwin-derivation-builder.cc @@ -5,17 +5,23 @@ # include # include # include +# include +# include /* This definition is undocumented but depended upon by all major browsers. */ extern "C" int sandbox_init_with_parameters(const char * profile, uint64_t flags, const char * const parameters[], char ** errorbuf); -/* Darwin IPC cleanup structures and constants */ +/* Darwin IPC structures and constants */ # define IPCS_MAGIC 0x00000001 # define IPCS_SHM_ITER 0x00000002 +# define IPCS_SEM_ITER 0x00000020 +# define IPCS_MSG_ITER 0x00000200 # define IPCS_SHM_SYSCTL "kern.sysv.ipcs.shm" +# define IPCS_MSG_SYSCTL "kern.sysv.ipcs.msg" +# define IPCS_SEM_SYSCTL "kern.sysv.ipcs.sem" -struct IPCS_command +struct IpcsCommand { uint32_t ipcs_magic; uint32_t ipcs_op; @@ -223,31 +229,97 @@ struct DarwinDerivationBuilder : DerivationBuilderImpl void cleanupSysVIPCForUser(uid_t uid) { - struct IPCS_command ic; - struct shmid_ds shm_ds; + struct IpcsCommand ic; size_t ic_size = sizeof(ic); + // IPC ids to cleanup + std::vector shm_ids, msg_ids, sem_ids; - ic.ipcs_magic = IPCS_MAGIC; - ic.ipcs_op = IPCS_SHM_ITER; - ic.ipcs_cursor = 0; - ic.ipcs_data = &shm_ds; - ic.ipcs_datalen = sizeof(shm_ds); + { + struct shmid_ds shm_ds; + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SHM_ITER; + ic.ipcs_cursor = 0; + ic.ipcs_data = &shm_ds; + ic.ipcs_datalen = sizeof(shm_ds); - while (true) { - memset(&shm_ds, 0, sizeof(shm_ds)); + while (true) { + memset(&shm_ds, 0, sizeof(shm_ds)); - if (sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) { - break; - } + if (sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) { + break; + } - if (shm_ds.shm_perm.uid == uid) { - int shmid = shmget(shm_ds.shm_perm._key, 0, 0); - if (shmid != -1) { - if (shmctl(shmid, IPC_RMID, NULL) == 0) - debug("removed shared memory segment with shmid %d (key: 0x%x)", shmid, shm_ds.shm_perm._key); + if (shm_ds.shm_perm.uid == uid) { + int shmid = shmget(shm_ds.shm_perm._key, 0, 0); + if (shmid != -1) { + shm_ids.push_back(shmid); + } } } } + + for (auto id : shm_ids) { + if (shmctl(id, IPC_RMID, NULL) == 0) + debug("removed shared memory segment with shmid %d", id); + } + + { + struct msqid_ds msg_ds; + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_MSG_ITER; + ic.ipcs_cursor = 0; + ic.ipcs_data = &msg_ds; + ic.ipcs_datalen = sizeof(msg_ds); + + while (true) { + memset(&msg_ds, 0, sizeof(msg_ds)); + + if (sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) { + break; + } + + if (msg_ds.msg_perm.uid == uid) { + int msgid = msgget(msg_ds.msg_perm._key, 0); + if (msgid != -1) { + msg_ids.push_back(msgid); + } + } + } + } + + for (auto id : msg_ids) { + if (msgctl(id, IPC_RMID, NULL) == 0) + debug("removed message queue with msgid %d", id); + } + + { + struct semid_ds sem_ds; + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SEM_ITER; + ic.ipcs_cursor = 0; + ic.ipcs_data = &sem_ds; + ic.ipcs_datalen = sizeof(sem_ds); + + while (true) { + memset(&sem_ds, 0, sizeof(sem_ds)); + + if (sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) { + break; + } + + if (sem_ds.sem_perm.uid == uid) { + int semid = semget(sem_ds.sem_perm._key, 0, 0); + if (semid != -1) { + sem_ids.push_back(semid); + } + } + } + } + + for (auto id : sem_ids) { + if (semctl(id, 0, IPC_RMID) == 0) + debug("removed semaphore with semid %d", id); + } } void killSandbox(bool getStats) override