Skip to content

Commit 3f18cad

Browse files
authored
Merge pull request NixOS#14459 from jfroche/fix/macos-ipcs
Fix macOS IPC cleanup in builder
2 parents 41b62aa + 0507674 commit 3f18cad

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

src/libstore/unix/build/darwin-derivation-builder.cc

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,33 @@
33
# include <spawn.h>
44
# include <sys/sysctl.h>
55
# include <sandbox.h>
6+
# include <sys/ipc.h>
7+
# include <sys/shm.h>
8+
# include <sys/msg.h>
9+
# include <sys/sem.h>
610

711
/* This definition is undocumented but depended upon by all major browsers. */
812
extern "C" int
913
sandbox_init_with_parameters(const char * profile, uint64_t flags, const char * const parameters[], char ** errorbuf);
1014

15+
/* Darwin IPC structures and constants */
16+
# define IPCS_MAGIC 0x00000001
17+
# define IPCS_SHM_ITER 0x00000002
18+
# define IPCS_SEM_ITER 0x00000020
19+
# define IPCS_MSG_ITER 0x00000200
20+
# define IPCS_SHM_SYSCTL "kern.sysv.ipcs.shm"
21+
# define IPCS_MSG_SYSCTL "kern.sysv.ipcs.msg"
22+
# define IPCS_SEM_SYSCTL "kern.sysv.ipcs.sem"
23+
24+
struct IpcsCommand
25+
{
26+
uint32_t ipcs_magic;
27+
uint32_t ipcs_op;
28+
uint32_t ipcs_cursor;
29+
uint32_t ipcs_datalen;
30+
void * ipcs_data;
31+
};
32+
1133
namespace nix {
1234

1335
struct DarwinDerivationBuilder : DerivationBuilderImpl
@@ -204,6 +226,119 @@ struct DarwinDerivationBuilder : DerivationBuilderImpl
204226
posix_spawn(
205227
NULL, drv.builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
206228
}
229+
230+
/**
231+
* Cleans up all System V IPC objects owned by the specified user.
232+
*
233+
* On Darwin, IPC objects (shared memory segments, message queues, and semaphore)
234+
* can persist after the build user's processes are killed, since there are no IPC namespaces
235+
* like on Linux. This can exhaust kernel IPC limits over time.
236+
*
237+
* Uses sysctl to enumerate and remove all IPC objects owned by the given UID.
238+
*/
239+
void cleanupSysVIPCForUser(uid_t uid)
240+
{
241+
struct IpcsCommand ic;
242+
size_t ic_size = sizeof(ic);
243+
// IPC ids to cleanup
244+
std::vector<int> shm_ids, msg_ids, sem_ids;
245+
246+
{
247+
struct shmid_ds shm_ds;
248+
ic.ipcs_magic = IPCS_MAGIC;
249+
ic.ipcs_op = IPCS_SHM_ITER;
250+
ic.ipcs_cursor = 0;
251+
ic.ipcs_data = &shm_ds;
252+
ic.ipcs_datalen = sizeof(shm_ds);
253+
254+
while (true) {
255+
memset(&shm_ds, 0, sizeof(shm_ds));
256+
257+
if (sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) {
258+
break;
259+
}
260+
261+
if (shm_ds.shm_perm.uid == uid) {
262+
int shmid = shmget(shm_ds.shm_perm._key, 0, 0);
263+
if (shmid != -1) {
264+
shm_ids.push_back(shmid);
265+
}
266+
}
267+
}
268+
}
269+
270+
for (auto id : shm_ids) {
271+
if (shmctl(id, IPC_RMID, NULL) == 0)
272+
debug("removed shared memory segment with shmid %d", id);
273+
}
274+
275+
{
276+
struct msqid_ds msg_ds;
277+
ic.ipcs_magic = IPCS_MAGIC;
278+
ic.ipcs_op = IPCS_MSG_ITER;
279+
ic.ipcs_cursor = 0;
280+
ic.ipcs_data = &msg_ds;
281+
ic.ipcs_datalen = sizeof(msg_ds);
282+
283+
while (true) {
284+
memset(&msg_ds, 0, sizeof(msg_ds));
285+
286+
if (sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) {
287+
break;
288+
}
289+
290+
if (msg_ds.msg_perm.uid == uid) {
291+
int msgid = msgget(msg_ds.msg_perm._key, 0);
292+
if (msgid != -1) {
293+
msg_ids.push_back(msgid);
294+
}
295+
}
296+
}
297+
}
298+
299+
for (auto id : msg_ids) {
300+
if (msgctl(id, IPC_RMID, NULL) == 0)
301+
debug("removed message queue with msgid %d", id);
302+
}
303+
304+
{
305+
struct semid_ds sem_ds;
306+
ic.ipcs_magic = IPCS_MAGIC;
307+
ic.ipcs_op = IPCS_SEM_ITER;
308+
ic.ipcs_cursor = 0;
309+
ic.ipcs_data = &sem_ds;
310+
ic.ipcs_datalen = sizeof(sem_ds);
311+
312+
while (true) {
313+
memset(&sem_ds, 0, sizeof(sem_ds));
314+
315+
if (sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size) != 0) {
316+
break;
317+
}
318+
319+
if (sem_ds.sem_perm.uid == uid) {
320+
int semid = semget(sem_ds.sem_perm._key, 0, 0);
321+
if (semid != -1) {
322+
sem_ids.push_back(semid);
323+
}
324+
}
325+
}
326+
}
327+
328+
for (auto id : sem_ids) {
329+
if (semctl(id, 0, IPC_RMID) == 0)
330+
debug("removed semaphore with semid %d", id);
331+
}
332+
}
333+
334+
void killSandbox(bool getStats) override
335+
{
336+
DerivationBuilderImpl::killSandbox(getStats);
337+
if (buildUser) {
338+
auto uid = buildUser->getUID();
339+
cleanupSysVIPCForUser(uid);
340+
}
341+
}
207342
};
208343

209344
} // namespace nix

0 commit comments

Comments
 (0)