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

DDB 'b addr,count' silently truncates the skip count (radix-16 lexer + int struct fields) β€” breakpoint armed but can never fire, garbage negative Count displayed

Summary

Three unchecked conversions (radix-16 lexer, unvalidated db_expr_t, int fields) with an exact-zero stop comparison: 'b ticks,4294967296' yields Count -1802079594 - a silently dead tripwire with zero diagnostics while listed as armed (the DF-5721 dead-instrument class via a distinct root cause). Live-proven on stock; range-check fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5726 Β· 13 files
FileTypeDescriptionSize
README.md β€” 1.8 KB ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
serial_vm1.sh β€” 1.8 KB view raw
drive_ddb.py β€” 2.2 KB view raw
enter_ddb.sh β€” 867 B view raw
run.sh β€” 218 B view raw
session1-smp.serial.log β€” 14.5 KB view raw
baseline-up.serial.log β€” 29.3 KB view raw
env.txt β€” 350 B view raw
fix.diff β€” 601 B view raw
fix_build.log β€” 5.7 MB ↓ download
fix_run.log β€” 15.2 KB view raw
verdict.json β€” 2.8 KB view raw

DF-5726 β€” DDB b addr,count silently truncates the skip count to a garbage/negative int β€” breakpoint never fires, no diagnostic

Severity: Low (DDB console is root-gated; operational "silent disarm" defect, same instrument-is-dead class as DF-5721).

What breaks

The break addr[,count] skip count travels through three unchecked conversions, none of which validates:

  1. db_lex.c:203 β€” db_tok_number = db_tok_number * r + digit with default radix 16 (db_lex.c:119), so a decimal-looking literal like 4294967296 is parsed as hex 0x4294967296;
  2. db_break.c:271-278 β€” db_breakpoint_cmd() maps only the literal -1 to 1 and passes any other db_expr_t (long) straight through;
  3. db_break.c:106-107 β€” init_count/count are int fields (db_break.h:52-53): 0x4294967296 truncates to 0x94967296 = -1802079594.

A stop then requires --bkpt->count == 0 exactly (db_run.c:100), reachable only from count==1; a zero or negative count therefore never fires β€” the breakpoint is silently dead while show breaks still lists it as armed.

Live proof (stock kernel)

baseline-up.serial.log (UP) / session1-smp.serial.log (SMP):

db> b ticks,4294967296
db> b bootverbose,2
db> show breaks
 Map      Count    Address
*ffffffff8159eea0     2    bootverbose
*ffffffff8159eea0 -1802079594    ticks   <-- garbage negative skip count

Fix

Validate in db_breakpoint_cmd() β€” see fix.diff (count < 1 || count > INT_MAX β†’ db_error). Validated on the patched kernel: b ticks,4294967296 now errors with "skip count out of range" and adds nothing; b ticks,2 still works.

Reproduce

./serial_vm1.sh up && ./enter_ddb.sh
DF5725_SOCK=/tmp/opencode/df-5725-ser-up.sock ./drive_ddb.py \
   "b ticks,4294967296" 2 "b bootverbose,2" 2 "show breaks" 3 "d ticks" 2 "d bootverbose" 2 "c" 8
VERDICT.md
↓ download raw

DF-5726 β€” VERDICT

reproduced β€” impact: none (silent-disarm correctness defect; no memory unsafety). The documented break addr[,count] skip count is mangled by unchecked conversions and the breakpoint silently never fires while show breaks lists it as armed with a garbage count.

Environment

Same rig as DF-5725 (stock X86_64_GENERIC, UP, socket-serial DDB; guest /sys/ddb/db_break.c MD5 = audited tree). Observed on both the SMP and UP instances (identical).

Observation (exact)

db> b ticks,4294967296
db> b bootverbose,2
db> show breaks
 Map      Count    Address
*ffffffff8159eea0     2    bootverbose
*ffffffff8159eea0 -1802079594    ticks

4294967296 (decimal intent) parsed with DDB's default radix 16 (sys/ddb/db_lex.c:119, accumulation at db_lex.c:203) as 0x4294967296, passed as db_expr_t (long) through db_breakpoint_cmd() (sys/ddb/db_break.c:271-278, only the literal -1 is remapped), then stored into the int fields init_count/count (db_break.c:106-107, sys/ddb/db_break.h:52-53): 0x4294967296 β†’ 0x94967296 = -1802079594.

A stop requires --bkpt->count == 0 exactly (sys/ddb/db_run.c:100), i.e. the pre-decrement count must be 1. A zero or negative count never reaches it: the breakpoint is dead while listed as armed β€” the same dead-instrument/DF-5721 operational class, and every value β‰₯ 2^31 silently produces one.

Why it matters

An incident-response operator who writes b kafka_probe,4294967296 (a deliberate "skip 4G hits" style count) arms nothing, with zero diagnostics.

Fix validation

fix.diff (db_break.c): count < 1 || count > INT_MAX β†’ db_error. On the combined-fix kernel (fix_run.log):

db> b ticks,4294967296
breakpoint: skip count out of range
db> show breaks
... only the other breakpoints; nothing added ...
db> b ticks,2          (still accepted, Count 2)

Baseline reproduced / patched behavior gone β†’ fixed.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Stock kernel: garbage negative Count displayed, bp armed-never-fires. Patched kernel: db_error 'breakpoint: skip count out of range', breakpoint not added, in-range counts (2) unaffected. Bad behavior gone.

findings/poc/DF-5726/fix.diff; fix_run.log ('breakpoint: skip count out of range' + show breaks listing only valid entries); baseline-up.serial.log
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Sat Oct 10 18:42:38 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC

Confirmed kernel references

Detail

Evidence (decisive lines)

baseline-up.serial.log / session1-smp.serial.log: 'b ticks,4294967296' + 'b bootverbose,2' -> show breaks prints 'Count -1802079594' for ticks (2 for bootverbose). fix_run.log (patched): 'breakpoint: skip count out of range' error, nothing added; count 2 still accepted.

PoC changes

No seed PoC; uses the DF-5725 pack's serial rig (drive_ddb.py/serial_vm1.sh/enter_ddb.sh).

Verified recommended fix

Reject out-of-range skip counts in db_breakpoint_cmd(): if (count < 1 || count > INT_MAX) db_error(...) (fix.diff).

Verdict

b addr,count stores the skip count through three unchecked conversions (radix-16 lexer accumulation db_lex.c:203, db_expr_t pass-through db_break.c:271-278, int fields db_break.c:106-107): live-proven that 'b ticks,4294967296' arms a breakpoint with Count -1802079594 (0x4294967296 truncated to 0x94967296), and since a stop requires --bkpt->count == 0 exactly (db_run.c:100), zero/negative/truncated counts silently never fire while show breaks lists the breakpoint as armed. Root-gated DDB console -> Low; dead-instrument operational class (DF-5721 family), no memory unsafety.