Before-buffer OOB read and conditional OOB write on CTRL(n) down-arrow at end of history
Summary
Pressing down-arrow when history cursor already at/past newest entry takes else branch clearing first byte of input buffer to NUL then falls through to hist_redraw. index() returns db_lbuf_start itself then db_le[-1] dereference reads one byte before buffer. If that byte happens to be \r or \n *--db_le=\0 writes NUL one byte before buffer 1-byte OOB write. CTRL(n) handler at :232 tests if(db_lhistcur<db_lhistidx-1) on first down-arrow always false takes else at :236-243 executes *db_lbuf_start=\0. No break falls through hist_redraw :245. index(:249) returns db_lbuf_start itself. :250 evaluates db_le[-1] which is db_lbuf_start[-1] OOB read before caller buffer. If byte==\r||\n :251 *--db_le=\0 stores NUL at db_lbuf_start[-1] 1-byte OOB write. Code lacks db_le>db_lbuf_start lower-bound check. Reachable with single keypress no history needed. DDB-only privileged context defense-in-depth. [VERIFIED pass2: value-attributed before-buffer read - pre-byte 0x0a ends 0x0d vs 0x58 unchanged tracking the OOB byte; lower-bound guard fix-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2239 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| run.log | β | 899 B | view raw | |
| run_stock_boot.log | β | 17.4 KB | view raw | |
| run_fixed_boot.log | β | 16.3 KB | view raw | |
| fix.diff | β | 390 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 | β | 168 B | view raw | |
| env.txt | β | 234 B | view raw |
Fix verification
fixedStock: pre-buffer byte mutated 0a->0d on the trigger, untouched for 0x58. Fixed: no pre-buffer access in either mode, control bytes identical.
run.log stock vs fixed DF2239 lines and region hex dumps.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: DF2239 mode1 pre-set=0a pre-now=0d and mode0 pre-set=58 pre-now=58 on stock; both unchanged on fixed. Hex dumps of region[8..24] in the boot logs.
PoC changes
Built the kernel canary harness + console-input injection (harness.diff) feeding a real CTRL(n)+Enter keystroke stream to db_readline.
Verified recommended fix
Guard the trailing-newline strip with db_le > db_lbuf_start before reading db_le[-1].
Verdict
Proven by behavioral attribution: pressing CTRL(n) as the first key (empty history) makes hist_redraw compute db_le == db_lbuf_start and dereference db_le[-1]. With a pre-buffer 0x0a the NUL write fires and the byte ends as 0x0d (two OOB writes total incl. the later Enter store); with 0x58 no write occurs -- the branch outcome tracks the out-of-bounds byte value, proving the read. Fixed guard (db_le > db_lbuf_start) leaves both cases untouched. DDB console-holder only, value never displayed; Low/defense-in-depth confirmed.
No comments yet.