aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-10-19 19:57:59 -0400
committerDavid Howells <dhowells@redhat.com>2018-10-23 19:41:09 -0400
commit18ac61853cc4e44eb30e125fc8344a3b25c7b6fe (patch)
tree9565bfd8b0dec553d351ca13ce841a5e2bbeae7a
parent2feeaf8433c8e68de3d0a06a0ffe7742bcd13c1a (diff)
afs: Fix callback handling
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>
-rw-r--r--fs/afs/fsclient.c2
-rw-r--r--fs/afs/internal.h9
-rw-r--r--fs/afs/security.c7
-rw-r--r--fs/afs/yfsclient.c2
4 files changed, 12 insertions, 8 deletions
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 3975969719de..7c75a1813321 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -269,7 +269,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,
269 269
270 write_seqlock(&vnode->cb_lock); 270 write_seqlock(&vnode->cb_lock);
271 271
272 if (call->cb_break == afs_cb_break_sum(vnode, cbi)) { 272 if (!afs_cb_is_broken(call->cb_break, vnode, cbi)) {
273 vnode->cb_version = ntohl(*bp++); 273 vnode->cb_version = ntohl(*bp++);
274 cb_expiry = ntohl(*bp++); 274 cb_expiry = ntohl(*bp++);
275 vnode->cb_type = ntohl(*bp++); 275 vnode->cb_type = ntohl(*bp++);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index e5b596bd8acf..b60d15212975 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -776,10 +776,13 @@ static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode)
776 return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break; 776 return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break;
777} 777}
778 778
779static inline unsigned int afs_cb_break_sum(struct afs_vnode *vnode, 779static inline bool afs_cb_is_broken(unsigned int cb_break,
780 struct afs_cb_interest *cbi) 780 const struct afs_vnode *vnode,
781 const struct afs_cb_interest *cbi)
781{ 782{
782 return vnode->cb_break + cbi->server->cb_s_break + vnode->volume->cb_v_break; 783 return !cbi || cb_break != (vnode->cb_break +
784 cbi->server->cb_s_break +
785 vnode->volume->cb_v_break);
783} 786}
784 787
785/* 788/*
diff --git a/fs/afs/security.c b/fs/afs/security.c
index d1ae53fd3739..5f58a9a17e69 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -147,7 +147,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
147 break; 147 break;
148 } 148 }
149 149
150 if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) { 150 if (afs_cb_is_broken(cb_break, vnode,
151 vnode->cb_interest)) {
151 changed = true; 152 changed = true;
152 break; 153 break;
153 } 154 }
@@ -177,7 +178,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
177 } 178 }
178 } 179 }
179 180
180 if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) 181 if (afs_cb_is_broken(cb_break, vnode, vnode->cb_interest))
181 goto someone_else_changed_it; 182 goto someone_else_changed_it;
182 183
183 /* We need a ref on any permits list we want to copy as we'll have to 184 /* We need a ref on any permits list we want to copy as we'll have to
@@ -256,7 +257,7 @@ found:
256 257
257 spin_lock(&vnode->lock); 258 spin_lock(&vnode->lock);
258 zap = rcu_access_pointer(vnode->permit_cache); 259 zap = rcu_access_pointer(vnode->permit_cache);
259 if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) && 260 if (!afs_cb_is_broken(cb_break, vnode, vnode->cb_interest) &&
260 zap == permits) 261 zap == permits)
261 rcu_assign_pointer(vnode->permit_cache, replacement); 262 rcu_assign_pointer(vnode->permit_cache, replacement);
262 else 263 else
diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c
index d5e3f0095040..12658c1363ae 100644
--- a/fs/afs/yfsclient.c
+++ b/fs/afs/yfsclient.c
@@ -324,7 +324,7 @@ static void xdr_decode_YFSCallBack(struct afs_call *call,
324 324
325 write_seqlock(&vnode->cb_lock); 325 write_seqlock(&vnode->cb_lock);
326 326
327 if (call->cb_break == afs_cb_break_sum(vnode, cbi)) { 327 if (!afs_cb_is_broken(call->cb_break, vnode, cbi)) {
328 cb_expiry = xdr_to_u64(xdr->expiration_time); 328 cb_expiry = xdr_to_u64(xdr->expiration_time);
329 do_div(cb_expiry, 10 * 1000 * 1000); 329 do_div(cb_expiry, 10 * 1000 * 1000);
330 vnode->cb_version = ntohl(xdr->version); 330 vnode->cb_version = ntohl(xdr->version);