[PATCH bpf-next v7 2/2] bpf, arena: fix range_tree_set inconsistency on kmalloc_nolock failure

From: chenyuan_fl

Date: Wed Sep 23 2026 - 05:10:28 EST


From: Yuan Chen <chenyuan@xxxxxxxxxx>

range_tree_set() pre-allocates the node needed for a brand-new range
before calling range_tree_clear(), so an allocation failure returns
-ENOMEM without having modified the tree (previously the overlapping
nodes were already removed by range_tree_clear() before the allocation
was attempted, permanently losing the cleared sub-ranges).

The adjacency lookups done before the clear already return the nodes
that the merge below needs: range_tree_clear() frees only the nodes
that lie entirely inside [start, last], trims the overlapping ones in
place, and the split case cannot happen because of the 'already set'
check above. Do the lookup once and drop the unreachable -EFAULT
checks.

Signed-off-by: Yuan Chen <chenyuan@xxxxxxxxxx>
---
kernel/bpf/range_tree.c | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/kernel/bpf/range_tree.c b/kernel/bpf/range_tree.c
index 15b588377a76..2460e78d6a55 100644
--- a/kernel/bpf/range_tree.c
+++ b/kernel/bpf/range_tree.c
@@ -199,6 +199,7 @@ int is_range_tree_set(struct range_tree *rt, u32 start, u32 len)
int range_tree_set(struct range_tree *rt, u32 start, u32 len)
{
u32 last = start + len - 1;
+ struct range_node *new_rn = NULL;
struct range_node *right;
struct range_node *left;
int err;
@@ -208,20 +209,19 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
if (left && left->rn_start <= start && left->rn_last >= last)
return 0;

+ left = range_it_iter_first(rt, start - 1, start - 1);
+ right = range_it_iter_first(rt, last + 1, last + 1);
+ if (!left && !right) {
+ new_rn = kmalloc_nolock(sizeof(struct range_node),
+ __GFP_ACCOUNT, NUMA_NO_NODE);
+ if (!new_rn)
+ return -ENOMEM;
+ }
+
/* Clear out everything in the range we want to set. */
err = range_tree_clear(rt, start, len);
if (err)
- return err;
-
- /* Do we have a left-adjacent range ? */
- left = range_it_iter_first(rt, start - 1, start - 1);
- if (left && left->rn_last + 1 != start)
- return -EFAULT;
-
- /* Do we have a right-adjacent range ? */
- right = range_it_iter_first(rt, last + 1, last + 1);
- if (right && right->rn_start != last + 1)
- return -EFAULT;
+ goto out_free_new;

if (left && right) {
/* Combine left and right adjacent ranges */
@@ -241,14 +241,16 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
right->rn_start = start;
range_it_insert(right, rt);
} else {
- left = kmalloc_nolock(sizeof(struct range_node), __GFP_ACCOUNT, NUMA_NO_NODE);
- if (!left)
- return -ENOMEM;
- left->rn_start = start;
- left->rn_last = last;
- range_it_insert(left, rt);
+ /* No adjacent ranges; use the pre-allocated node */
+ new_rn->rn_start = start;
+ new_rn->rn_last = last;
+ range_it_insert(new_rn, rt);
}
return 0;
+
+out_free_new:
+ kfree_nolock(new_rn);
+ return err;
}

void range_tree_destroy(struct range_tree *rt)
--
2.54.0