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)
PoC verification
Evidence pack
findings/poc/DF-2241 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedStock: 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.
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).
No comments yet.