# DF-5720 — ddb `/`-modifier parsing copies a stale token string (numeric modifiers silently substituted with the previous identifier)

**File:** `sys/ddb/db_command.c` (lines 361–370)
**Severity:** Info (debugger parser correctness / stale-state injection; no memory-safety impact)
**Status:** reproduced live on the QEMU guest (deterministic).

## The bug

`db_command()` accepts a numeric token after the command's `/` but copies
`db_tok_string` into `modif[]` — and the lexer only populates `db_tok_string`
for `tIDENT` tokens (numbers go into `db_tok_number`, `sys/ddb/db_lex.c:190-216`).
The string therefore copied is whatever identifier was lexed last — on a plain
line that is the **command's own name** (`x/4` → modif `"x"`, `examine/4` →
modif `"examine"`, `write/4` → modif `"write"`, `print/4` → modif `"print"`).

Consumers then switch on the letters of that stale string:

| input        | modif used | observed behavior (stock kernel)                |
|--------------|------------|--------------------------------------------------|
| `x/4 addr`   | `"x"`      | silently behaves as `x/x` (4 ignored)            |
| `examine/4 addr` | `"examine"` | per-char walk: `x` hex + `a` pointer/symbol read + `i` **disassembly** |
| `print/4 0x41` | `"print"` | `'p'` has no case in `db_print_cmd` → **empty line** |
| `write/4 a v`  | `"write"` | `'w'` → `db_error("Unknown size")` — write refused |

Controls in the same session prove letter modifiers work (`x/x`, `print/x`,
`write/l`).

## Build / run

No compilation — this is a serial-console ddb session:

1. `./serial_vm.sh up` — relaunch the guest with a writable socket serial
   console (adapted from the proven DF-5561 rig).
2. `ssh -F ../../../dfbsd-qemu/config dfbsd 'sysctl debug.enter_debugger=ddb'`
   (root vehicle; kernel stops at `db>` on the serial console).
3. `python3 drive_ddb.py` — sends the battery above with proper UART pacing.
4. Transcript lands in `/tmp/opencode/df-5720-drain.log` (copied to `run.log`).

`./run.sh` automates 1–4.

## Expected output (see run.log)

```
db> x/x db_active
db_active:      1
db> x/4 db_active
db_active:      1                       <-- "4" silently ignored (same as x/x)
db> examine/4 db_active
db_active:      1               0x100000001addb %al,(%rax)
db_global_jmpbuf_valid+0x6:             <-- modif="examine": x + a + i walk (!)
db> print 0x41
      41
db> print/4 0x41
                                        <-- empty line: modif="print" -> 'p'
db> print/x 0x41
      41
db> write/4 db_active 1
Unknown size                            <-- modif="write" -> 'w' -> error
db> write/l db_active 1
db_active               1 = 1           <-- control: letter modifier works
db> x/x db_active
db_active:      1
db> c
CPU0 restarting CPUs: 0x000000000000003e
 restarted
```

Success criterion: the three markers (mixed `a`/`i` walk on `examine/4`,
empty output on `print/4`, `Unknown size` on `write/4`) with all controls
behaving normally.

## Note — post-continue guest state (NOT part of this finding)

After `c`, this snapshot's guest does not resume userland (no serial echo, no
ssh; vCPUs ~5%). A controlled experiment (`ab_cycle.sh 5 none`, i.e. enter ddb,
send **no commands**, `c`) reproduces the same starvation — the behavior is a
property of the `debug.enter_debugger`/`kdb_trap`/`restart_cpus` resume path
(adjacent to DF-2246/DF-5561 territory), **not** of anything in
`sys/ddb/db_command.c`. Documented as a cross-file lead in VERDICT.md; the
modifier-bug evidence above was fully captured before the continue.
