* [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