iconv_xlat16_open trusts attacker-controlled cp_data size causing OOB heap read and NULL-deref panic
Summary
iconv_xlat16_open() interprets csp->cp_data as [0x200-pointer index][variable count of 0x80-uint32_t data blocks] structure but never validates cp_data is non-NULL or large enough to contain index and all data blocks implied by non-NULL index entries. Pointer headp advanced by 0x80 uint32_t (0x200 bytes) per non-NULL entry with no bounds check against actual allocation. kern.iconv.add sysctl has NO privilege check only enforces ia_datalen<=ICONV_CSMAXDATALEN not minimum. Any local user can register maliciously undersized xlat16 table. When filesystem mounted using charset iconv_xlat16_open sets d_table[i] to wild pointers past allocation. Every dp->d_table[c1][c2] lookup in iconv_xlat16_conv/tolower/toupper reads OOB kernel heap leaking data back to userspace as translated text. Deterministic kernel heap info leak of up to ~256KB per malicious table leaking adjacent slab allocations kernel pointers KASLR defeat credentials network data. ia_datalen=0 variant cp_data==NULL causes immediate NULL-deref panic at line 71. struct iconv_cspair has no cp_datalen field converter has no size information.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2236 · 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df2236_register.c | trigger-source | registers malicious xlat16 pair with cp_data=NULL via sysctl | 1.9 KB | view raw |
| build.sh | build-script | builds df2236_register | 137 B | view raw |
| run.sh | run-script | register malicious pair + mount_msdos -D CP437 -> panic | 1.5 KB | view raw |
| VERDICT.md | verdict | analysis: mechanism, privilege correction, fix | 4.0 KB | ↓ raw |
| fix.diff | suggested-fix | add cp_datalen + validate cp_data bounds in iconv_xlat16_open | 2.1 KB | view raw |
| fix_build.log | build-log | libiconv.ko rebuild with fix (rc=0) | 9.1 KB | view raw |
| panic.txt | panic-signature | fatal trap 12 in iconv_xlat16_open+0x34 | 663 B | view raw |
| env.txt | environment | uname, cc, modules, privilege note | 316 B | view raw |
DF-2236 — iconv_xlat16_open trusts attacker-controlled cp_data (NULL deref + OOB read)
Verdict
REPRODUCED — kernel panic (fatal trap 12, NULL pointer dereference) in iconv_xlat16_open
confirmed on the unpatched GENERIC kernel. Fix validated: the patched libiconv.ko rejects
the malicious table with EINVAL instead of panicking.
Bug analysis
iconv_xlat16_open() (sys/libiconv/iconv_xlat16.c:59-93) interprets csp->cp_data
as a [0x200-entry index of uint32_t* pointers][variable count of 0x80-uint32_t data blocks]
layout, but never validates that cp_data is non-NULL or large enough:
headp = (uint32_t *)((caddr_t)csp->cp_data + sizeof(dp->d_table)); // cp_data + 0x1000
idxp = (uint32_t **)csp->cp_data; // cp_data (may be NULL!)
for (i = 0; i < 0x200; i++) {
if (*idxp) { // dereferences cp_data — NULL => panic
dp->d_table[i] = headp;
headp += 0x80; // advances 0x200 bytes per non-NULL entry, unchecked
}
idxp++;
}
- CWE-476 NULL deref: if
ia_datalen=0,cp_datastays NULL →*idxpdereferences NULL → fatal trap 12 page fault. This is the demonstrated crash. - CWE-125 OOB read: if
ia_datalenis non-zero but undersized,idxpreads pastcp_data(OOB index entries) andheadpadvances pastcp_data(OOB data pointers). Later,iconv_xlat16_conv()dereferencesdp->d_table[c1][c2]→ OOB heap read.
Reproduction
- Register a malicious xlat16 charset pair
"CP437"→"ISO8859-1"withia_datalen=0(→cp_data=NULL) viakern.iconv.addsysctl. - Create a 32 MB FAT16 image (
newfs_msdos -F 16). mount_msdos -D CP437 /dev/vn0 /mnt→ kernelmsdosfs_mountcallsiconv_open("CP437","ISO8859-1")→ finds the malicious pair →iconv_xlat16_open→*NULL→ panic.
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x0 fault code = supervisor read data, page not present Stopped at iconv_xlat16_open+0x34: movq (%rsi,%rax,1),%rdx
The mount_msdos userland registration is not a barrier: kiconv_add_xlat16_cspair()
returns 0 (success) when the pair already exists (kiconv_lookupcs() hit), so the
pre-registered malicious pair is the one iconv_open finds.
Privilege correction (finding claim error)
The finding claims "kern.iconv.add sysctl has NO privilege check" and rates this
PR:L (unprivileged). This is incorrect: on DragonFly 6.5-DEVELOPMENT, writing
to kern.iconv.add requires root — an unprivileged user gets EPERM:
$ ./df2236_register CP437 ISO8859-1 # as maxx (uid 1001) sysctl kern.iconv.add failed: Operation not permitted
The SYSCTL_PROC node uses CTLFLAG_RW without CTLFLAG_ANYBODY, so the kernel's
sysctl write path enforces suser_cred. The real privilege is PR:H (root-only),
making this a root→kernel DoS, not an unprivileged→kernel exploit. The code bug
(NULL deref / OOB) is real, but the severity is lower than claimed.
Fix
Three-part fix (in fix.diff):
sys/sys/iconv.h: addint cp_datalentostruct iconv_cspairto track the actual allocation size.sys/libiconv/iconv.c: setcp_dataleniniconv_register_cspair(default 0) and iniconv_sysctl_add(storeia_datalen).sys/libiconv/iconv_xlat16.c: validatecp_data != NULLandcp_datalen >= sizeof(dp->d_table)before use; check that each implied 0x200-byte data block stays withincp_data + cp_datalenbefore assigningdp->d_table[i].
Fix validation
| Kernel | PoC result |
|---|---|
| Unpatched (#0 baseline) | fatal trap 12 panic in iconv_xlat16_open+0x34 (NULL deref) |
| Patched libiconv.ko | mount returns EINVAL gracefully, no panic, guest alive |
| Patched + normal mount (no charset) | mount succeeds (exit 0) — fix doesn't break normal operation |
Rebuilt libiconv.ko with make KERNCONF=X86_64_GENERIC in sys/libiconv/ (rc=0),
installed to /boot/kernel/libiconv.ko, reloaded.
Fix verification
fixedVALIDATED: unpatched libiconv.ko -- same PoC (register cp_data=NULL + mount_msdos -D CP437) -> fatal trap 12 panic in iconv_xlat16_open+0x34. Patched libiconv.ko -- same PoC -> mount returns EINVAL gracefully (iconv_xlat16_open detects NULL cp_data, returns EINVAL), NO panic, guest alive. Normal mount without charset succeeds (exit 0) -> fix doesn't break operation.
baseline: fatal trap 12 page fault at iconv_xlat16_open+0x34, fault addr=0x0, guest DOWN. patched: mount_msdos -D CP437 -> 'Invalid argument' (EINVAL from iconv_xlat16_open), guest UP. normal mount_msdos (no -D): exit 0 on patched kernel.
Confirmed kernel references
Detail
Exploit chain
none (not memory corruption in write/UAF sense -- NULL-pointer-dereference DoS). Root registers malicious xlat16 pair with cp_data=NULL via kern.iconv.add sysctl, then mount_msdos -D
Evidence (decisive lines)
Fatal trap 12: page fault while in kernel mode | fault virtual address = 0x0 | fault code = supervisor read data, page not present | Stopped at iconv_xlat16_open+0x34: movq (%rsi,%rax,1),%rdx | db>
PoC changes
Created df2236_register.c (registers malicious xlat16 pair with ia_datalen=0 via kern.iconv.add sysctl), build.sh, run.sh. Finding's PoC scaffold did not exist; built from scratch.
Verified recommended fix
Three-part fix: (1) add int cp_datalen to struct iconv_cspair (sys/sys/iconv.h) to track allocation size; (2) set cp_datalen in iconv_register_cspair and iconv_sysctl_add; (3) in iconv_xlat16_open validate cp_data != NULL and cp_datalen >= sizeof(dp->d_table), and check each implied 0x200-byte data block stays within cp_data+cp_datalen before assigning dp->d_table[i]. Supersedes finding proposal (finding did not propose concrete fix). Full diff in findings/poc/DF-2236/fix.diff.
Verdict
REPRODUCED. iconv_xlat16_open (iconv_xlat16.c:68-78) dereferences csp->cp_data without NULL check. Registering charset pair with ia_datalen=0 via kern.iconv.add leaves cp_data=NULL; when iconv_open later finds this pair (triggered by mount_msdos -D CP437), iconv_xlat16_open executes *idxp where idxp=NULL -> fatal trap 12 page fault at iconv_xlat16_open+0x34 (fault virtual address 0x0). Confirmed by panic in boot.log. PRIVILEGE CORRECTION: finding claims kern.iconv.add has 'NO privilege check' (PR:L) but this is FALSE -- sysctl returns EPERM for unprivileged users (verified as maxx uid 1001). Real privilege PR:H (root-only), making this root->kernel DoS, not unprivileged->kernel.
No comments yet.