All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Emil Tsalapatis" <emil@etsalapatis.com>
Cc: bpf@vger.kernel.org
Subject: Re: [RESEND PATCH bpf-next 1/2] selftests/bpf: libarena: Add rbtree data structure
Date: Wed, 13 May 2026 00:42:16 +0000	[thread overview]
Message-ID: <20260513004217.49B44C2BCB0@smtp.kernel.org> (raw)
In-Reply-To: <20260511214100.9487-2-emil@etsalapatis.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Inserting duplicate keys into an `RB_DUPLICATE` rbtree overwrites existing subtrees, causing data loss and memory leaks.
- [High] The lock-free node freelist implementation is vulnerable to the ABA problem, leading to tree corruption.
--

commit 1c2293fc665383e2481f9d7ee15173fb46b0ea76
Author: Emil Tsalapatis <emil@etsalapatis.com>

selftests/bpf: libarena: Add rbtree data structure

This commit adds a native red-black tree data structure to libarena. It
supports multiple APIs (key-value based, node based) and uses the libarena
memory allocator to manage its data.

> diff --git a/tools/testing/selftests/bpf/libarena/src/rbtree.bpf.c b/tools/testing/selftests/bpf/libarena/src/rbtree.bpf.c
> new file mode 100644
> index 0000000000000..2ce2dfe5a2235
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/libarena/src/rbtree.bpf.c
> @@ -0,0 +1,1032 @@
[ ... ]
> +static inline rbnode_t *rb_node_alloc_common(rbtree_t *rbtree __arg_arena, u64 key, u64 value)
> +{
> +	rbnode_t *rbnode = NULL;
> +
> +	if (unlikely(!rbtree))
> +		return NULL;
> +
> +	/* Use the built-in node cache for the rbtree to avoid going to the allocator. */
> +	do  {
> +		rbnode = rbtree->freelist;
> +		if (!rbnode)
> +			break;
> +
> +	} while (cmpxchg(&rbtree->freelist, rbnode, rbnode->parent) != rbnode && can_loop);

Could this lock-free freelist implementation be vulnerable to the ABA problem
in concurrent environments?

If Thread A reads rbtree->freelist and evaluates rbnode->parent, and is then
delayed, Thread B could allocate rbnode, allocate its parent, and then free
rbnode back to the freelist. When Thread A resumes, the cmpxchg would
succeed because the freelist head is still rbnode, but it would erroneously
set the freelist head to the stale rbnode->parent. This could cause the
allocator to hand out an in-use tree node, leading to multiple concurrent
uses of the same memory and tree corruption.

[ ... ]

> +static
> +int rb_node_insert(rbtree_t *rbtree __arg_arena, rbnode_t *node __arg_arena)
> +{
> +	rbnode_t *grandparent, *parent = rbtree->root;
> +	enum rbnode_find_mode mode;
> +	u64 key = node->key;
> +	rbnode_t *uncle;
> +	int dir;
> +	int ret;
> +
> +	if (unlikely(!rbtree))
> +		return -EINVAL;
> +
> +	if (!parent) {
> +		rbtree->root = node;
> +		return 0;
> +	}
> +
> +	mode = RBN_FIND_DFL;
> +	/* Always find the first node with the key when allowing duplicates. */
> +	if (rbtree->insert == RB_DUPLICATE)
> +		mode = RBN_FIND_LUB;
> +	parent = rbnode_find(parent, key, mode);
> +
> +	if (key == parent->key && rbtree->insert != RB_DUPLICATE) {
> +		if (rbtree->insert == RB_UPDATE) {
> +			/*
> +			 * Replace the old node with the new one.
> +			 * Free up the old node.
> +			 */
> +			ret = rbnode_replace(rbtree, parent, node);
> +			if (ret)
> +				return ret;
> +
> +			if (rbtree->alloc == RB_ALLOC)
> +				rb_node_free(rbtree, parent);
> +
> +			return 0;
> +		}
> +
> +		/* Otherwise it's RB_DEFAULT. */
> +		return -EALREADY;
> +	}
> +
> +	node->parent = parent;
> +	/* Also works if key == parent->key. */
> +	if (key <= parent->key)
> +		parent->left = node;
> +	else
> +		parent->right = node;

Does this code overwrite existing subtrees when inserting duplicate keys
into an RB_DUPLICATE rbtree?

When rbnode_find() is called with RBN_FIND_LUB, it can break early when it
finds the target key whose child in the search direction has a different
key:

tools/testing/selftests/bpf/libarena/src/rbtree.bpf.c:rbnode_find() {
    ...
		/*
		 * For least upper bound lookups, ensure that we
		 * stop searching once we find the rightmost key.
		 */
		if (node->key == key && node->child[dir]->key != key)
			break;
    ...
}

This causes it to return an internal node where node->child[dir] is
explicitly not NULL. Back in rb_node_insert(), the new node is
unconditionally assigned to parent->left or parent->right. This blindly
overwrites the existing child pointer, permanently detaching and leaking
the previous subtree.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260511214100.9487-1-emil@etsalapatis.com?part=1

  parent reply	other threads:[~2026-05-13  0:42 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-11 21:40 [RESEND PATCH bpf-next 0/2] selftests/bpf: libarena: Add initial data structures Emil Tsalapatis
2026-05-11 21:40 ` [RESEND PATCH bpf-next 1/2] selftests/bpf: libarena: Add rbtree data structure Emil Tsalapatis
2026-05-11 22:24   ` bot+bpf-ci
2026-05-12 20:18     ` Emil Tsalapatis
2026-05-13  0:42   ` sashiko-bot [this message]
2026-05-15  0:08   ` Alexei Starovoitov
2026-05-11 21:41 ` [RESEND PATCH bpf-next 2/2] selftests/bpf: libarena: Add Lev-Chase queue " Emil Tsalapatis
2026-05-11 22:12   ` bot+bpf-ci
2026-05-12 20:20     ` Emil Tsalapatis
2026-05-13  2:23   ` sashiko-bot
2026-05-15  0:11   ` Alexei Starovoitov
2026-05-15  0:13   ` Alexei Starovoitov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260513004217.49B44C2BCB0@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=emil@etsalapatis.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.