CVE-2026-46020
Description
In the Linux kernel, the following vulnerability has been resolved:
mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
Patch series "mm/damon/core: validate damos_quota_goal->nid".
node_mem[cg]_{used,free}_bp DAMOS quota goals receive the node id. The node id is used for si_meminfo_node() and NODE_DATA() without proper validation. As a result, privileged users can trigger an out of bounds memory access using DAMON_SYSFS. Fix the issues.
The issue was originally reported [1] with a fix by another author. The original author announced [2] that they will stop working including the fix that was still in the review stage. Hence I'm restarting this.
This patch (of 2):
Users can set damos_quota_goal->nid with arbitrary value for node_mem_{used,free}_bp. But DAMON core is using those for si_meminfo_node() without the validation of the value. This can result in out of bounds memory access. The issue can actually triggered using DAMON user-space tool (damo), like below.
$ sudo ./damo start --damos_action stat \ --damos_quota_goal node_mem_used_bp 50% -1 \ --damos_quota_interval 1s $ sudo dmesg [...] [ 65.565986] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000098
Fix this issue by adding the validation of the given node. If an invalid node id is given, it returns 0% for used memory ratio, and 100% for free memory ratio.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Missing node ID validation in DAMON quota goals allows privileged users to trigger out-of-bounds memory access in the Linux kernel.
Vulnerability
The Linux kernel's DAMON (Data Access Monitoring) subsystem in mm/damon/core.c lacks validation of the nid field in damos_quota_goal when using node_mem_used_bp or node_mem_free_bp quota goals. The value is used in si_meminfo_node() without checking if it is a valid node ID, leading to a potential out-of-bounds memory access. This affects all kernel versions with DAMON support (likely since introduction). The issue can be triggered via DAMON sysfs interface by a privileged user. [1]
Exploitation
An attacker with local root or CAP_SYS_ADMIN privileges (since DAMON sysfs access requires privileges) can craft a damos_quota_goal entry with an invalid node ID (e.g., -1) using the damo tool or direct sysfs manipulation. This causes the kernel to use the invalid node ID in functions like si_meminfo_node() or NODE_DATA(), resulting in a NULL pointer dereference or out-of-bounds read/write. No user interaction beyond setting the configuration is required. [1]
Impact
A local privileged attacker can cause a kernel crash (denial of service) or potentially leak sensitive kernel memory (information disclosure) due to the out-of-bounds access. The severity is high as it allows a local user with root or CAP_SYS_ADMIN to crash the system or gain unintended access to kernel memory. The fix ensures that invalid node IDs return safe default values (0% for used memory, 100% for free memory). [1]
Mitigation
The fix is included in the Linux kernel stable commit b09958e235f2b9cd3898b85a8529172afa80d212 [1]. Users should update to a kernel version containing this commit. As a workaround, restrict access to DAMON sysfs to trusted users only, or disable DAMON if not required. No known CVE listing in CISA KEV. [1]
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
6b09958e235f2mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index 3fb199a47fa9e9..3cda8682d76b9a 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2038,12 +2038,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
bcad74078708mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index 3c114b81f36d51..1a447dd0c05338 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2078,12 +2078,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
40250b2dded0mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index ddabb93f23773e..9a848d7647ef9e 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2217,12 +2217,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
40250b2dded0mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index ddabb93f23773e..9a848d7647ef9e 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2217,12 +2217,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
bcad74078708mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index 3c114b81f36d51..1a447dd0c05338 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2078,12 +2078,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
b09958e235f2mm/damon/core: validate damos_quota_goal->nid for node_mem_{used,free}_bp
1 file changed · +12 −1
mm/damon/core.c+12 −1 modifieddiff --git a/mm/damon/core.c b/mm/damon/core.c index 3fb199a47fa9e9..3cda8682d76b9a 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2038,12 +2038,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing validation of the user-supplied NUMA node ID before passing it to si_meminfo_node() allows out-of-bounds memory access."
Attack vector
A privileged user (CAP_SYS_ADMIN or root) can trigger an out-of-bounds memory access by setting an invalid NUMA node ID via DAMON_SYSFS or the `damo` user-space tool. For example, passing `-1` as the node ID for a `node_mem_used_bp` quota goal causes `si_meminfo_node()` to dereference a NULL pointer, leading to a kernel crash [patch_id=2660387]. The attacker only needs to configure a DAMON scheme with a quota goal using an arbitrary node ID value.
Affected code
The vulnerability resides in `mm/damon/core.c` in the function `damos_get_node_mem_bp()`. This function passes `goal->nid` directly to `si_meminfo_node()` without any validation [patch_id=2660387]. The `damos_quota_goal->nid` field is user-controllable via DAMON_SYSFS for the `node_mem_{used,free}_bp` quota goal metrics.
What the fix does
The patch adds a new helper function `invalid_mem_node()` that checks three conditions: `nid < 0`, `nid >= MAX_NUMNODES`, and `!node_state(nid, N_MEMORY)` [patch_id=2660387]. Before calling `si_meminfo_node()`, the code now calls this validation check. If the node is invalid, it returns a safe default value — 0 for used-memory ratio and 10000 (100%) for free-memory ratio — instead of proceeding to the vulnerable memory access [patch_id=2660387].
Preconditions
- authAttacker must have CAP_SYS_ADMIN or root privileges to configure DAMON via DAMON_SYSFS or the damo tool
- configKernel must be built with CONFIG_NUMA enabled
- inputAttacker must set an invalid node ID (e.g., -1 or a value >= MAX_NUMNODES or a node without N_MEMORY state) for a node_mem_{used,free}_bp quota goal
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.