diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-06-12 16:01:21 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-06-12 17:45:36 -0400 |
commit | dde194a64bb5c3fd05d965775dc92e8a4920a53a (patch) | |
tree | 2de96109524026f5e3b2bf6d9e3a6999716c8bde /fs | |
parent | d251ed271d528afb407cc2ede30923e34cb209a5 (diff) |
afs: fix sget() races, close leak on umount
* set ->s_fs_info in set() callback passed to sget()
* allocate the thing and set it up enough for afs_test_super() before
making it visible
* have it freed in ->kill_sb() (current tree simply leaks it)
* have ->put_super() leave ->s_fs_info->volume alone; it's too early for
dropping it; do that from ->kill_sb() after having called kill_anon_super().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/afs/super.c | 73 |
1 files changed, 32 insertions, 41 deletions
diff --git a/fs/afs/super.c b/fs/afs/super.c index fb240e8766d6..b7d48d7eaa1e 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c | |||
@@ -31,8 +31,8 @@ | |||
31 | static void afs_i_init_once(void *foo); | 31 | static void afs_i_init_once(void *foo); |
32 | static struct dentry *afs_mount(struct file_system_type *fs_type, | 32 | static struct dentry *afs_mount(struct file_system_type *fs_type, |
33 | int flags, const char *dev_name, void *data); | 33 | int flags, const char *dev_name, void *data); |
34 | static void afs_kill_super(struct super_block *sb); | ||
34 | static struct inode *afs_alloc_inode(struct super_block *sb); | 35 | static struct inode *afs_alloc_inode(struct super_block *sb); |
35 | static void afs_put_super(struct super_block *sb); | ||
36 | static void afs_destroy_inode(struct inode *inode); | 36 | static void afs_destroy_inode(struct inode *inode); |
37 | static int afs_statfs(struct dentry *dentry, struct kstatfs *buf); | 37 | static int afs_statfs(struct dentry *dentry, struct kstatfs *buf); |
38 | 38 | ||
@@ -40,7 +40,7 @@ struct file_system_type afs_fs_type = { | |||
40 | .owner = THIS_MODULE, | 40 | .owner = THIS_MODULE, |
41 | .name = "afs", | 41 | .name = "afs", |
42 | .mount = afs_mount, | 42 | .mount = afs_mount, |
43 | .kill_sb = kill_anon_super, | 43 | .kill_sb = afs_kill_super, |
44 | .fs_flags = 0, | 44 | .fs_flags = 0, |
45 | }; | 45 | }; |
46 | 46 | ||
@@ -50,7 +50,6 @@ static const struct super_operations afs_super_ops = { | |||
50 | .drop_inode = afs_drop_inode, | 50 | .drop_inode = afs_drop_inode, |
51 | .destroy_inode = afs_destroy_inode, | 51 | .destroy_inode = afs_destroy_inode, |
52 | .evict_inode = afs_evict_inode, | 52 | .evict_inode = afs_evict_inode, |
53 | .put_super = afs_put_super, | ||
54 | .show_options = generic_show_options, | 53 | .show_options = generic_show_options, |
55 | }; | 54 | }; |
56 | 55 | ||
@@ -282,19 +281,25 @@ static int afs_parse_device_name(struct afs_mount_params *params, | |||
282 | */ | 281 | */ |
283 | static int afs_test_super(struct super_block *sb, void *data) | 282 | static int afs_test_super(struct super_block *sb, void *data) |
284 | { | 283 | { |
285 | struct afs_mount_params *params = data; | 284 | struct afs_super_info *as1 = data; |
286 | struct afs_super_info *as = sb->s_fs_info; | 285 | struct afs_super_info *as = sb->s_fs_info; |
287 | 286 | ||
288 | return as->volume == params->volume; | 287 | return as->volume == as1->volume; |
288 | } | ||
289 | |||
290 | static int afs_set_super(struct super_block *sb, void *data) | ||
291 | { | ||
292 | sb->s_fs_info = data; | ||
293 | return set_anon_super(sb, NULL); | ||
289 | } | 294 | } |
290 | 295 | ||
291 | /* | 296 | /* |
292 | * fill in the superblock | 297 | * fill in the superblock |
293 | */ | 298 | */ |
294 | static int afs_fill_super(struct super_block *sb, void *data) | 299 | static int afs_fill_super(struct super_block *sb, |
300 | struct afs_mount_params *params) | ||
295 | { | 301 | { |
296 | struct afs_mount_params *params = data; | 302 | struct afs_super_info *as = sb->s_fs_info; |
297 | struct afs_super_info *as = NULL; | ||
298 | struct afs_fid fid; | 303 | struct afs_fid fid; |
299 | struct dentry *root = NULL; | 304 | struct dentry *root = NULL; |
300 | struct inode *inode = NULL; | 305 | struct inode *inode = NULL; |
@@ -302,22 +307,11 @@ static int afs_fill_super(struct super_block *sb, void *data) | |||
302 | 307 | ||
303 | _enter(""); | 308 | _enter(""); |
304 | 309 | ||
305 | /* allocate a superblock info record */ | ||
306 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); | ||
307 | if (!as) { | ||
308 | _leave(" = -ENOMEM"); | ||
309 | return -ENOMEM; | ||
310 | } | ||
311 | |||
312 | afs_get_volume(params->volume); | ||
313 | as->volume = params->volume; | ||
314 | |||
315 | /* fill in the superblock */ | 310 | /* fill in the superblock */ |
316 | sb->s_blocksize = PAGE_CACHE_SIZE; | 311 | sb->s_blocksize = PAGE_CACHE_SIZE; |
317 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 312 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
318 | sb->s_magic = AFS_FS_MAGIC; | 313 | sb->s_magic = AFS_FS_MAGIC; |
319 | sb->s_op = &afs_super_ops; | 314 | sb->s_op = &afs_super_ops; |
320 | sb->s_fs_info = as; | ||
321 | sb->s_bdi = &as->volume->bdi; | 315 | sb->s_bdi = &as->volume->bdi; |
322 | 316 | ||
323 | /* allocate the root inode and dentry */ | 317 | /* allocate the root inode and dentry */ |
@@ -326,7 +320,7 @@ static int afs_fill_super(struct super_block *sb, void *data) | |||
326 | fid.unique = 1; | 320 | fid.unique = 1; |
327 | inode = afs_iget(sb, params->key, &fid, NULL, NULL); | 321 | inode = afs_iget(sb, params->key, &fid, NULL, NULL); |
328 | if (IS_ERR(inode)) | 322 | if (IS_ERR(inode)) |
329 | goto error_inode; | 323 | return PTR_ERR(inode); |
330 | 324 | ||
331 | if (params->autocell) | 325 | if (params->autocell) |
332 | set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); | 326 | set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); |
@@ -342,16 +336,8 @@ static int afs_fill_super(struct super_block *sb, void *data) | |||
342 | _leave(" = 0"); | 336 | _leave(" = 0"); |
343 | return 0; | 337 | return 0; |
344 | 338 | ||
345 | error_inode: | ||
346 | ret = PTR_ERR(inode); | ||
347 | inode = NULL; | ||
348 | error: | 339 | error: |
349 | iput(inode); | 340 | iput(inode); |
350 | afs_put_volume(as->volume); | ||
351 | kfree(as); | ||
352 | |||
353 | sb->s_fs_info = NULL; | ||
354 | |||
355 | _leave(" = %d", ret); | 341 | _leave(" = %d", ret); |
356 | return ret; | 342 | return ret; |
357 | } | 343 | } |
@@ -367,6 +353,7 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, | |||
367 | struct afs_volume *vol; | 353 | struct afs_volume *vol; |
368 | struct key *key; | 354 | struct key *key; |
369 | char *new_opts = kstrdup(options, GFP_KERNEL); | 355 | char *new_opts = kstrdup(options, GFP_KERNEL); |
356 | struct afs_super_info *as; | ||
370 | int ret; | 357 | int ret; |
371 | 358 | ||
372 | _enter(",,%s,%p", dev_name, options); | 359 | _enter(",,%s,%p", dev_name, options); |
@@ -399,12 +386,22 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, | |||
399 | ret = PTR_ERR(vol); | 386 | ret = PTR_ERR(vol); |
400 | goto error; | 387 | goto error; |
401 | } | 388 | } |
402 | params.volume = vol; | 389 | |
390 | /* allocate a superblock info record */ | ||
391 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); | ||
392 | if (!as) { | ||
393 | ret = -ENOMEM; | ||
394 | afs_put_volume(vol); | ||
395 | goto error; | ||
396 | } | ||
397 | as->volume = vol; | ||
403 | 398 | ||
404 | /* allocate a deviceless superblock */ | 399 | /* allocate a deviceless superblock */ |
405 | sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms); | 400 | sb = sget(fs_type, afs_test_super, afs_set_super, as); |
406 | if (IS_ERR(sb)) { | 401 | if (IS_ERR(sb)) { |
407 | ret = PTR_ERR(sb); | 402 | ret = PTR_ERR(sb); |
403 | afs_put_volume(vol); | ||
404 | kfree(as); | ||
408 | goto error; | 405 | goto error; |
409 | } | 406 | } |
410 | 407 | ||
@@ -422,16 +419,16 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, | |||
422 | } else { | 419 | } else { |
423 | _debug("reuse"); | 420 | _debug("reuse"); |
424 | ASSERTCMP(sb->s_flags, &, MS_ACTIVE); | 421 | ASSERTCMP(sb->s_flags, &, MS_ACTIVE); |
422 | afs_put_volume(vol); | ||
423 | kfree(as); | ||
425 | } | 424 | } |
426 | 425 | ||
427 | afs_put_volume(params.volume); | ||
428 | afs_put_cell(params.cell); | 426 | afs_put_cell(params.cell); |
429 | kfree(new_opts); | 427 | kfree(new_opts); |
430 | _leave(" = 0 [%p]", sb); | 428 | _leave(" = 0 [%p]", sb); |
431 | return dget(sb->s_root); | 429 | return dget(sb->s_root); |
432 | 430 | ||
433 | error: | 431 | error: |
434 | afs_put_volume(params.volume); | ||
435 | afs_put_cell(params.cell); | 432 | afs_put_cell(params.cell); |
436 | key_put(params.key); | 433 | key_put(params.key); |
437 | kfree(new_opts); | 434 | kfree(new_opts); |
@@ -439,18 +436,12 @@ error: | |||
439 | return ERR_PTR(ret); | 436 | return ERR_PTR(ret); |
440 | } | 437 | } |
441 | 438 | ||
442 | /* | 439 | static void afs_kill_super(struct super_block *sb) |
443 | * finish the unmounting process on the superblock | ||
444 | */ | ||
445 | static void afs_put_super(struct super_block *sb) | ||
446 | { | 440 | { |
447 | struct afs_super_info *as = sb->s_fs_info; | 441 | struct afs_super_info *as = sb->s_fs_info; |
448 | 442 | kill_anon_super(sb); | |
449 | _enter(""); | ||
450 | |||
451 | afs_put_volume(as->volume); | 443 | afs_put_volume(as->volume); |
452 | 444 | kfree(as); | |
453 | _leave(""); | ||
454 | } | 445 | } |
455 | 446 | ||
456 | /* | 447 | /* |