Dead stats module carries a latent length==0 off-by-one OOB write in mod_stats_update_event (unsigned wrap β event_string[-1]); module not compiled by any Makefile β delete it or guard
Summary
length==0 wraps copy_length-1 to 0xFFFFFFFF and writes one byte before the array (net-zero clobbered by the following entry_id store); gcc -Warray-bounds flags the exact expression in the byte-exact clone run on the guest. Module not compiled by any Makefile (dead) - filed Info so it is deleted or guarded before wiring. Guard-or-delete fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4912 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| stats_len0_demo.c | β | 2.1 KB | view raw | |
| build.sh | β | 104 B | view raw | |
| run.sh | β | 89 B | view raw | |
| run.log | β | 843 B | view raw | |
| env.txt | β | 347 B | view raw | |
| fix.diff | β | 557 B | view raw | |
| verdict.json | β | 2.7 KB | view raw | |
| manifest.json | β | 878 B | view raw |
DF-4912 β stats module: latent OOB write in mod_stats_update_event on length == 0, in code that is not compiled into any kernel build
File: sys/dev/drm/amd/display/modules/stats/stats.c:316-321
Severity: Info | CWE-787 (latent) / dead code | confidence: certain
Two facts, both proven without hardware
-
The module is dead code.
sys/dev/drm/amd/amdgpu/Makefile:49-51buildsdisplay/modules/{freesync,color,info_packet}β notmodules/stats. No file insys/referencesmodules/statsor calls anymod_stats_*function (repo-widergβ zero hits outside the module itself and its header). The object is never compiled, never linked, never callable. -
Latent defect. If the module were ever wired up:
c copy_length = length; /* stats.c:316 */ if (length > MOD_STATS_EVENT_STRING_MAX) copy_length = MOD_STATS_EVENT_STRING_MAX; memcpy(&events[index].event_string, event_string, copy_length); events[index].event_string[copy_length - 1] = '\0'; /* :321 */Withlength == 0this executesevent_string[-1] = '\0'β a one-byte write before the 512-byte array, i.e. into the high byte ofentry_id(offset 3 ofstruct stats_event_cache, little-endian) β CWE-787 off-by-one. The very next statement (events[index].entry_id = core_stats->entry_id;, :323) rewrites that exact word, so the corruption is self-clobbering; net effect on the struct is zero. No caller exists to pass 0 today.
Build / run (semantic demonstration of the off-by-one only)
./build.sh # cc -o stats_len0_demo stats_len0_demo.c
./run.sh # standalone extraction of the exact kernel logic; shows the
# write lands at buffer[-1] and is then overwritten
The demo proves the index arithmetic; it does NOT execute kernel code (none exists in the build).
Recommendation
Delete sys/dev/drm/amd/display/modules/stats/ (matches upstream direction β
the module was never wired into the amdgpu driver), or β if it is kept for
future wiring β apply fix.diff (guard length == 0 / clamp to
MOD_STATS_EVENT_STRING_MAX - 1).
VERDICT β DF-4912
status: untested β deliberately: the vulnerable code is not part of any
kernel build, so there is no kernel artifact to test. reproduced: 0
(kernel path) / semantic off-by-one demonstrated in userspace
(run.log). impact: none (dead code + self-clobbering write). Severity
Info.
Fact 1 β the module is dead code (certain)
sys/dev/drm/amd/amdgpu/Makefile:49-51compilesdisplay/modules/freesync,display/modules/color,display/modules/info_packetβ stats is absent (confirmed both in the audit tree and in the guest's/usr/src, run.log).- Repo-wide search (
rg -rn "modules/stats" sys/andrg mod_stats_*): zero hits outsidemodules/stats/stats.citself andmodules/inc/mod_stats.h. No allocation, no call, no linkage.sys/conf/fileshas no stats entry.
Fact 2 β latent off-by-one OOB write (certain, demonstrated)
mod_stats_update_event (stats.c:296-326):
copy_length = length; /* :316 */
if (length > MOD_STATS_EVENT_STRING_MAX) /* :317 */
copy_length = MOD_STATS_EVENT_STRING_MAX;
memcpy(&events[index].event_string, event_string, copy_length);
events[index].event_string[copy_length - 1] = '\0'; /* :321 β OOB at [-1] when length==0 */
length == 0 β copy_length - 1 wraps to 0xFFFFFFFF (unsigned) β the
compiler itself proves it: building the exact clone with gcc 8.3 on the guest
emits warning: array subscript 4294967295 is above array bounds of
'char[512]' [-Warray-bounds] (run.log). At runtime the write lands at
event_string[-1] = offset 3 of struct stats_event_cache = the MSB of
entry_id on little-endian β and stats.c:323 then assigns entry_id
wholesale, clobbering the OOB byte. Net memory effect: zero. No caller exists
to pass length == 0 regardless.
Other stats.c candidates checked and killed
mod_stats_reset_data(:283-287):memset(core_stats->time, 0, sizeof * 0)when disabled βmemset(NULL, 0, 0), benign in the kernel; entries/event bounds in all four update functions are correctly guarded (:310,:340,:385,:433);v_sync_time_in_us[num_vsyncs]bounded by the< MOD_STATS_NUM_VSYNCScheck (:392);time[index - 1]safe because index starts at 1 (:166); concurrent update threads can at worst double-increment the index pastentriesbut every write is re-guarded, so no OOB;mod_stats_dumpguards both array walks (:235,:239).kcalloc(entries β€ 0x01000000, ~120B)β 2 GB max on crafted persistent data β allocation failure is handled (:148,:156), anddm_read_persistent_datahas no live backing store for this key on DF.
Fix
Delete the module (matches upstream direction; it was never wired into the
amdgpu driver) or apply fix.diff (guard length == 0, clamp to
MOD_STATS_EVENT_STRING_MAX - 1).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableNot applicable: the defective code is not compiled into any kernel or module build; there is no baseline to patch-and-compare.
sys/dev/drm/amd/amdgpu/Makefile:49-51 (no stats entry); fix.diff provided for the delete-later alternative.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (guest: Makefile lists freesync/color/info_packet only; -Warray-bounds warning 'array subscript 4294967295'; demo output 'PROVEN: write landed at event_string[-1]'), VERDICT.md (killed-candidates list), fix.diff.
PoC changes
No seeded PoC; wrote stats_len0_demo.c (byte-exact clone of the kernel logic); first build failed on missing
Verified recommended fix
Delete modules/stats (never wired into the amdgpu build) or guard length==0 and clamp copy_length to MOD_STATS_EVENT_STRING_MAX-1 (fix.diff).
Verdict
sys/dev/drm/amd/display/modules/stats/stats.c is dead code: sys/dev/drm/amd/amdgpu/Makefile:49-51 builds modules/{freesync,color,info_packet} but not modules/stats, and no file in sys/ calls any mod_stats_* function (verified on the guest's /usr/src too). Inside the never-compiled module, mod_stats_update_event has a latent off-by-one: with length==0, copy_length-1 wraps unsigned and event_string[-1]='\0' writes one byte before the 512-byte array (into entry_id's MSB; stats.c:323 then rewrites entry_id wholesale, so net corruption is zero). gcc 8.3's -Warray-bounds flags the exact expression when the clone is built (run.log); the standalone demo shows the write lands at offset 3 and is clobbered. Info severity: recommend deleting the module or applying fix.diff.
No comments yet.