https://bugzilla.kernel.org/show_bug.cgi?id=220884
Bug ID: 220884
Summary: Potential NULL pointer dereference in
hmat_get_extended_linear_cache_size due to
uninitialized memregions member
Product: ACPI
Version: 2.5
Hardware: All
OS: Linux
Status: NEW
Severity: normal
Priority: P3
Component: Other
Assignee: [email protected]
Reporter: [email protected]
Regression: No
**Title**: Potential NULL pointer dereference in
`hmat_get_extended_linear_cache_size` due to uninitialized `memregions` member
**Bugzilla Entry ID**: (to be assigned)
**Product**: Linux Kernel
**Component**: ACPI / NUMA / HMAT
**Version**: upstream (torvalds/linux.git, master branch)
**Hardware**: x86_64, AArch64 (systems with HMAT support)
**Status**: NEW
**Severity**: medium
**Priority**: medium
**URL**: https://github.com/torvalds/linux/blob/master/drivers/acpi/numa/hmat.c
**CC**: [email protected], [email protected]
---
### **Description**
In function `hmat_get_extended_linear_cache_size()`, the code dereferences
`&target->memregions` without ensuring that the `memregions` structure member
has been properly initialized. While this is not a traditional NULL pointer
dereference (since `memregions` is a struct member, not a pointer), the
uninitialized `struct resource` can contain garbage values, leading to
unpredictable behavior in `resource_contains()`.
**Vulnerable Code Path**:
```c
res = &target->memregions;
if (!resource_contains(res, backing_res))
continue;
```
The function `resource_contains()` directly accesses `res->start` and
`res->end`, which may be uninitialized stack or heap garbage. This can cause:
- Incorrect evaluation of the `if` condition
- Use of uninitialized values in comparisons
- Potential information leak (kernel memory disclosure)
- Kernel instability or oops if `resource_contains` traverses linked-list
pointers (parent/sibling/child) that are also uninitialized
**Impact**: A local user (with privileges to trigger HMAT operations) may cause
a kernel crash or obtain unintended memory information. The bug requires
HMAT-capable hardware (Intel Xeon Scalable, AMD EPYC with HMAT) and can be
triggered during memory hot-add or system boot with malformed/fuzzed ACPI
tables.
**Root Cause**: The `memory_target` structure's `memregions` member is not
guaranteed to be initialized before use in
`hmat_get_extended_linear_cache_size`. The initialization likely occurs in
`create_memory_target()` or similar, but there is no validation that this has
occurred before the cache query function runs.
---
### **Steps to Reproduce**
1. Boot a kernel with HMAT support on HMAT-capable hardware (or QEMU with HMAT
tables)
2. Use a kernel module or sysfs interface to trigger
`hmat_get_extended_linear_cache_size` before `memregions` is fully set up
(or simulate by injecting an ACPI table with a memory target that has no
valid region described)
3. Observe kernel warning/oops if `resource_contains` hits an invalid pointer,
or note incorrect cache size returned due to uninitialized bounds.
Alternatively, with CONFIG_KASAN enabled, the kernel may report an
"uninitialized value" warning.
---
### **Proposed Fix**
Option 1 – Validate before use in `hmat_get_extended_linear_cache_size`:
```diff
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -XXX, +XXX @@
+ /* Ensure memregions has been initialized */
+ if (resource_size(&target->memregions) == 0)
+ continue;
+
res = &target->memregions;
if (!resource_contains(res, backing_res))
continue;
```
Option 2 – Ensure proper initialization in `create_memory_target` (if that is
the constructor):
```diff
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -XXX, +XXX @@
target = kzalloc(sizeof(*target), GFP_KERNEL);
if (!target)
return NULL;
+ /* Explicitly initialize the resource */
+ memset(&target->memregions, 0, sizeof(target->memregions));
+ target->memregions.start = 0;
+ target->memregions.end = 0;
INIT_LIST_HEAD(&target->caches);
...
```
Option 3 – Both.
---
### **Code Analysis**
Relevant structures (inferred from context):
```c
struct memory_target {
...
struct resource memregions; // NOT a pointer, but embedded struct
struct list_head caches;
...
};
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
```
Since `memregions` is embedded, `&target->memregions` is always a valid address
(offset inside `target`). The bug is that its fields contain undefined values.
Function `resource_contains` (simplified):
```c
bool resource_contains(struct resource *res1, struct resource *res2)
{
return res1->start <= res2->start && res1->end >= res2->end;
}
```
If `start`/`end` are uninitialized, the comparison is made with garbage,
possibly leading to incorrect logic flow.
---
### **Additional Notes**
- This is not a NULL dereference; it is an **uninitialized variable** bug.
- The bug is more subtle and may not crash unless the garbage values point to
invalid memory (e.g., if `resource_contains` is extended to follow
`parent`/`child` pointers).
- The fix is low-risk and defensive.
- Similar issues have been fixed in the resource subsystem before (e.g., commit
fffa9bb1b).
---
### **Regression Potential**
Low. The fix adds a safety check that should not affect correctly initialized
targets, and ensures buggy or maliciously crafted ACPI tables do not cause
undefined behavior.
---
### **Tested/Verified**
(To be filled by maintainer/reporter)
- [ ] Boot test on HMAT-capable system
- [ ] With CONFIG_KASAN, CONFIG_UBSAN
- [ ] Memory hotplug test cycle
---
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
_______________________________________________
acpi-bugzilla mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/acpi-bugzilla