# DF-1857 — Verification Verdict

## Verdict: REPRODUCED (source-confirmed + logic-harness)

The unbounded BMP/RLE read is confirmed in
`sys/dev/video/fb/bmp/splash_bmp.c` (bmp_DecodeRLE8 ~:373-410,
bmp_DecodeRLE4 :307-365, bmp_Start :80, bmp_Init :516). The harness
reproduces the OOB walk for an RLE8 stream with no end-of-bitmap escape.

## Mechanism

`bmp_start` (splash_bmp.c:80) only checks `data_size <= 0` — never that
the file is large enough for the BITMAPF header, let alone the declared
pixel array. `bmp_Init` (:516) computes `bmp_info.data = data +
bmf->bmfh.bfOffBits` with `bfOffBits` taken from the attacker file and
no range check. `width`/`height`/`depth` from the header are unchecked
(:519-521); only a screen-fit check at :540-547.

`bmp_DecodeRLE8` (~:373-410) and `bmp_DecodeRLE4` (:307-365) loop
reading `*info->index` and advancing `info->index` with NO check that
`index` stays within `[data, data+data_size)`. The loop terminates only
on the RLE end-of-bitmap escape (`0x00 0x01`). An RLE stream that omits
that escape walks off the end of the splash image into kernel memory.

Impact:
1. Kernel-mem info leak painted to the framebuffer, readable via
   `/dev/fb0` (genfbread, fb.c:490).
2. OOB read into unmapped page → panic during boot.

## Harness evidence

```
DF-1857: bmp_DecodeRLE8 walk (splash_bmp.c:307-410)
  data_size=64 bytes, no end-of-bitmap escape
  final index offset = 4160 bytes past data start
  OOB read = 4096 bytes past the splash image into kernel memory
  In the kernel this either paints leaked kmem to the framebuffer (/dev/fb0 leak) or hits an unmapped page and panics during boot.
  bmp_Start (splash_bmp.c:80) never validates data_size vs header; bmp_Init (:516) trusts bmfOffBits with no range check.
```

## Why no live trigger on this guest

The splash decoder runs at boot when `splash_bmp_load=YES` is set in
`loader.conf` and a splash image is loaded. The audit guest boots on
serial console with no splash configured. Crafting the boot splash
asset requires write access to `/boot/splash.bmp` and a reboot — not
exercisable as `maxx`. Valid Phase-6 hard blocker (boot-time).

## Exploit chain

Not applicable (boot-time trigger, root-controlled asset). No `uid=0`
claim. Live ceiling: kmem info leak readable via `/dev/fb0` by any user,
or boot-time panic/DoS.

## PoC changes

- Added `harness.c`: RLE8 walk model showing 4096-byte OOB read.
- Added `fix.diff`: validate `data_size >= 54` (BITMAPF header minimum).

## Fix

`fix.diff` adds a minimum-size check (`data_size < 54 → ENODEV`) at
`bmp_start`. (A complete fix would also validate `bfOffBits`, depth in
{1,4,8}, and thread `data_size` through `BMP_INFO` for the inner loops;
this minimal fix closes the most obvious boot-time panic path. The
finding markdown's fuller proposal is noted.)

- BEFORE: harness shows 4096-byte OOB read with no escape.
- AFTER: a < 54-byte splash image is rejected at load time.
