β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2032

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2032 Β· 7 files
FileTypeDescriptionSize
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
README.md readme original PoC README
↓ download raw

DF-2032 PoC β€” Kernel stack info leak via TWS_IOCTL_GET_COMPATIBILITY_INFO

Preconditions

  1. Can open /dev/tws0 (default: root only, mode 0600 UID_ROOT GID_OPERATOR)
  2. 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.md verdict full source-trace verdict
↓ download raw

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_testable
baseline reproduced→ patch + rebuild →patched clean

VALIDATED build.

VALIDATED build.
↓ fix.difftws.ko build rc=0 -Werror

Confirmed kernel references

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.