Skip to content

Commit 9a6b294

Browse files
dhowellstorvalds
authored andcommitted
afs: Fix use-after-free due to get/remove race in volume tree
When an afs_volume struct is put, its refcount is reduced to 0 before the cell->volume_lock is taken and the volume removed from the cell->volumes tree. Unfortunately, this means that the lookup code can race and see a volume with a zero ref in the tree, resulting in a use-after-free: refcount_t: addition on 0; use-after-free. WARNING: CPU: 3 PID: 130782 at lib/refcount.c:25 refcount_warn_saturate+0x7a/0xda ... RIP: 0010:refcount_warn_saturate+0x7a/0xda ... Call Trace: afs_get_volume+0x3d/0x55 afs_create_volume+0x126/0x1de afs_validate_fc+0xfe/0x130 afs_get_tree+0x20/0x2e5 vfs_get_tree+0x1d/0xc9 do_new_mount+0x13b/0x22e do_mount+0x5d/0x8a __do_sys_mount+0x100/0x12a do_syscall_64+0x3a/0x94 entry_SYSCALL_64_after_hwframe+0x62/0x6a Fix this by: (1) When putting, use a flag to indicate if the volume has been removed from the tree and skip the rb_erase if it has. (2) When looking up, use a conditional ref increment and if it fails because the refcount is 0, replace the node in the tree and set the removal flag. Fixes: 2032596 ("afs: Reorganise volume and server trees to be rooted on the cell") Signed-off-by: David Howells <[email protected]> Reviewed-by: Jeffrey Altman <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent af73483 commit 9a6b294

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

fs/afs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ struct afs_volume {
586586
#define AFS_VOLUME_OFFLINE 4 /* - T if volume offline notice given */
587587
#define AFS_VOLUME_BUSY 5 /* - T if volume busy notice given */
588588
#define AFS_VOLUME_MAYBE_NO_IBULK 6 /* - T if some servers don't have InlineBulkStatus */
589+
#define AFS_VOLUME_RM_TREE 7 /* - Set if volume removed from cell->volumes */
589590
#ifdef CONFIG_AFS_FSCACHE
590591
struct fscache_volume *cache; /* Caching cookie */
591592
#endif
@@ -1513,6 +1514,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *,
15131514
extern struct afs_volume *afs_create_volume(struct afs_fs_context *);
15141515
extern int afs_activate_volume(struct afs_volume *);
15151516
extern void afs_deactivate_volume(struct afs_volume *);
1517+
bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason);
15161518
extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace);
15171519
extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs_volume_trace);
15181520
extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);

fs/afs/volume.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
3232
} else if (p->vid > volume->vid) {
3333
pp = &(*pp)->rb_right;
3434
} else {
35-
volume = afs_get_volume(p, afs_volume_trace_get_cell_insert);
36-
goto found;
35+
if (afs_try_get_volume(p, afs_volume_trace_get_cell_insert)) {
36+
volume = p;
37+
goto found;
38+
}
39+
40+
set_bit(AFS_VOLUME_RM_TREE, &volume->flags);
41+
rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes);
3742
}
3843
}
3944

@@ -56,7 +61,8 @@ static void afs_remove_volume_from_cell(struct afs_volume *volume)
5661
afs_volume_trace_remove);
5762
write_seqlock(&cell->volume_lock);
5863
hlist_del_rcu(&volume->proc_link);
59-
rb_erase(&volume->cell_node, &cell->volumes);
64+
if (!test_and_set_bit(AFS_VOLUME_RM_TREE, &volume->flags))
65+
rb_erase(&volume->cell_node, &cell->volumes);
6066
write_sequnlock(&cell->volume_lock);
6167
}
6268
}
@@ -231,6 +237,20 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
231237
_leave(" [destroyed]");
232238
}
233239

240+
/*
241+
* Try to get a reference on a volume record.
242+
*/
243+
bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason)
244+
{
245+
int r;
246+
247+
if (__refcount_inc_not_zero(&volume->ref, &r)) {
248+
trace_afs_volume(volume->vid, r + 1, reason);
249+
return true;
250+
}
251+
return false;
252+
}
253+
234254
/*
235255
* Get a reference on a volume record.
236256
*/

0 commit comments

Comments
 (0)