Note: This is purely the output of the chat conversation and does not contain any raw data, codebase snippets, etc. used to generate the output.
msgctl in bsd/kern/sysv_msg.c, when handling IPC_SET, calls SYSV_MSG_SUBSYS_UNLOCK to drop the lock before running copyin. Why is this unsafe?
Grep searched codebase
*Viewed sysv_msg.c *
Dropping the SYSV_MSG_SUBSYS_LOCK in msgctl prior to running copyin creates a classic Time-of-Check to Time-of-Use (TOCTOU) race condition that can be exploited for privilege escalation and cross-process data interception.
Here is a breakdown of why this is unsafe:
When msgctl begins, it acquires the lock, resolves the msqid to a kernel pointer (msqptr = &msqids[msqid]), checks that the sequence number matches, and validates the caller's permissions (ipcperm(..., IPC_M)).
However, IPC_SET drops the lock to perform copyin() because copying memory from userspace might trigger a page fault and block the thread. While the lock is dropped, the kernel's message queue state is entirely unprotected.
Because msqids is a static array of queue slots, a slot is simply marked "free" when a queue is deleted, and re-used when a new queue is created. An attacker can exploit the unprotected window in the following sequence:
- Check: The attacker calls
IPC_SETon a message queue they own. The kernel validates their permissions and drops the lock. - Mutate: The attacker's thread is intentionally slowed down (e.g., by forcing the
copyinbuffer to be paged out). In another thread, the attacker callsmsgctl(..., IPC_RMID)on the same queue. The queue is destroyed, and its slot is marked as free. - Reallocate: A victim process (e.g., running as
root) callsmsggetto create a new message queue. The kernel reuses the attacker's newly-freed slot for the victim's queue. - Use: The attacker's original
IPC_SETthread finishescopyinand re-acquires the lock. It fails to re-verify the queue's sequence number or allocation status, and blindly applies its user-controlleduid,gid,mode, andmsg_qbytesto the queue currently occupyingmsqptr.
- Privilege Escalation / Interception: The attacker just overwrote the owner and permissions of the victim's message queue. They can set the
modeto0666and theuidto themselves, allowing them to freely read or inject arbitrary messages into the privileged process's IPC channel. - Zombie Resurrection: If the deleted queue was not reallocated,
IPC_SETwill overwritemsg_qbyteswith a non-zero value. This tricks the kernel into thinking the queue is allocated again, but its internal linked lists of messages have already been freed byIPC_RMID, putting the kernel in an unstable state that can lead to panics or use-after-free bugs.
(Note: IPC_STAT just a few lines down is also vulnerable. It drops the lock and then reads from msqptr->u unprotected, which could result in leaking the properties of another process's queue if a reallocation race occurs).