Skip to content

Instantly share code, notes, and snippets.

@zhuowei
Created May 15, 2026 04:41
Show Gist options
  • Select an option

  • Save zhuowei/390b9e171641561f88ceb2119d984bc1 to your computer and use it in GitHub Desktop.

Select an option

Save zhuowei/390b9e171641561f88ceb2119d984bc1 to your computer and use it in GitHub Desktop.

Chat Conversation

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.

User Input

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 *

Planner Response

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:

1. The Race Condition Window

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.

2. The Attack Vector (Queue Hijacking)

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:

  1. Check: The attacker calls IPC_SET on a message queue they own. The kernel validates their permissions and drops the lock.
  2. Mutate: The attacker's thread is intentionally slowed down (e.g., by forcing the copyin buffer to be paged out). In another thread, the attacker calls msgctl(..., IPC_RMID) on the same queue. The queue is destroyed, and its slot is marked as free.
  3. Reallocate: A victim process (e.g., running as root) calls msgget to create a new message queue. The kernel reuses the attacker's newly-freed slot for the victim's queue.
  4. Use: The attacker's original IPC_SET thread finishes copyin and re-acquires the lock. It fails to re-verify the queue's sequence number or allocation status, and blindly applies its user-controlled uid, gid, mode, and msg_qbytes to the queue currently occupying msqptr.

3. The Consequences

  • Privilege Escalation / Interception: The attacker just overwrote the owner and permissions of the victim's message queue. They can set the mode to 0666 and the uid to 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_SET will overwrite msg_qbytes with 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 by IPC_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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment