β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2784

Missing SEMAEM enforcement: un_adjval (short) signed overflow in semundo_adjust flips semexit's clamp branch β€” deterministic wrong undo application

Field Value
ID DF-2784
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
CWE CWE-190 (16-bit signed undo accumulator)
File sys/kern/sysv_sem.c
Lines 244 (accumulate), 259 (create), 1133-1140 (exit clamp)
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

semundo_adjust() accumulates SEM_UNDO values into the 16-bit signed un_adjval with no bound: seminfo.semaem (16384) is defined but enforced nowhere. Two +30000 SEM_UNDO ops wrap the accumulator from βˆ’60000 to +5536; at exit, semexit()'s positive branch skips the "clamp to 0" protection of the negative branch. Reproduced deterministically (2/2): semval after exit = 35536 where POSIX semantics require 0 β€” cross-process visible corruption of shared-semaphore state. Distinct from DF-0046: different variable (undo accumulator vs semaphore value), missing check (SEMAEM vs SEMVMX), mechanism (sign flip defeating the exit clamp vs u_short value wrap).

Threat model & preconditions

Any semaphore participant can leave a wrapped undo entry whose exit-time application writes an attacker-influenced arbitrary delta (Β±32767) to the shared semaphore, bypassing the clamp β€” synchronization-state corruption for victim applications (deadlock/livelock/mutex-bypass). No kernel memory-safety impact.

Proof of contest

VERIFIED (findings/poc/DF-2784/adjval_wrap.c): child performs {+30000,U}Γ—2 then βˆ’30000 (no undo) and exits; parent GETVAL = 35536 (correct: 0) β€” exactly 30000 + (βˆ’60000 mod 2^16). Fixed kernel: ERANGE rejection, no corruption.

Enforce seminfo.semaem on both paths of semundo_adjust returning ERANGE (validated diff in findings/poc/DF-2784/fix.diff).

Timeline

  • 2026-08-31 Discovered during pass-2 audit of sysv_sem.c (GLM 5.3); deterministic corruption reproduced + fix validated same run.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2784 Β· 10 files
FileTypeDescriptionSize
adjval_wrap.c β€” 2.4 KB view raw
build.sh β€” 59 B view raw
run.sh β€” 24 B view raw
build.log β€” 9 B view raw
run.log β€” 214 B view raw
run.2.log β€” 209 B view raw
env.txt β€” 305 B view raw
fix.diff β€” 1005 B view raw
VERDICT.md β€” 2.2 KB ↓ raw
verdict.json β€” 2.4 KB view raw
VERDICT.md
↓ download raw

DF-2784 VERDICT

Status: reproduced (deterministic semantic corruption, certain). Fix validated (fixed).

Baseline (stock kernel #0)

Two runs, identical (run.log, run.2.log):

child : semval before exit = 30000 (undo owed: -60000)
parent: semval after child exit = 35536
expected (POSIX clamp semantics): 0
BUG REPRODUCED: un_adjval wrapped to +5536, positive branch skipped the clamp

Root cause chain, all cited: 1. semop {+30000 SEM_UNDO, +30000 SEM_UNDO} applies semundo_adjust(…, -30000) twice (sysv_sem.c:984-985); 2. sunptr->un_adjval += adjval at sysv_sem.c:244 accumulates into a short: βˆ’30000 β†’ βˆ’60000 wraps to +5536. seminfo.semaem (16384) is defined (sysv_sem.c:91) but enforced nowhere; 3. at child exit, semexit() reads the wrapped value (sysv_sem.c:1107), takes the adjval >= 0 branch (:1138-1139, semval += adjval) and skips the semval < -adjval β†’ clamp to 0 protection (:1133-1137); 4. final semval = 30000 + 5536 = 35536 instead of 0 β€” visible cross-process (parent's GETVAL), i.e. attacker-controllable corruption of shared-semaphore state for every participant.

Deterministic: 2/2 baseline runs; exact value predicted from the 16-bit wrap arithmetic (βˆ’60000 + 65536 = 5536).

Non-overlap with DF-0046: DF-0046 is the missing SEMVMX bound on semval in semop/semexit (u_short overflow of the semaphore value). This finding is the missing SEMAEM bound on the undo accumulator un_adjval at sysv_sem.c:244/:259 β€” different variable, missing check, and failure mode (sign flip defeating the exit clamp).

Fix validation (patched kernel #1, fix.diff enforces SEMAEM β†’ ERANGE)

child : semop rejected with ERANGE (SEMAEM enforced)
parent: child rejected at semop (ERANGE) - SEMAEM enforcement active, no corruption  (rc=0)

Bad value gone: op rejected at adjustment time; semop's existing error path rolls the semaphore values back; exit applies nothing.

Impact ceiling

Low β€” corruption is confined to SysV semaphore values (logic/sync corruption for victim applications sharing the semaphore with the attacker; no kernel memory safety effect). Matches DF-0046's Low rating for the sibling defect.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel rejects the +30000 UNDO op with ERANGE ('SEMAEM enforcement active, no corruption', rc=0); the 35536 corruption is gone.

["run.fixed.log: 'parent: child rejected at semop (ERANGE) - SEMAEM enforcement active, no corruption'", 'fix.diff']
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Tue Sep 1 06:03:53 UTC 2026 root:dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Evidence (decisive lines)

["run.log / run.2.log: 'parent: semval after child exit = 35536' + 'BUG REPRODUCED: un_adjval wrapped to +5536' (2/2)", 'VERDICT.md: full wrap arithmetic (-60000+65536=5536) and root-cause chain']

PoC changes

Added ERANGE detection path (child _exit(42), parent recognizes) so the same binary demonstrates both baseline corruption and post-fix rejection.

Verified recommended fix

Enforce seminfo.semaem on both the accumulate (:244) and create (:259) paths of semundo_adjust, returning ERANGE (validated: op rejected, no corruption).

Verdict

semundo_adjust() accumulates SEM_UNDO values into the 16-bit signed un_adjval with no SEMAEM enforcement (sysv_sem.c:244). Two +30000 UNDO ops wrap -60000 to +5536; at exit semexit()'s positive branch (sysv_sem.c:1138-1139) skips the clamp-to-0 protection (1133-1137), leaving semval=35536 where POSIX semantics require 0. Deterministic, 2/2 runs, cross-process visible. Distinct from DF-0046 (semval/SEMVMX): this is the undo-accumulator/SEMAEM defect.