2024-03-18 11:28:19

by Dmitry Mastykin

[permalink] [raw]
Subject: docs: bug in "Krefs and RCU" example

Hello all,
It seems there is a problem in "Krefs and RCU" example, that may cause
a crash.
I marked the place between two problem lines with "problem is here" comment.
If list_del_rcu() will be called between these lines, and list will become
empty, then q.next will not point to a valid struct my_data.
entry->refcount will also be invalid.
Instead, q.next must be read first, and then compared with q to check
list's emptiness (for example like in list_for_each_entry_rcu macro).

Signed-off-by: Dmitry Mastykin <[email protected]>

diff --git a/Documentation/core-api/kref.rst b/Documentation/core-api/kref.rst
index c61eea6f1bf2..b76d71d9c9a0 100644
--- a/Documentation/core-api/kref.rst
+++ b/Documentation/core-api/kref.rst
@@ -274,44 +274,45 @@ return value. If you are sure (by already having a valid pointer) that
kref_get_unless_zero() will return true, then use kref_get() instead.

Krefs and RCU
=============

The function kref_get_unless_zero also makes it possible to use rcu
locking for lookups in the above example::

struct my_data
{
struct rcu_head rhead;
.
struct kref refcount;
.
.
};

static struct my_data *get_entry_rcu()
{
struct my_data *entry = NULL;
rcu_read_lock();
if (!list_empty(&q)) {
+ // problem is here
entry = container_of(q.next, struct my_data, link);
if (!kref_get_unless_zero(&entry->refcount))
entry = NULL;
}
rcu_read_unlock();
return entry;
}

static void release_entry_rcu(struct kref *ref)
{
struct my_data *entry = container_of(ref, struct my_data, refcount);

mutex_lock(&mutex);
list_del_rcu(&entry->link);
mutex_unlock(&mutex);
kfree_rcu(entry, rhead);
}

static void put_entry(struct my_data *entry)
{
kref_put(&entry->refcount, release_entry_rcu);
}


2024-03-19 03:36:33

by Matthew Wilcox

[permalink] [raw]
Subject: Re: docs: bug in "Krefs and RCU" example

On Mon, Mar 18, 2024 at 02:27:44PM +0300, Dmitry Mastykin wrote:
> Hello all,
> It seems there is a problem in "Krefs and RCU" example, that may cause
> a crash.
> I marked the place between two problem lines with "problem is here" comment.
> If list_del_rcu() will be called between these lines, and list will become
> empty, then q.next will not point to a valid struct my_data.
> entry->refcount will also be invalid.
> Instead, q.next must be read first, and then compared with q to check
> list's emptiness (for example like in list_for_each_entry_rcu macro).

I agree you've identified a problem, but there's no way we should apply
this patch that adds "problem is here"! Instead we should show the
proper usage, which is a call to list_first_or_null_rcu().