DF-2780 / verdict.json
{ "finding_id": "DF-2780", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Per-process mqueue limit bypass proven three ways on the stock kernel: (1) control = EMFILE at exactly 512 creates; (2) the open-EXISTING path performs no limit check at all - mq_open(existing) succeeded at the EMFILE limit; (3) fork path - a child that drops the last file reference of one inherited descriptor underflows its u_int p_mqueue_cnt 0->0xffffffff (decrement runs in the last-ref closer, not the opener; fork zeroes the child counter) after which the equality check '== mq_open_max' never matches: the child created 513 queues (exit 42 = bypass). Combined with DF-2781 this enables unbounded queue/memory creation; combined with repeated fork+close rounds the per-process limit is void.", "exploit_chain": "open 1 queue -> fork -> parent closes its copy (fo_close NOT run; parent count leaks) -> pipe-sync -> child closes inherited copy as LAST ref -> mq_close_fop decrements child's 0 counter to 0xffffffff -> equality limit check misses -> unlimited creates until fd limit; independently, opening existing queues bypasses the check without any fork.", "evidence": ["run.log: control 512/EMFILE, '[existing] ... SUCCEEDED', '[fork-child] created 513 queues ... exit=42 => BYPASS REPRODUCED'", "fix_validation.log: patched - existing path EMFILE, fork-child exactly 512, exit=1"], "kernel_refs": ["sys/kern/sys_mqueue.c:384", "sys/kern/sys_mqueue.c:452", "sys/kern/sys_mqueue.c:577", "sys/kern/sys_mqueue.c:592", "sys/kern/sys_mqueue.c:531-563", "sys/sys/proc.h:235", "sys/kern/kern_fork.c:444", "sys/kern/kern_fork.c:551"], "poc_changes": "Pipe-synchronized close ordering so the child's close is the last reference (first version closed in the wrong order and did not underflow); control-loop fds closed/unlinked between stages; raw syscalls.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 30, "guest_dirty": 0, "build_cmd": "cc -O2 -o fork_cnt fork_cnt.c", "run_cmd": "./fork_cnt", "code_hash": "0f43b7b5cc64ef282fcffb1affd245fe5c369c37458883d79f9a8d846dc4efc8", "notes": "The opener-side counter leak (parent stuck at 1 with no fds) is the mirror bug - premature EMFILE - noted in the finding.", "recommended_fix": "Inherit p_mqueue_cnt across fork (count DTYPE_MQUEUE fds after fdcopy), use >= instead of ==, and enforce the limit on the open-existing path (fix.diff).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Sep 1 04:57:57 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel: existing-path open now returns EMFILE at the limit; fork child creates exactly 512 (no underflow effect) and exits 1 ('bypass not observed').", "fix_evidence": "fix_validation.log" } |