Read-before-bounds-check: 1-byte OOB read of source buffer in UCS-2/UTF-16 input parsing
Summary
In iconv_ucs_conv src-code-is-proper-subset-of-ENCODING_UNICODE branch converter unconditionally reads two bytes from source buffer (lines 248-253) before checking ir<inlen at line 267. When caller passes odd inbytesleft (ir==1) trivially produced by truncated UCS-2/UTF-16 string in crafted filesystem image or SMB packet this reads one byte past end of source buffer. Sibling iconv_xlat16 performs same operation safely via ir>1?*(src+1)&0xff:0 (iconv_xlat16.c:135). If OOB byte matches surrogate pattern 0xd8-0xdb AND UCS4+FROM_UTF16 set inlen becomes 4 function also reads src[2] and src[3] up to 3-byte overrun worst case. All overrun paths break with ret=-1 before any output written no info leak. Memory-safety/defense-in-depth defect. Trigger: crafted filesystem image (msdosfs long-name ntfs filename cd9660 Joliet UDF) with UCS-2/UTF-16 string odd length mounted by root with KICONV direct UCS-2LE/UTF-16BE cspair. Also via SMB client smb_copy_iconv processing server-supplied UCS-2 odd length. Practical impact low kernel heap slack rarely faults no data exfiltrated. Under KASAN definite report. [VERIFIED pass2: real UCS pair via kern.iconv.add faults exactly at iconv_ucs_conv+0x210 reading src 1 before the ir-lt-inlen check on a guard-page rig; silent 1-byte heap-slack read in real use, 3-byte speculation refuted; ir-lt-2 guard fix-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2235 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| run.log | β | 731 B | view raw | |
| panic.txt | β | 386 B | view raw | |
| run_stock_boot.log | β | 13.6 KB | view raw | |
| run_controls_boot.log | β | 17.4 KB | view raw | |
| run_fixed_boot.log | β | 16.3 KB | view raw | |
| fix.diff | β | 321 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 | β | 193 B | view raw | |
| env.txt | β | 234 B | view raw |
Fix verification
fixedStock: Fatal trap 12 at the exact OOB read. Fixed (ir<2 guard): trigger returns ret=-1 consumed=0, guard page untouched, control conversion byte-identical (e4 89 81), guest healthy.
run_stock_boot.log vs run_fixed_boot.log (DF2235: TRIGGER SURVIVED ret=-1) and control output equality across both boots.
Confirmed kernel references
Detail
Evidence (decisive lines)
panic.txt and run_stock_boot.log: Fatal trap 12, supervisor read, fault VA = freed page, Stopped at iconv_ucs_conv+0x210 movzbl 0x1(%rbx),%edx. Controls in run_controls_boot.log (AB -> e4 89 81). Fixed boot: DF2235: TRIGGER SURVIVED ret=-1 consumed=0.
PoC changes
Replaced the sketch with a kernel-compiled harness: real kern.iconv.add registration of a UTF-8/UCS-2LE pair (converter name UTF-16BE, not UCS -- discovered via kern.iconv.drvlist) and a freed-successor-page guard so the kernel-mode fault is not silently re-faulted by vm_fault (a pmap_kremove-only guard failed because the vm_map entry remained).
Verified recommended fix
Check ir>=2 (and or>=2) before the unconditional 2-byte read in the proper-subset branch of iconv_ucs_conv.
Verdict
The 1-byte OOB read is proven mechanically: with a 1-byte UCS-2LE source placed at a page boundary and the following page freed, iconv_conv faults precisely at iconv_ucs_conv+0x210 (the q = (p+1) load at iconv_ucs.c:248) on the unmapped src[1] before the ir
No comments yet.