sunrpc: don’t mark uninitialised items as VALID. [Linux 3.16.72]

This Linux kernel change "sunrpc: don’t mark uninitialised items as VALID" is included in the Linux 3.16.72 release. This change is authored by NeilBrown <neilb [at] suse.com> on Fri Apr 5 11:34:40 2019 +1100. The commit for this change in Linux stable tree is cf0eea8 (patch) which is from upstream commit d58431e. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream d58431e.

sunrpc: don't mark uninitialised items as VALID.

commit d58431eacb226222430940134d97bfd72f292fcd upstream.

A recent commit added a call to cache_fresh_locked()
when an expired item was found.
The call sets the CACHE_VALID flag, so it is important
that the item actually is valid.
There are two ways it could be valid:
1/ If ->update has been called to fill in relevant content
2/ if CACHE_NEGATIVE is set, to say that content doesn't exist.

An expired item that is waiting for an update will be neither.
Setting CACHE_VALID will mean that a subsequent call to cache_put()
will be likely to dereference uninitialised pointers.

So we must make sure the item is valid, and we already have code to do
that in try_to_negate_entry().  This takes the hash lock and so cannot
be used directly, so take out the two lines that we need and use them.

Now cache_fresh_locked() is certain to be called only on
a valid item.

Fixes: 4ecd55ea0742 ("sunrpc: fix cache_head leak due to queued request")
Signed-off-by: NeilBrown <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
[bwh: Backported to 3.16: adjust context]
Signed-off-by: Ben Hutchings <[email protected]>

There are 3 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/sunrpc/cache.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index cdfa7f4..4a5598a4 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -50,6 +50,7 @@ static void cache_init(struct cache_head *h)
    h->last_refresh = now;
 }

+static inline int cache_is_valid(struct cache_head *h);
 static void cache_fresh_locked(struct cache_head *head, time_t expiry);
 static void cache_fresh_unlocked(struct cache_head *head,
                struct cache_detail *detail);
@@ -98,6 +99,8 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
                *hp = tmp->next;
                tmp->next = NULL;
                detail->entries --;
+               if (cache_is_valid(tmp) == -EAGAIN)
+                   set_bit(CACHE_NEGATIVE, &tmp->flags);
                cache_fresh_locked(tmp, 0);
                freeme = tmp;
                break;

Leave a Reply

Your email address will not be published. Required fields are marked *