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 | /* |
