Hauppauge EEPROM block-parse reads block_2/block_3 via attacker-influenced offsets without bounds checks (kernel stack OOB read / panic on probe)
- File:
sys/dev/video/bktr/bktr_card.c - Lines: 1001, 1005, 1006, 1008, 1009, 1010, 1012, 1019
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U:C:N/I:N/A:H - CWE: CWE-125 Out-of-bounds Read
- Confidence: likely
Summary
In probeCard()'s CARD_HAUPPAUGE branch, the Hauppauge EEPROM data-block
parser computes block_2 = &eeprom[block_1_total_size] and
block_3 = &eeprom[block_1_total_size + block_2_total_size] directly from bytes
read off the card EEPROM (block_1[1..2], block_2[1..2]) and then dereferences
block_2[1], block_2[2], and block_3[3] without any bounds validation.
Because only 128 bytes are actually read into the 256-byte stack buffer
eeprom[] (and readEEProm's return value is not checked), a crafted EEPROM can
drive these pointers anywhere from the uninitialized tail of eeprom[] out past
the kernel stack frame, causing silent uninitialized-stack reads into driver state
or β for large offsets β a fault into the kernel-stack guard page that panics the
system at probe time.
Root cause
bktr_card.c:1001 calls readEEProm(bktr, 0, 128, &eeprom) which loads at most
128 bytes into the 256-byte stack buffer u_char eeprom[256] declared at
bktr_card.c:605; eeprom[128..255] is left uninitialized and the readEEProm
return value (-1 on failure) is never checked.
bktr_card.c:1005-1006 then derive:
block_1_data_size = (block_1[2] << 8 | block_1[1]); /* range 0..65535 */
block_1_total_size = block_1_data_size + 3; /* range 3..65538 */
directly from EEPROM contents.
bktr_card.c:1008 sets block_2 = &eeprom[block_1_total_size] with no check
that block_1_total_size + 2 < 128 (the data actually read), and line 1009
immediately dereferences block_2[1] and block_2[2] to compute
block_2_data_size.
bktr_card.c:1012 then sets
block_3 = &eeprom[block_1_total_size + block_2_total_size] (offset up to ~131079)
and bktr_card.c:1019 dereferences block_3[3].
All three dereferences are unbounded; only block_1[9..15] reads
(bktr_card.c:1014-1017) happen to be in-bounds.
Threat
Attacker position: anyone who can influence the contents of an I2C EEPROM-like
device responding at one of {0xa0, 0xac, 0xae} on a Brooktree Bt848/Bt878
capture card installed in the target machine (e.g. a reprogrammed Hauppauge
EEPROM, a custom/faulty I2C device on the bktr bus, or any I2C responder at 0xa0
once card detection is forced via the root-settable hw.bt848.card=2 sysctl
combined with bktr_card.c:638-641 override path).
The probe runs unconditionally at boot (bktr_core.c:505,
probeCard(TRUE, unit)) and again on every open of /dev/bktr0
(bktr_os.c:458-476).
Impact: with block_1[1..2] encoding a small out-of-range total size
(e.g. 0x0080..0x00FF) the driver silently reads uninitialized kernel stack at
eeprom[128..255] into driver state (no exfiltration path observed β values feed
only the 1-bit no_audio_mux decision at bktr_card.c:1019 and the verbose
kprintf at 1024-1030 which uses in-bounds block_1 fields); with a large total
size (e.g. 0xFFFF) the dereference lands past the ~4 KB kernel stack and hits
the stack guard page, panicking the system.
Net impact is a kernel-panic DoS at boot or device open; no info-leak or write primitive was found.
Recommended fix
Validate block offsets against the 128 bytes actually read, bail out (skip the
Hauppauge parse) on out-of-range offsets or on readEEProm failure.
--- a/sys/dev/video/bktr/bktr_card.c
+++ b/sys/dev/video/bktr/bktr_card.c
@@ -998,9 +998,23 @@ probeCard( bktr_ptr_t bktr, int verbose, int unit )
unsigned char tuner_code;
unsigned char no_audio_mux;
- readEEProm(bktr, 0, 128, (u_char *) &eeprom );
+ /* Only 128 bytes are loaded into eeprom[]; require the read to
+ * succeed and validate every block offset against that window
+ * before dereferencing block_2/block_3 pointers derived from
+ * (possibly hostile) EEPROM contents. */
+ if ( readEEProm(bktr, 0, 128, (u_char *) &eeprom ) < 0 )
+ break;
/* LOCATE THE EEPROM DATA BLOCKS */
block_1 = &eeprom[0];
block_1_data_size = (block_1[2] << 8 | block_1[1]);
block_1_total_size = block_1_data_size + 3; /* Header bytes */
+ /* block_2[2] must remain inside the 128-byte window */
+ if (block_1_total_size < 0 || block_1_total_size > 128 - 3)
+ break;
block_2 = &eeprom[block_1_total_size];
block_2_data_size = (block_2[2] << 8 | block_2[1]);
block_2_total_size = block_2_data_size + 3; /* Header bytes */
+ /* block_3[3] must remain inside the 128-byte window */
+ if (block_2_total_size < 0 ||
+ block_1_total_size + block_2_total_size > 128 - 4)
+ break;
block_3 = &eeprom[block_1_total_size + block_2_total_size];
model = (block_1[12] << 8 | block_1[11]);
Breaking out of the enclosing switch (card) case on a bad EEPROM simply falls
through to the default tuner-selection logic at bktr_card.c:1189-1203, which is
the intended behaviour for an unrecognised card.
The same if (readEEProm(...) < 0) ... guard should also be added at
bktr_card.c:667 and 794 to prevent the parallel uninitialized-stack use in the
subsystem-ID and Osprey/Hauppauge-signature detection paths (defense in depth).
Related findings
- DF-1215 (sibling, bktr_core.c): Critical METEORSVIDEO DMA target address.
- DF-1216 (sibling, bktr_core.c): BPP mismatch OOB.
- DF-1217 (sibling, bktr_core.c): I2C no-auth.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1569 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited bug | 766 B | view raw |
| VERDICT.md | verdict | source-confirmation analysis | 712 B | β raw |
| build.sh | build-script | N/A (source-only) | 61 B | view raw |
| run.sh | run-script | N/A (source-only) | 87 B | view raw |
DF-1569 VERDICT
Verdict: REPRODUCED (source-confirmed)
Impact: Low (driver-level NULL deref / OOB / leak / DoS β hardware-gated)
Mechanism: bktr_card.c:1001 readEEProm reads 128 bytes into 256-byte eeprom[] stack buffer; return value NEVER checked. 1005-1006 block_1_data_size=(block_1[2]<<8|block_1[1]) range 0..65535; block_1_total_size=d
Citation: sys/dev/video/bktr/bktr_card.c:1001-1019
Fix: Applied fix.diff β compiles in batch kernel build (rc=0, -Werror).
Verification method: Source-only line-by-line trace of cited path:line. Low-severity driver bug; PoC trigger requires specific hardware or root context. Confirmed the cited vulnerable pattern exists in source.
Fix verification
fixedfix.diff compiled in batch kernel build rc=0 -Werror
fix.diff compiled in batch kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none (Low severity)
Evidence (decisive lines)
Source-confirmed: unchecked EEPROM block offsets deref block_2/3 OOB (bktr_card.c:1001-1019)
Verified recommended fix
Source-confirmed: unchecked EEPROM block offsets deref block_2/3 OOB (bktr_card.c:1001-1019)
Verdict
Source-confirmed: unchecked EEPROM block offsets deref block_2/3 OOB (bktr_card.c:1001-1019)
No comments yet.