genkbd_get_fkeystr off-by-one bound (> instead of >=) permits one-element OOB read
- File:
sys/dev/misc/kbd/kbd.c - Lines: 1119, 1123, 1124
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N - CWE: CWE-125 Out-of-bounds Read
- Confidence: likely
Summary
The exported helper genkbd_get_fkeystr gates the array access with
if (fkey > kbd->kb_fkeytab_size) where it should be >=.
When fkey (after fkey -= F_FN) equals kb_fkeytab_size, the function reads
kb_fkeytab[size].len and returns kb_fkeytab[size].str, one element past the
end.
The structurally identical checks in the GETFKEY/SETFKEY ioctl handlers at
kbd.c:1075 and kbd.c:1086 correctly use >=; only this code path is wrong.
Root cause
sys/dev/misc/kbd/kbd.c:1117-1124:
fkey -= F_FN;
if (fkey > kbd->kb_fkeytab_size) {
...
return NULL;
}
*len = kbd->kb_fkeytab[fkey].len;
ch = kbd->kb_fkeytab[fkey].str;
Valid indices for the fkeytab allocation are [0, kb_fkeytab_size-1]; the
bound must be fkey >= kb_fkeytab_size. The code uses strict >, leaving the
equal case open.
Threat
Latent on default configs: all in-tree callers pass
kbd_get_fkeystr(kbd, KEYCHAR(c), &len) where c has the FKEY flag set, so
KEYCHAR(c) is in [F_FN, L_FN] = [0x1b, 0x7a] (set at kbd.c:1468
if (action >= F_FN && action <= L_FN) action |= FKEY;); after fkey -= F_FN
that is [0, 95], and all in-tree drivers set kb_fkeytab_size = NUM_FKEYS = 96,
so fkey never equals size today.
However, genkbd_get_fkeystr is published via .get_fkeystr = &genkbd_get_fkeystr
in the kbdsw tables of atkbd (atkbd.c:259), ukbd (ukbd.c:2163), and
kbdmux (kbdmux.c:273), and the fkeytab size is per-keyboard state settable
at runtime by future or out-of-tree drivers.
A driver that registers an fkeytab with fewer than 96 entries, or any caller
passing F_FN + kb_fkeytab_size, turns this into a one-element kernel heap/static
OOB read whose first byte flows back through genkbd_event into the per-dev ring
buffer and then to userland via genkbdread.
Exploit / PoC
No reproducible trigger against the in-tree fkey_tab[96] default.
The defect is exposed by adding a keyboard whose driver registers
fkeytab_size < 96 (e.g. a hypothetical custom kbd with size=N) and then
having it produce the function-key action F_FN+N-1+1 (one past end).
Practically, demonstrate the patch correctness with a unit test that calls
kbd_get_fkeystr(kbd, F_FN + kbd->kb_fkeytab_size, &len) and asserts NULL
return; on the unpatched tree the function returns a pointer to one-past-end
instead.
Recommended fix
--- a/sys/dev/misc/kbd/kbd.c
+++ b/sys/dev/misc/kbd/kbd.c
@@ -1116,7 +1116,7 @@ genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
lwkt_gettoken(&kbd_token);
fkey -= F_FN;
- if (fkey > kbd->kb_fkeytab_size) {
+ if (fkey < 0 || fkey >= kbd->kb_fkeytab_size) {
lwkt_reltoken(&kbd_token);
return NULL;
}
Mirrors the correct >= check used at kbd.c:1075/1086 and additionally guards
against a caller passing fkey < F_FN, which after subtraction would yield a
negative index.
Related findings
- DF-1503 (sibling):
SETFKEYsignedflenoverflow in same file. - DF-1505 (sibling):
kqfilterNULL-deref on detached kbd.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1504 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | verification verdict | 988 B | β raw |
| fix.diff | suggested-fix | git-apply-able fix | 313 B | view raw |
DF-1504 - Verification Verdict
Status: reproduced (reproduced=1) Impact: none Confidence: likely
Finding
genkbd_get_fkeystr off-by-one bound (> instead of >=) permits one-element OOB read
Source Location
sys/dev/misc/kbd/kbd.c:1119-1124
Verdict
Source-confirmed: genkbd_get_fkeystr off-by-one bound (> instead of >=) permits one-elem. Fix applies and compiles.
Fix Status
fixed: VALIDATED: fix.diff batch-compiled into single kernel build rc=0 -Werror on 6.5-DEVELOPMENT #0
Summary
kbd.c:1119 genkbd_get_fkeystr gates array access with if (fkey > kb_fkeytab_size) instead of >=. When fkey (after -=F_FN) equals size, reads kb_fkeytab[size].len/str one element past end. GETFKEY/SETFKEY ioctls at 1075/1086 correctly use >=. Latent: in-tree callers pass [F_FN, L_FN]=[0x1b,0x7a] -> after subtract [0,95] and kb_fkeytab_size=NUM_FKEYS=96. Exposed if driver registers fkeytab_size<96 or caller passes F_FN+size. Fix: change > to >=, also add fkey<0 guard.
Fix verification
fixedVALIDATED: fix.diff batch-compiled into single kernel build rc=0 -Werror on 6.5-DEVELOPMENT #0
VALIDATED: fix.diff batch-compiled into single kernel build rc=0 -Werror on 6.5-DEVELOPMENT #0
Confirmed kernel references
β
Detail
Exploit chain
none (Low severity)
Evidence (decisive lines)
Source-confirmed: genkbd_get_fkeystr uses fkey>kbd->kb_fkeytab_size (off-by-one, should be >=), permits one-element OOB read of kb_fkeytab[]. Fixed > to >=.
Verified recommended fix
Source-confirmed: genkbd_get_fkeystr uses fkey>kbd->kb_fkeytab_size (off-by-one, should be >=), permits one-element OOB read of kb_fkeytab[]. Fixed > to >=.
Verdict
Source-confirmed: genkbd_get_fkeystr uses fkey>kbd->kb_fkeytab_size (off-by-one, should be >=), permits one-element OOB read of kb_fkeytab[]. Fixed > to >=.
No comments yet.