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

Signed divide/modulo by -1 on LONG_MIN triggers #DE trap leaking db_active wedging system after DDB exit

Summary

db_mult_expr guards division/modulo against rhs==0 (db_expr.c:147) but NOT against LONG_MIN/-1 quotient-overflow case which on x86 raises #DE (divide-error) trap from idivq instruction. rhs=-1 reachable via parenthesized sub-expression db_term parses (0-1) through recursive db_expression. #DE trap recovered by longjmp(db_global_jmpbuf) in kdb_trap but longjmp targets setjmp at db_interface.c:170 re-executing db_active++ at :172. After operator types continue db_active decremented from 2 to 1 (not 0) so trap.c:420 routes every subsequent kernel trap through trap_fatal->DDB re-entry. System drops back into DDB on next page fault timer interrupt syscall permanently wedged until reboot. Trigger expression: 0x8000000000000000 % (0-1). DDB-only privileged context no privilege escalation but subtle non-obvious PERSISTENT DoS operator types innocent-looking arithmetic expression. Production incident-response scenario admin drops into DDB types debugging expression system permanently wedged crash dump/forensics lost compounds original outage. [VERIFIED pass2: both layers - outside DDB Fatal trap 18 at db_mult_expr+0xf7 idivq, inside a real Debugger session the #DE longjmps via db_global_jmpbuf leaving db_active 1 after continue and every later trap re-enters DDB forever - permanent wedge during incident response; LONG_MIN rhs-minus-1 guard fix-validated clean exit]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2241 Β· 14 files
FileTypeDescriptionSize
run.log β€” 1.7 KB view raw
panic.txt β€” 885 B view raw
run_stock_m1_boot.log β€” 13.7 KB view raw
run_stock_m1.out β€” 15 B view raw
run_stock_m2_boot.log β€” 14.4 KB view raw
run_fixed_m1_boot.log β€” 13.1 KB view raw
run_fixed_m2_boot.log β€” 13.3 KB view raw
fix.diff β€” 349 B view raw
dfh.c β€” 24.3 KB view raw
dfarm.c β€” 2.4 KB view raw
harness.diff β€” 2.2 KB view raw
build.sh β€” 66 B view raw
run.sh β€” 157 B view raw
env.txt β€” 234 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Stock: Fatal trap 18 + panic (mode 1) and in-session #DE -> db_active=1 -> permanent DDB re-entry wedge (mode 2). Fixed: Divide by 0 error both modes, db_active=0 after DDB exit, guests healthy; guard visible in disassembly.

run_stock_m1_boot.log / run_stock_m2_boot.log vs run_fixed_m1_boot.log / run_fixed_m2_boot.log; objdump cmp -1 guard in fixed db_mult_expr.
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #8 DFHARNESS x86_64 (stock-behavior kernels #4/#6, fixed #8; base 6.5-DEVELOPMENT 600519)

Confirmed kernel references

Detail

Exploit chain

dumb-terminal/console holder types a single arithmetic expression in DDB -> #DE -> longjmp re-entry -> db_active leak -> continue -> every subsequent kernel trap re-enters DDB -> system permanently wedged until power cycle (no uid=0 relevance; the operator already owns the box, the victim is availability/forensics).

Evidence (decisive lines)

run_stock_m2_boot.log: db> df2241wedge, Fatal trap 18, db> continue, dfh: DF2241: db_active=1 after DDB exit, LEAK CONFIRMED, Fatal trap 12 -> stopping CPUs -> wedged. run_fixed_m2_boot.log: Divide by 0, db_active=0, no leak, guest healthy. RIP resolution: nm db_mult_expr ffffffff802cae00, trap RIP ...caef7.

PoC changes

Built the scripted-DDB rig: DB_COMMAND-registered df2241wedge command, console-input injection queue (df2241wedge / expression / continue), Debugger() invocation from a sysctl, post-session db_active readout, and a deliberate NULL touch to demonstrate the wedge. Learned: mode 1 and mode 2 must run on separate boots (lexer look-token tEOF from a prior evaluation makes the next parse fail -- a harness artifact that initially masqueraded as fixed-kernel behavior on an unfixed build; caught and re-verified on fresh boots with the guard binary-verified via objdump).

Verified recommended fix

Guard the divide/modulo/roundup against the LONG_MIN/-1 quotient overflow in db_mult_expr (db_error like Divide by 0).

Verdict

Both layers proven. (1) The primitive: 0x8000000000000000%(0-1) through the real ddb expression engine raises Fatal trap 18 with RIP db_mult_expr+0xf7 -- the idivq of lhs %= rhs, guarded only against rhs==0. (2) The scenario: inside a real Debugger() session the #DE longjmps via db_global_jmpbuf, db_active becomes 2, continue exits at 1 -- the harness prints the leaked value (db_active=1) after the session -- and the next kernel trap re-enters DDB with no console input, permanently wedging the system (runner timeout, serial frozen at db>). Fixed kernel: guard fires db_error (Divide by 0) both outside and inside DDB; db_active=0 after exit; guest healthy. DDB-console-holder reachability; impact is loss of the whole system incl. the recovery path (dos).