Commit ae753ee2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-fixes-20171201' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:
 "Two fix patches for the AFS filesystem:

   - Fix the refcounting on permit caching.

   - AFS inode (afs_vnode) fields need resetting after allocation
     because they're only initialised when slab pages are obtained from
     the page allocator"

* tag 'afs-fixes-20171201' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Properly reset afs_vnode (inode) fields
  afs: Fix permit refcounting
parents 3c1c4ddf f8de483e
...@@ -441,7 +441,10 @@ enum afs_lock_state { ...@@ -441,7 +441,10 @@ enum afs_lock_state {
}; };
/* /*
* AFS inode private data * AFS inode private data.
*
* Note that afs_alloc_inode() *must* reset anything that could incorrectly
* leak from one inode to another.
*/ */
struct afs_vnode { struct afs_vnode {
struct inode vfs_inode; /* the VFS's inode record */ struct inode vfs_inode; /* the VFS's inode record */
......
...@@ -120,7 +120,7 @@ static void afs_hash_permits(struct afs_permits *permits) ...@@ -120,7 +120,7 @@ static void afs_hash_permits(struct afs_permits *permits)
void afs_cache_permit(struct afs_vnode *vnode, struct key *key, void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
unsigned int cb_break) unsigned int cb_break)
{ {
struct afs_permits *permits, *xpermits, *replacement, *new = NULL; struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL;
afs_access_t caller_access = READ_ONCE(vnode->status.caller_access); afs_access_t caller_access = READ_ONCE(vnode->status.caller_access);
size_t size = 0; size_t size = 0;
bool changed = false; bool changed = false;
...@@ -204,7 +204,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -204,7 +204,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
new = kzalloc(sizeof(struct afs_permits) + new = kzalloc(sizeof(struct afs_permits) +
sizeof(struct afs_permit) * size, GFP_NOFS); sizeof(struct afs_permit) * size, GFP_NOFS);
if (!new) if (!new)
return; goto out_put;
refcount_set(&new->usage, 1); refcount_set(&new->usage, 1);
new->nr_permits = size; new->nr_permits = size;
...@@ -229,8 +229,6 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -229,8 +229,6 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
afs_hash_permits(new); afs_hash_permits(new);
afs_put_permits(permits);
/* Now see if the permit list we want is actually already available */ /* Now see if the permit list we want is actually already available */
spin_lock(&afs_permits_lock); spin_lock(&afs_permits_lock);
...@@ -262,11 +260,15 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -262,11 +260,15 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
kfree(new); kfree(new);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break) || zap = rcu_access_pointer(vnode->permit_cache);
permits != rcu_access_pointer(vnode->permit_cache)) if (cb_break == (vnode->cb_break + vnode->cb_interest->server->cb_s_break) &&
goto someone_else_changed_it_unlock; zap == permits)
rcu_assign_pointer(vnode->permit_cache, replacement); rcu_assign_pointer(vnode->permit_cache, replacement);
else
zap = replacement;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
afs_put_permits(zap);
out_put:
afs_put_permits(permits); afs_put_permits(permits);
return; return;
......
...@@ -536,7 +536,9 @@ static void afs_kill_super(struct super_block *sb) ...@@ -536,7 +536,9 @@ static void afs_kill_super(struct super_block *sb)
} }
/* /*
* initialise an inode cache slab element prior to any use * Initialise an inode cache slab element prior to any use. Note that
* afs_alloc_inode() *must* reset anything that could incorrectly leak from one
* inode to another.
*/ */
static void afs_i_init_once(void *_vnode) static void afs_i_init_once(void *_vnode)
{ {
...@@ -568,11 +570,21 @@ static struct inode *afs_alloc_inode(struct super_block *sb) ...@@ -568,11 +570,21 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
atomic_inc(&afs_count_active_inodes); atomic_inc(&afs_count_active_inodes);
/* Reset anything that shouldn't leak from one inode to the next. */
memset(&vnode->fid, 0, sizeof(vnode->fid)); memset(&vnode->fid, 0, sizeof(vnode->fid));
memset(&vnode->status, 0, sizeof(vnode->status)); memset(&vnode->status, 0, sizeof(vnode->status));
vnode->volume = NULL; vnode->volume = NULL;
vnode->lock_key = NULL;
vnode->permit_cache = NULL;
vnode->cb_interest = NULL;
#ifdef CONFIG_AFS_FSCACHE
vnode->cache = NULL;
#endif
vnode->flags = 1 << AFS_VNODE_UNSET; vnode->flags = 1 << AFS_VNODE_UNSET;
vnode->cb_type = 0;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
_leave(" = %p", &vnode->vfs_inode); _leave(" = %p", &vnode->vfs_inode);
return &vnode->vfs_inode; return &vnode->vfs_inode;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment