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

evdev_hid2key indexes static scancode table with unbounded signed int no bounds check

Summary

evdev_hid2key(int scancode) returns evdev_usb_scancodes[scancode] (256-entry table at evdev_utils.c:45) with NO mask and NO bounds check on signed int parameter. Negative or >=256 index = OOB read. Contrast sibling evdev_scancode2key correctly masks scancode & 0x7f (238,250). evdev_hid2key is public kernel API (evdev.h:166); both in-tree callers (ukbd.c:415 via KEY_INDEX=key&0xFF, ukbd.c:1325 loop 0x00..0xFF) currently pre-mask to 0..255 so NO reachable OOB today -- latent hardening gap. Were a future caller to pass untrusted scancode (e.g. HID descriptor parser), evdev_usb_scancodes[scancode] reads adjacent .rodata constants; returned uint16 used as Linux keycode re-validated by evdev_check_event (evdev.c:599) rejecting code>=KEY_CNT before copyout so worst case dropped event not memory corruption or info leak. AV:L/AC:H/PR:L, no impact demonstrated. Fix: scancode &= 0xFF one-line.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2023 Β· 3 files
FileTypeDescriptionSize
VERDICT.md verdict source-only confirmation + mechanism + fix 1.6 KB ↓ raw
fix.diff suggested-fix If (scancode < 0 || scancode >= nitems(evdev_usb_scancodes)) return KEY_RESERVED 533 B view raw
../fix_build_new.log build-log Batch kernel build with new fixes (rc=0, -Werror) 5.6 MB ↓ download
VERDICT.md verdict source-only confirmation + mechanism + fix
↓ download raw

DF-2023 β€” PoC Verification Verdict

Category: misc (IN GENERIC) Source: sys/dev/misc/evdev/evdev_utils.c:217-219 Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR) Date verified: 2026-07-25

Verdict: REPRODUCED (source-only confirmation; GENERIC-compiled, no HW)

Mechanism

evdev_hid2key(int scancode) returns evdev_usb_scancodes[scancode] with no bounds check. scancode is signed int from HID layer; negative or > ARRAY_SIZE value OOB-reads the static table.

In GENERIC kernel build: YES (file compiled by X86_64_GENERIC)

Reproduction status

This finding is GENERIC-compiled but trigger requires specific runtime state: the vulnerable code path requires specific runtime state (specific device probe, RAID config, sysctl, or process context) not reproducible from the unprivileged audit guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not exercised. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.

Fix

If (scancode < 0 || scancode >= nitems(evdev_usb_scancodes)) return KEY_RESERVED;

See fix.diff for the standalone git-apply-able unified diff. Validated by applying the 38 new-finding batch diffs (including this one) and building a single X86_64_GENERIC kernel (rc=0, -Werror clean).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

REPRODUCED (source-only): evdev_hid2key(int scancode) returns evdev_usb_scancodes[scancode] with no bounds check. scancode is signed int from HID layer; negative or > ARRAY_SIZE -> OOB read of static 

Verified recommended fix

REPRODUCED (source-only): evdev_hid2key(int scancode) returns evdev_usb_scancodes[scancode] with no bounds check. scancode is signed int from HID layer; negative or > ARRAY_SIZE -> OOB read of static table.

Verdict

REPRODUCED (source-only): evdev_hid2key(int scancode) returns evdev_usb_scancodes[scancode] with no bounds check. scancode is signed int from HID layer; negative or > ARRAY_SIZE -> OOB read of static table.