Skip to contentSkip to conversation

mm/damon/core: validate goal nid before accessing node data

PATCH In review Josh Law 2 participants 4 messages Mar 25 – Mar 26

Subsystems (3)
lore
Share & tools
lore

Josh Law

author
#0 lore raw

PATCH v2 1/1 mm/damon/core: validate goal nid before accessing node data

damos_get_node_mem_bp() and damos_get_node_memcg_used_bp() pass goal->nid directly to si_meminfo_node() and NODE_DATA() without checking that it refers to a valid, online NUMA node. Since goal->nid is set from userspace via sysfs with no validation, a negative or out-of-range value causes an out-of-bounds access in NODE_DATA(), and a valid but offline node gives undefined results.

Add bounds and node_state(N_MEMORY) checks before using the nid, consistent with damon_migrate_pages().

Fixes 0e1c773b501f ("mm/damon/core: introduce damos quota goal metrics for memory node utilization")
---
 mm/damon/core.c | 8 ++++++++
 1 file changed, 8 insertions(+)
mm/damon/core.c +8 -0
index 59b709f04975..112125b635d7 100644
@@ -2227,6 +2227,10 @@ static __kernel_ulong_t damos_get_node_mem_bp(
 	struct sysinfo i;
 	__kernel_ulong_t numerator;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+
 	si_meminfo_node(&i, goal->nid);
 	if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
 		numerator = i.totalram - i.freeram;
@@ -2243,6 +2247,10 @@ static unsigned long damos_get_node_memcg_used_bp(
 	unsigned long used_pages, numerator;
 	struct sysinfo i;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+
 	memcg = mem_cgroup_get_from_id(goal->memcg_id);
 	if (!memcg) {
 		if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
Signature · 1 line
2.34.1
maintainer
#1 lore raw

Re: (sashiko review) [PATCH v2] mm/damon/core: validate goal nid before accessing node data

Forwarding Sashiko review for doing discussions via mails.

# review url: https://sashiko.dev/#/patchset/[email protected]
# start of sashiko.dev inline review
commit 24e25058b8f508495d0b3db33bed16564df7fed4
Author: Josh Law <[email protected]>
Subject: mm/damon/core: validate goal nid before accessing node data

This patch adds bounds and node_state(N_MEMORY) checks before using goal->nid from userspace to prevent out-of-bounds access in NODE_DATA().

mm/damon/core.c static __kernel_ulong_t damos_get_node_mem_bp( · line 2251

Quoted 12 lines
mm/damon/core.c +4 -0
index db6c67e52d2b..ed45a7443abe 100644
@@ -2245,6 +2245,10 @@ static __kernel_ulong_t damos_get_node_mem_bp(
 	struct sysinfo i;
 	__kernel_ulong_t numerator;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+

When goal->nid is invalid or offline, this unconditionally returns 0. For the DAMOS_QUOTA_NODE_MEM_FREE_BP metric, wouldn't returning 0 imply 0% free memory?

Could this cause the DAMON feedback loop to interpret the goal as severely under-achieved and drastically increase the scheme's aggressiveness?

Should this fallback logic check the metric type and return 10000 (100% free) for FREE_BP metrics to fail gracefully, mirroring the existing fallback logic used when a memory cgroup lookup fails?

mm/damon/core.c

Quoted 6 lines
mm/damon/core.c +0 -0
 	si_meminfo_node(&i, goal->nid);
 	if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
 		numerator = i.totalram - i.freeram;
mm/damon/core.c +0 -0
 	else	/* DAMOS_QUOTA_NODE_MEM_FREE_BP */
 		numerator = i.freeram;
 	return mult_frac(numerator, 10000, i.totalram);

While not introduced by this patch, since this function is being modified to handle node edge cases, is it possible for a node to have N_MEMORY set, but have 0 managed pages? For example, if all of its memory is reserved for hugetlb or unmanaged ZONE_DEVICE memory.

If so, si_meminfo_node() would set i.totalram to 0. Would the call to mult_frac() then cause a divide-by-zero panic when it evaluates x_ / d_ ?

mm/damon/core.c static unsigned long damos_get_node_memcg_used_bp( · line 2271

Quoted 8 lines
mm/damon/core.c +4 -0
@@ -2261,6 +2265,10 @@ static unsigned long damos_get_node_memcg_used_bp(
 	unsigned long used_pages, numerator;
 	struct sysinfo i;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+

Similar to the function above, returning 0 unconditionally here for DAMOS_QUOTA_NODE_MEMCG_FREE_BP might artificially trigger maximum reclaim aggressiveness.

# end of sashiko.dev inline review
# review url: https://sashiko.dev/#/patchset/[email protected]
#
# hkml [1] generated a draft of this mail.  It can be regenerated
# using below command:
#
#     hkml patch sashiko_dev --for_forwarding \
#             [email protected]
#
# [1] https://github.com/sjp38/hackermail

Sent using hkml (https://github.com/sjp38/hackermail)

maintainer
#2 lore raw

Re: (sashiko review) [PATCH v2] mm/damon/core: validate goal nid before accessing node data

Quoted 30 lines · On Wed, 25 Mar 2026 17:10:50 -0700 SeongJae Park <[email protected]> wrote:

Forwarding Sashiko review for doing discussions via mails.

# review url: https://sashiko.dev/#/patchset/[email protected]
# start of sashiko.dev inline review
commit 24e25058b8f508495d0b3db33bed16564df7fed4
Author: Josh Law <[email protected]>
Subject: mm/damon/core: validate goal nid before accessing node data

This patch adds bounds and node_state(N_MEMORY) checks before using goal->nid from userspace to prevent out-of-bounds access in NODE_DATA().

mm/damon/core.c static __kernel_ulong_t damos_get_node_mem_bp( · line 2251

Quoted 12 lines
mm/damon/core.c +4 -0
index db6c67e52d2b..ed45a7443abe 100644
@@ -2245,6 +2245,10 @@ static __kernel_ulong_t damos_get_node_mem_bp(
 	struct sysinfo i;
 	__kernel_ulong_t numerator;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+

When goal->nid is invalid or offline, this unconditionally returns 0. For the DAMOS_QUOTA_NODE_MEM_FREE_BP metric, wouldn't returning 0 imply 0% free memory?

Quoted 7 lines

Could this cause the DAMON feedback loop to interpret the goal as severely under-achieved and drastically increase the scheme's aggressiveness?

Should this fallback logic check the metric type and return 10000 (100% free) for FREE_BP metrics to fail gracefully, mirroring the existing fallback logic used when a memory cgroup lookup fails?

I am also thinking similar to sashiko. Could you please make the change?

Quoted 8 lines

mm/damon/core.c

Quoted 7 lines
mm/damon/core.c +0 -0
 	si_meminfo_node(&i, goal->nid);
 	if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
 		numerator = i.totalram - i.freeram;
mm/damon/core.c +0 -0
 	else	/* DAMOS_QUOTA_NODE_MEM_FREE_BP */
 		numerator = i.freeram;
 	return mult_frac(numerator, 10000, i.totalram);
Quoted 4 lines

While not introduced by this patch, since this function is being modified to handle node edge cases, is it possible for a node to have N_MEMORY set, but have 0 managed pages? For example, if all of its memory is reserved for hugetlb or unmanaged ZONE_DEVICE memory.

I'm not very sure if this is really possible. Josh, do you know?

Quoted 4 lines

If so, si_meminfo_node() would set i.totalram to 0. Would the call to mult_frac() then cause a divide-by-zero panic when it evaluates x_ / d_ ?

If sashiko's theory is true, I think we should add a zero value check here?

Quoted 5 lines
Quoted 5 lines
mm/damon/core.c +0 -0
@@ -2261,6 +2265,10 @@ static unsigned long damos_get_node_memcg_used_bp(
 	unsigned long used_pages, numerator;
 	struct sysinfo i;
 
Quoted 8 lines

mm/damon/core.c static __kernel_ulong_t damos_get_node_mem_bp( · line 2233

Quoted 4 lines
mm/damon/core.c +4 -0
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+

Similar to the function above, returning 0 unconditionally here for DAMOS_QUOTA_NODE_MEMCG_FREE_BP might artificially trigger maximum reclaim aggressiveness.

Similar to my above comment, I agree to sashiko.

Thanks,
SJ

[...]

maintainer
#3 lore raw
In reply to Josh Law

On Wed, 25 Mar 2026 15:52:21 +0000 Josh Law <[email protected]> wrote:

No rush, Josh. As I mentioned before, please give about a day after the last comment on the previous version of the patch, before posting a new version. That could help giving enough time for others to add their important findings.

Quoted 3 lines

damos_get_node_mem_bp() and damos_get_node_memcg_used_bp() pass goal->nid directly to si_meminfo_node() and NODE_DATA() without checking that it refers to a valid, online NUMA node. Since

s/online/memory/ ?

Quoted 11 lines

goal->nid is set from userspace via sysfs with no validation, a negative or out-of-range value causes an out-of-bounds access in NODE_DATA(), and a valid but offline node gives undefined results.

Add bounds and node_state(N_MEMORY) checks before using the nid, consistent with damon_migrate_pages().

Fixes 0e1c773b501f ("mm/damon/core: introduce damos quota goal metrics for memory node utilization")

---

As I also previously mentioned, please add changelog here.

Quoted 31 lines
 mm/damon/core.c | 8 ++++++++
 1 file changed, 8 insertions(+)
mm/damon/core.c +8 -0
index 59b709f04975..112125b635d7 100644
@@ -2227,6 +2227,10 @@ static __kernel_ulong_t damos_get_node_mem_bp(
 	struct sysinfo i;
 	__kernel_ulong_t numerator;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+
 	si_meminfo_node(&i, goal->nid);
 	if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
 		numerator = i.totalram - i.freeram;
@@ -2243,6 +2247,10 @@ static unsigned long damos_get_node_memcg_used_bp(
 	unsigned long used_pages, numerator;
 	struct sysinfo i;
 
+	if (goal->nid < 0 || goal->nid >= MAX_NUMNODES ||
+	    !node_state(goal->nid, N_MEMORY))
+		return 0;
+
 	memcg = mem_cgroup_get_from_id(goal->memcg_id);
 	if (!memcg) {
 		if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP)
Signature · 1 line
2.34.1

I will add more comments as a reply to sashiko comment.

Thanks,
SJ

[...]