Kernel stack info leak via TWS_IOCTL_GET_COMPATIBILITY_INFO (uninitialized driver_version tail)
Summary
tws_ioctl_aen (tws_user.c:267) declares uninitialized stack-local "struct tws_compatibility_packet cpkt". Handler at lines 304-326 sets every numeric field but populates only leading 14 bytes of driver_version[32] via memcpy(cpkt.driver_version,TWS_DRIVER_VERSION_STRING,sizeof(TWS_DRIVER_VERSION_STRING)) at 307-308 (TWS_DRIVER_VERSION_STRING is "10.80.00.001" sizeof=14 tws.h:67), leaving driver_version[14..31] (18 bytes) as raw kernel-stack residue. len=sizeof(struct tws_compatibility_packet)=56 clamped to min(len,ubuf->driver_pkt.buffer_length) at 322-324; caller supplying buffer_length>=56 gets full struct. memcpy(ubuf->data_buf,&cpkt,len) at 325 places 18 uninit bytes into kernel ioctl scratch buffer. TWS_IOCTL_GET_COMPATIBILITY_INFO is _IOWR(T,209,struct tws_ioctl_with_payload) so sys_generic.c:729-730 copyout(data,uspc_data,size) on driver SUCCESS return delivers stack residue to userspace. sys_generic.c:691-697 only bzeros scratch for pure IOC_OUT; for IOC_INOUT buffer holds copyind user data NOT pre-zeroed so genuine kernel stack leak not echoed user data. /dev/twsN UID_ROOT GID_OPERATOR mode 0600 (tws.c:284-285) so root-only by default; becomes Medium-relevant if admin grants operator group rw for tw_cli monitoring (realistic for 3ware/LSI RAID boxes). 18 bytes of kernel stack per call with attacker-controlled repetition useful for KASLR bypass/pointer harvest. AV:L/PR:H/AC:L, C:L.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2032 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | original PoC README | 1.2 KB | β raw |
| VERDICT.md | verdict | full source-trace verdict | 1.5 KB | β raw |
| build.sh | build-script | build/verify instructions | 438 B | view raw |
| env.txt | environment | guest environment (no matching HW) | 814 B | view raw |
| fix.diff | suggested-fix | git-apply-able fix, verified to compile -Werror | 471 B | view raw |
| fix_build.log | build-log | Phase 8 module build evidence (-Werror rc=0) | 1.4 KB | view raw |
| run.sh | run-script | run instructions (HW-gated) | 320 B | view raw |
DF-2032 PoC β Kernel stack info leak via TWS_IOCTL_GET_COMPATIBILITY_INFO
Preconditions
- Can open
/dev/tws0(default: root only, mode 0600 UID_ROOT GID_OPERATOR) - 3ware/LSI TWS Thunderbolt SCSI controller present
Trigger
cc -O2 -Wall -o tws_leak tws_leak.c
sudo ./tws_leak
The PoC issues TWS_IOCTL_GET_COMPATIBILITY_INFO with buffer_length >= 56
(the full struct size). The driver writes only the first 14 bytes of
driver_version[32] from the version string; bytes 14..31 are raw kernel
stack residue.
Expected output
driver_version[0..13] = "10.80.00.001" driver_version[14..31] (LEAKED stack bytes): XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX N/18 bytes non-zero -> kernel stack leaked
The leaked 18 bytes vary across invocations (stack residue: return addresses, frame pointers, etc.).
Success criterion
Non-zero bytes in driver_version[14..31] that vary across runs, confirming
genuine kernel stack leak (not echoed user data β the ioctl scratch buffer is
not pre-zeroed for _IOWR commands).
Fix
Add bzero(&cpkt, sizeof(cpkt)); after the declaration at line 267, or use
struct tws_compatibility_packet cpkt = { {0} };.
VERDICT -- DF-2032 (Low)
Verdict: REPRODUCED (source-only)
Impact: leak:19 kernel stack residue bytes (driver_version[13..31]); HW-gated (needs tws(4)), source-confirmed
Confidence: certain
Mechanism (source-traced)
tws_ioctl_aen (tws_user.c:267) declares uninitialized stack-local struct tws_compatibility_packet cpkt. The TWS_IOCTL_GET_COMPATIBILITY_INFO handler (:304-326) populates every numeric field but only writes the leading bytes of driver_version[32] via memcpy(cpkt.driver_version, TWS_DRIVER_VERSION_STRING, sizeof(...)) at :307-308 (the string '10.80.00.001' is 13 bytes incl NUL), leaving driver_version[13..31] (19 bytes) as raw kernel-stack residue. memcpy(ubuf->data_buf, &cpkt, len) at :325 with len=sizeof(struct)=56 then copyout's the residue to userspace.
Why not runtime-reproduced
The guest (DragonFlyBSD 6.5-DEVELOPMENT #0 master DEV, KVM) has NO matching
hardware: pciconf shows no mfi/tws/iir RAID controller and no amdgpu/DRM GPU;
the driver therefore cannot attach and the vulnerable path is not runtime-
triggerable here. The defect was confirmed at the source level by tracing
the cited path:line against sys/, and the proposed fix was applied and the
affected module (tws) built clean with -Werror (see fix_build.log).
Fix
tws_user.c:267: add bzero(&cpkt, sizeof(cpkt)) after the declaration (or initialize the struct) so driver_version[13..31] is not leaked as stack residue.
The standalone, git-apply-able diff is fix.diff.
Fix verification
not_testableVALIDATED build.
VALIDATED build.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- t
- w
- s
- /
- t
- w
- s
- _
- u
- s
- e
- r
- .
- c
- :
- 2
- 6
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- t
- w
- s
- /
- t
- w
- s
- _
- u
- s
- e
- r
- .
- c
- :
- 3
- 0
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- t
- w
- s
- /
- t
- w
- s
- _
- u
- s
- e
- r
- .
- c
- :
- 3
- 2
- 5
Detail
Exploit chain
none (HW-gated).
Evidence (decisive lines)
HW-GATED (no tws). Source-CONFIRMED. tws_ioctl_aen declares uninit tws_compatibility_packet cpkt; memcpy only 13B of driver_version[32]; copyout sends 19B kernel stack residue to user.
Verified recommended fix
Add bzero(&cpkt, sizeof(cpkt)) after declaration.
Verdict
HW-GATED (no tws). Source-CONFIRMED. tws_ioctl_aen declares uninit tws_compatibility_packet cpkt; memcpy only 13B of driver_version[32]; copyout sends 19B kernel stack residue to user.
No comments yet.