public inbox for io-uring@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] io_uring/rsrc: use refcount_t for io_rsrc_node.refs
@ 2026-05-13 23:15 Oleg Sevostyanov
  2026-05-13 23:55 ` Jens Axboe
  0 siblings, 1 reply; 2+ messages in thread
From: Oleg Sevostyanov @ 2026-05-13 23:15 UTC (permalink / raw)
  To: io-uring; +Cc: Jens Axboe, Pavel Begunkov


[-- Attachment #1.1: Type: text/plain, Size: 1946 bytes --]

Hello,

This patch converts the refs field in struct io_rsrc_node from plain
int to refcount_t.

Background
----------
During a static analysis pass of io_uring/rsrc.{c,h} I examined all
sites that touch io_rsrc_node.refs:

  - io_rsrc_node_alloc()     rsrc.c:147   initialises to 1
  - io_buf_node_lookup()     rsrc.c:1117  refs++ under io_ring_submit_lock
  - io_clone_buffers() x2    rsrc.c:1199  refs++ under uring_lock
(lockdep_assert_held
                             rsrc.c:1232  asserted on both ctx's)
  - io_put_rsrc_node()       rsrc.h:107   --refs under uring_lock

All four sites are correctly guarded by ctx->uring_lock, so there is no
present race or overflow risk.  This is a defence-in-depth change only.

Rationale
---------
io_mapped_ubuf (defined in the same header, rsrc.h:40) already uses
refcount_t for its own refs field.  Aligning io_rsrc_node to the same
convention:

  1. Gives lockless overflow/underflow detection "for free" on kernels
     built with REFCOUNT_FULL or on architectures that provide
     REFCOUNT_ARCH_OPTIMIZED (x86 since 4.14).

  2. Makes it harder for a future patch that removes or relaxes locking
     to silently introduce a refcount bug—the saturating behaviour of
     refcount_t would catch wraps and emit a WARN_ONCE before a
     use-after-free could occur.

  3. Self-documents the intent: the field is a reference counter, not an
     arbitrary signed integer.

The change is mechanical:

  int refs = 1           -> refcount_set(&node->refs, 1)
  node->refs++           -> refcount_inc(&node->refs)
  if (!--node->refs)     -> if (refcount_dec_and_test(&node->refs))

No functional change is intended.  I do not have a stable kernel build
environment that includes the full io_uring tree, so I am unable to
provide a Tested-by, but the patch compiles cleanly against the 6.8
source tree (io_uring/ sparse checkout).

Oleg Sevostyanov

[-- Attachment #1.2: Type: text/html, Size: 2187 bytes --]

[-- Attachment #2: 0001-io_uring-rsrc-use-refcount_t-for-io_rsrc_node.refs.patch --]
[-- Type: application/octet-stream, Size: 3023 bytes --]

From: Oleg Sevostyanov 
Date: Thu, 14 May 2026 00:00:00 +0000
Subject: [PATCH] io_uring/rsrc: use refcount_t for io_rsrc_node.refs

The refs field in struct io_rsrc_node is a plain int protected by
ctx->uring_lock. While the current locking is correct and prevents
concurrent access, using a plain int means the kernel's refcount
overflow/underflow detection (REFCOUNT_FULL or REFCOUNT_ARCH_OPTIMIZED)
is bypassed.

If a future change accidentally removes the lock or introduces a new
path that bumps refs without holding uring_lock, a plain int would
silently wrap. refcount_t would catch this via WARN_ONCE and saturate
at 0 or INT_MAX, preventing use-after-free in the node.

io_mapped_ubuf in the same header already uses refcount_t for its refs
field; align io_rsrc_node with the same convention.

No functional change intended.

Signed-off-by: Oleg Sevostyanov 
---
 io_uring/rsrc.c |  8 ++++----
 io_uring/rsrc.h |  4 +++-
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index xxxxxxx..xxxxxxx 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -144,7 +144,7 @@ struct io_rsrc_node *io_rsrc_node_alloc(struct io_ring_ctx *ctx, int type)
 	node = io_cache_alloc(&ctx->node_cache, GFP_KERNEL);
 	if (node) {
 		node->type = type;
-		node->refs = 1;
+		refcount_set(&node->refs, 1);
 		node->tag = 0;
 		node->file_ptr = 0;
 	}
@@ -1114,7 +1114,7 @@ struct io_rsrc_node *io_buf_node_lookup(struct io_uring_buf_node_req *req,
 	io_ring_submit_lock(ctx, issue_flags);
 	node = io_rsrc_node_lookup(&ctx->buf_table, req->buf_index);
 	if (node) {
-		node->refs++;
+		refcount_inc(&node->refs);
 		req->buf_node = node;
 		io_ring_submit_unlock(ctx, issue_flags);
 		return node;
@@ -1196,7 +1196,7 @@ int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx,
 	for (i = 0; i < min(arg->dst_off, ctx->buf_table.nr); i++) {
 		struct io_rsrc_node *node = ctx->buf_table.nodes[i];

 		if (node) {
 			data.nodes[i] = node;
-			node->refs++;
+			refcount_inc(&node->refs);
 		}
 	}
@@ -1229,7 +1229,7 @@ int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx,
 	for (i = nbufs; i < ctx->buf_table.nr; i++) {
 		struct io_rsrc_node *node = ctx->buf_table.nodes[i];

 		if (node) {
 			data.nodes[i] = node;
-			node->refs++;
+			refcount_inc(&node->refs);
 		}
 	}

diff --git a/io_uring/rsrc.h b/io_uring/rsrc.h
index xxxxxxx..xxxxxxx 100644
--- a/io_uring/rsrc.h
+++ b/io_uring/rsrc.h
@@ -2,6 +2,8 @@
 #ifndef IOU_RSRC_H
 #define IOU_RSRC_H

+#include <linux/refcount.h>
+
 #include <linux/io_uring_types.h>
 #include <linux/lockdep.h>

@@ -14,7 +16,7 @@ enum {
 struct io_rsrc_node {
 	unsigned char			type;
-	int				refs;
+	refcount_t			refs;

 	u64 tag;
 	union {
@@ -104,7 +106,7 @@ static inline void io_put_rsrc_node(struct io_ring_ctx *ctx,
 				    struct io_rsrc_node *node)
 {
 	lockdep_assert_held(&ctx->uring_lock);
-	if (!--node->refs)
+	if (refcount_dec_and_test(&node->refs))
 		io_free_rsrc_node(ctx, node);
 }
--
2.44.0

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] io_uring/rsrc: use refcount_t for io_rsrc_node.refs
  2026-05-13 23:15 [PATCH] io_uring/rsrc: use refcount_t for io_rsrc_node.refs Oleg Sevostyanov
@ 2026-05-13 23:55 ` Jens Axboe
  0 siblings, 0 replies; 2+ messages in thread
From: Jens Axboe @ 2026-05-13 23:55 UTC (permalink / raw)
  To: Oleg Sevostyanov, io-uring; +Cc: Pavel Begunkov

On 5/13/26 5:15 PM, Oleg Sevostyanov wrote:
> Hello,
> 
> This patch converts the refs field in struct io_rsrc_node from plain
> int to refcount_t.
> 
> Background
> ----------
> During a static analysis pass of io_uring/rsrc.{c,h} I examined all
> sites that touch io_rsrc_node.refs:
> 
>   - io_rsrc_node_alloc()     rsrc.c:147   initialises to 1
>   - io_buf_node_lookup()     rsrc.c:1117  refs++ under io_ring_submit_lock
>   - io_clone_buffers() x2    rsrc.c:1199  refs++ under uring_lock (lockdep_assert_held
>                              rsrc.c:1232  asserted on both ctx's)
>   - io_put_rsrc_node()       rsrc.h:107   --refs under uring_lock
> 
> All four sites are correctly guarded by ctx->uring_lock, so there is no
> present race or overflow risk.  This is a defence-in-depth change only.
> 
> Rationale
> ---------
> io_mapped_ubuf (defined in the same header, rsrc.h:40) already uses
> refcount_t for its own refs field.  Aligning io_rsrc_node to the same
> convention:

Because those can be shared across rings (cloning buffers), hence we
cannot rely on the ring lock for that.

>   1. Gives lockless overflow/underflow detection "for free" on kernels
>      built with REFCOUNT_FULL or on architectures that provide
>      REFCOUNT_ARCH_OPTIMIZED (x86 since 4.14).

It's certainly not "for free".

>   2. Makes it harder for a future patch that removes or relaxes locking
>      to silently introduce a refcount bug?the saturating behaviour of
>      refcount_t would catch wraps and emit a WARN_ONCE before a
>      use-after-free could occur.

You could just add a lockdep assert for that.

>   3. Self-documents the intent: the field is a reference counter, not an
>      arbitrary signed integer.

I mean, it's named ->refs, you'd think that'd make it clear enough.

> No functional change is intended.  I do not have a stable kernel build
> environment that includes the full io_uring tree, so I am unable to
> provide a Tested-by, but the patch compiles cleanly against the 6.8
> source tree (io_uring/ sparse checkout).

So in other words, you didn't even test this? And it's against an
ancient kernel?

None of that matters though, as that's a hard no on this patch.

-- 
Jens Axboe

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-13 23:55 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-13 23:15 [PATCH] io_uring/rsrc: use refcount_t for io_rsrc_node.refs Oleg Sevostyanov
2026-05-13 23:55 ` Jens Axboe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox