diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-01 08:36:27 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-01 08:36:27 -0500 |
| commit | ae753ee2771a1bacade56411bb98037b2545c929 (patch) | |
| tree | 51bd6c507272f03ace4da537f531029785e69cb5 | |
| parent | 3c1c4ddffb58b9e10b3365764fe59546130b3f32 (diff) | |
| parent | f8de483e7440b0d23ce6372b3ef8358841c8827b (diff) | |
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
| -rw-r--r-- | fs/afs/internal.h | 5 | ||||
| -rw-r--r-- | fs/afs/security.c | 18 | ||||
| -rw-r--r-- | fs/afs/super.c | 14 |
3 files changed, 27 insertions, 10 deletions
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index e03910cebdd4..804d1f905622 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
| @@ -441,7 +441,10 @@ enum afs_lock_state { | |||
| 441 | }; | 441 | }; |
| 442 | 442 | ||
| 443 | /* | 443 | /* |
| 444 | * AFS inode private data | 444 | * AFS inode private data. |
| 445 | * | ||
| 446 | * Note that afs_alloc_inode() *must* reset anything that could incorrectly | ||
| 447 | * leak from one inode to another. | ||
| 445 | */ | 448 | */ |
| 446 | struct afs_vnode { | 449 | struct afs_vnode { |
| 447 | struct inode vfs_inode; /* the VFS's inode record */ | 450 | struct inode vfs_inode; /* the VFS's inode record */ |
diff --git a/fs/afs/security.c b/fs/afs/security.c index 2b00097101b3..b88b7d45fdaa 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c | |||
| @@ -120,7 +120,7 @@ static void afs_hash_permits(struct afs_permits *permits) | |||
| 120 | void afs_cache_permit(struct afs_vnode *vnode, struct key *key, | 120 | void afs_cache_permit(struct afs_vnode *vnode, struct key *key, |
| 121 | unsigned int cb_break) | 121 | unsigned int cb_break) |
| 122 | { | 122 | { |
| 123 | struct afs_permits *permits, *xpermits, *replacement, *new = NULL; | 123 | struct afs_permits *permits, *xpermits, *replacement, *zap, *new = NULL; |
| 124 | afs_access_t caller_access = READ_ONCE(vnode->status.caller_access); | 124 | afs_access_t caller_access = READ_ONCE(vnode->status.caller_access); |
| 125 | size_t size = 0; | 125 | size_t size = 0; |
| 126 | bool changed = false; | 126 | bool changed = false; |
| @@ -204,7 +204,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, | |||
| 204 | new = kzalloc(sizeof(struct afs_permits) + | 204 | new = kzalloc(sizeof(struct afs_permits) + |
| 205 | sizeof(struct afs_permit) * size, GFP_NOFS); | 205 | sizeof(struct afs_permit) * size, GFP_NOFS); |
| 206 | if (!new) | 206 | if (!new) |
| 207 | return; | 207 | goto out_put; |
| 208 | 208 | ||
| 209 | refcount_set(&new->usage, 1); | 209 | refcount_set(&new->usage, 1); |
| 210 | new->nr_permits = size; | 210 | new->nr_permits = size; |
| @@ -229,8 +229,6 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, | |||
| 229 | 229 | ||
| 230 | afs_hash_permits(new); | 230 | afs_hash_permits(new); |
| 231 | 231 | ||
| 232 | afs_put_permits(permits); | ||
| 233 | |||
| 234 | /* Now see if the permit list we want is actually already available */ | 232 | /* Now see if the permit list we want is actually already available */ |
| 235 | spin_lock(&afs_permits_lock); | 233 | spin_lock(&afs_permits_lock); |
| 236 | 234 | ||
| @@ -262,11 +260,15 @@ found: | |||
| 262 | kfree(new); | 260 | kfree(new); |
| 263 | 261 | ||
| 264 | spin_lock(&vnode->lock); | 262 | spin_lock(&vnode->lock); |
| 265 | if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break) || | 263 | zap = rcu_access_pointer(vnode->permit_cache); |
| 266 | permits != rcu_access_pointer(vnode->permit_cache)) | 264 | if (cb_break == (vnode->cb_break + vnode->cb_interest->server->cb_s_break) && |
| 267 | goto someone_else_changed_it_unlock; | 265 | zap == permits) |
| 268 | rcu_assign_pointer(vnode->permit_cache, replacement); | 266 | rcu_assign_pointer(vnode->permit_cache, replacement); |
| 267 | else | ||
| 268 | zap = replacement; | ||
| 269 | spin_unlock(&vnode->lock); | 269 | spin_unlock(&vnode->lock); |
| 270 | afs_put_permits(zap); | ||
| 271 | out_put: | ||
| 270 | afs_put_permits(permits); | 272 | afs_put_permits(permits); |
| 271 | return; | 273 | return; |
| 272 | 274 | ||
diff --git a/fs/afs/super.c b/fs/afs/super.c index d3f97da61bdf..1037dd41a622 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
| @@ -536,7 +536,9 @@ static void afs_kill_super(struct super_block *sb) | |||
| 536 | } | 536 | } |
| 537 | 537 | ||
| 538 | /* | 538 | /* |
| 539 | * initialise an inode cache slab element prior to any use | 539 | * Initialise an inode cache slab element prior to any use. Note that |
| 540 | * afs_alloc_inode() *must* reset anything that could incorrectly leak from one | ||
| 541 | * inode to another. | ||
| 540 | */ | 542 | */ |
| 541 | static void afs_i_init_once(void *_vnode) | 543 | static void afs_i_init_once(void *_vnode) |
| 542 | { | 544 | { |
| @@ -568,11 +570,21 @@ static struct inode *afs_alloc_inode(struct super_block *sb) | |||
| 568 | 570 | ||
| 569 | atomic_inc(&afs_count_active_inodes); | 571 | atomic_inc(&afs_count_active_inodes); |
| 570 | 572 | ||
| 573 | /* Reset anything that shouldn't leak from one inode to the next. */ | ||
| 571 | memset(&vnode->fid, 0, sizeof(vnode->fid)); | 574 | memset(&vnode->fid, 0, sizeof(vnode->fid)); |
| 572 | memset(&vnode->status, 0, sizeof(vnode->status)); | 575 | memset(&vnode->status, 0, sizeof(vnode->status)); |
| 573 | 576 | ||
| 574 | vnode->volume = NULL; | 577 | vnode->volume = NULL; |
| 578 | vnode->lock_key = NULL; | ||
| 579 | vnode->permit_cache = NULL; | ||
| 580 | vnode->cb_interest = NULL; | ||
| 581 | #ifdef CONFIG_AFS_FSCACHE | ||
| 582 | vnode->cache = NULL; | ||
| 583 | #endif | ||
| 584 | |||
| 575 | vnode->flags = 1 << AFS_VNODE_UNSET; | 585 | vnode->flags = 1 << AFS_VNODE_UNSET; |
| 586 | vnode->cb_type = 0; | ||
| 587 | vnode->lock_state = AFS_VNODE_LOCK_NONE; | ||
| 576 | 588 | ||
| 577 | _leave(" = %p", &vnode->vfs_inode); | 589 | _leave(" = %p", &vnode->vfs_inode); |
| 578 | return &vnode->vfs_inode; | 590 | return &vnode->vfs_inode; |
