]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
afs: Fix callback handling
authorDavid Howells <dhowells@redhat.com>
Fri, 19 Oct 2018 23:57:59 +0000 (00:57 +0100)
committerDavid Howells <dhowells@redhat.com>
Tue, 23 Oct 2018 23:41:09 +0000 (00:41 +0100)
In some circumstances, the callback interest pointer is NULL, so in such a
case we can't dereference it when checking to see if the callback is
broken.  This causes an oops in some circumstances.

Fix this by replacing the function that worked out the aggregate break
counter with one that actually does the comparison, and then make that
return true (ie. broken) if there is no callback interest as yet (ie. the
pointer is NULL).

Fixes: 68251f0a6818 ("afs: Fix whole-volume callback handling")
Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/fsclient.c
fs/afs/internal.h
fs/afs/security.c
fs/afs/yfsclient.c

index 3975969719de6a862b911dd21a61e3514a08e133..7c75a1813321ce16f60fbd8826824e951f6f0419 100644 (file)
@@ -269,7 +269,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,
 
        write_seqlock(&vnode->cb_lock);
 
-       if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {
+       if (!afs_cb_is_broken(call->cb_break, vnode, cbi)) {
                vnode->cb_version       = ntohl(*bp++);
                cb_expiry               = ntohl(*bp++);
                vnode->cb_type          = ntohl(*bp++);
index e5b596bd8acf6f083bdbe94ee692e8ef4d2873b1..b60d15212975acbd173b7be88bee8c16cd804509 100644 (file)
@@ -776,10 +776,13 @@ static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode)
        return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break;
 }
 
-static inline unsigned int afs_cb_break_sum(struct afs_vnode *vnode,
-                                           struct afs_cb_interest *cbi)
+static inline bool afs_cb_is_broken(unsigned int cb_break,
+                                   const struct afs_vnode *vnode,
+                                   const struct afs_cb_interest *cbi)
 {
-       return vnode->cb_break + cbi->server->cb_s_break + vnode->volume->cb_v_break;
+       return !cbi || cb_break != (vnode->cb_break +
+                                   cbi->server->cb_s_break +
+                                   vnode->volume->cb_v_break);
 }
 
 /*
index d1ae53fd37399756201a18dbe698cb79b98f6b63..5f58a9a17e694a09dbe0d0b70d9dbc0cc9833aa4 100644 (file)
@@ -147,7 +147,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
                                        break;
                                }
 
-                               if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) {
+                               if (afs_cb_is_broken(cb_break, vnode,
+                                                    vnode->cb_interest)) {
                                        changed = true;
                                        break;
                                }
@@ -177,7 +178,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
                }
        }
 
-       if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest))
+       if (afs_cb_is_broken(cb_break, vnode, vnode->cb_interest))
                goto someone_else_changed_it;
 
        /* We need a ref on any permits list we want to copy as we'll have to
@@ -256,7 +257,7 @@ found:
 
        spin_lock(&vnode->lock);
        zap = rcu_access_pointer(vnode->permit_cache);
-       if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) &&
+       if (!afs_cb_is_broken(cb_break, vnode, vnode->cb_interest) &&
            zap == permits)
                rcu_assign_pointer(vnode->permit_cache, replacement);
        else
index d5e3f00950406f910cd3a2554ad17ae252a52920..12658c1363ae41951783049d56b3933f3f3fd43e 100644 (file)
@@ -324,7 +324,7 @@ static void xdr_decode_YFSCallBack(struct afs_call *call,
 
        write_seqlock(&vnode->cb_lock);
 
-       if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {
+       if (!afs_cb_is_broken(call->cb_break, vnode, cbi)) {
                cb_expiry = xdr_to_u64(xdr->expiration_time);
                do_div(cb_expiry, 10 * 1000 * 1000);
                vnode->cb_version       = ntohl(xdr->version);