Skip to content

Commit

Permalink
afs: Fix callback handling
Browse files Browse the repository at this point in the history
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: 68251f0 ("afs: Fix whole-volume callback handling")
Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
David Howells committed Oct 23, 2018
1 parent 2feeaf8 commit 18ac618
Show file tree
Hide file tree
Showing 4 changed files with 12 additions and 8 deletions.
2 changes: 1 addition & 1 deletion fs/afs/fsclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++);
Expand Down
9 changes: 6 additions & 3 deletions fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand Down
7 changes: 4 additions & 3 deletions fs/afs/security.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -256,7 +257,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,

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
Expand Down
2 changes: 1 addition & 1 deletion fs/afs/yfsclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 18ac618

Please sign in to comment.