Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

Al Viro dde194a6 d251ed27

+32 -41
+32 -41
fs/afs/super.c
··· 31 31 static void afs_i_init_once(void *foo); 32 32 static struct dentry *afs_mount(struct file_system_type *fs_type, 33 33 int flags, const char *dev_name, void *data); 34 + static void afs_kill_super(struct super_block *sb); 34 35 static struct inode *afs_alloc_inode(struct super_block *sb); 35 - static void afs_put_super(struct super_block *sb); 36 36 static void afs_destroy_inode(struct inode *inode); 37 37 static int afs_statfs(struct dentry *dentry, struct kstatfs *buf); 38 38 ··· 40 40 .owner = THIS_MODULE, 41 41 .name = "afs", 42 42 .mount = afs_mount, 43 - .kill_sb = kill_anon_super, 43 + .kill_sb = afs_kill_super, 44 44 .fs_flags = 0, 45 45 }; 46 46 ··· 50 50 .drop_inode = afs_drop_inode, 51 51 .destroy_inode = afs_destroy_inode, 52 52 .evict_inode = afs_evict_inode, 53 - .put_super = afs_put_super, 54 53 .show_options = generic_show_options, 55 54 }; 56 55 ··· 281 282 */ 282 283 static int afs_test_super(struct super_block *sb, void *data) 283 284 { 284 - struct afs_mount_params *params = data; 285 + struct afs_super_info *as1 = data; 285 286 struct afs_super_info *as = sb->s_fs_info; 286 287 287 - return as->volume == params->volume; 288 + return as->volume == as1->volume; 289 + } 290 + 291 + static int afs_set_super(struct super_block *sb, void *data) 292 + { 293 + sb->s_fs_info = data; 294 + return set_anon_super(sb, NULL); 288 295 } 289 296 290 297 /* 291 298 * fill in the superblock 292 299 */ 293 - static int afs_fill_super(struct super_block *sb, void *data) 300 + static int afs_fill_super(struct super_block *sb, 301 + struct afs_mount_params *params) 294 302 { 295 - struct afs_mount_params *params = data; 296 - struct afs_super_info *as = NULL; 303 + struct afs_super_info *as = sb->s_fs_info; 297 304 struct afs_fid fid; 298 305 struct dentry *root = NULL; 299 306 struct inode *inode = NULL; ··· 307 302 308 303 _enter(""); 309 304 310 - /* allocate a superblock info record */ 311 - as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); 312 - if (!as) { 313 - _leave(" = -ENOMEM"); 314 - return -ENOMEM; 315 - } 316 - 317 - afs_get_volume(params->volume); 318 - as->volume = params->volume; 319 - 320 305 /* fill in the superblock */ 321 306 sb->s_blocksize = PAGE_CACHE_SIZE; 322 307 sb->s_blocksize_bits = PAGE_CACHE_SHIFT; 323 308 sb->s_magic = AFS_FS_MAGIC; 324 309 sb->s_op = &afs_super_ops; 325 - sb->s_fs_info = as; 326 310 sb->s_bdi = &as->volume->bdi; 327 311 328 312 /* allocate the root inode and dentry */ ··· 320 326 fid.unique = 1; 321 327 inode = afs_iget(sb, params->key, &fid, NULL, NULL); 322 328 if (IS_ERR(inode)) 323 - goto error_inode; 329 + return PTR_ERR(inode); 324 330 325 331 if (params->autocell) 326 332 set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); ··· 336 342 _leave(" = 0"); 337 343 return 0; 338 344 339 - error_inode: 340 - ret = PTR_ERR(inode); 341 - inode = NULL; 342 345 error: 343 346 iput(inode); 344 - afs_put_volume(as->volume); 345 - kfree(as); 346 - 347 - sb->s_fs_info = NULL; 348 - 349 347 _leave(" = %d", ret); 350 348 return ret; 351 349 } ··· 353 367 struct afs_volume *vol; 354 368 struct key *key; 355 369 char *new_opts = kstrdup(options, GFP_KERNEL); 370 + struct afs_super_info *as; 356 371 int ret; 357 372 358 373 _enter(",,%s,%p", dev_name, options); ··· 386 399 ret = PTR_ERR(vol); 387 400 goto error; 388 401 } 389 - params.volume = vol; 402 + 403 + /* allocate a superblock info record */ 404 + as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); 405 + if (!as) { 406 + ret = -ENOMEM; 407 + afs_put_volume(vol); 408 + goto error; 409 + } 410 + as->volume = vol; 390 411 391 412 /* allocate a deviceless superblock */ 392 - sb = sget(fs_type, afs_test_super, set_anon_super, &params); 413 + sb = sget(fs_type, afs_test_super, afs_set_super, as); 393 414 if (IS_ERR(sb)) { 394 415 ret = PTR_ERR(sb); 416 + afs_put_volume(vol); 417 + kfree(as); 395 418 goto error; 396 419 } 397 420 ··· 419 422 } else { 420 423 _debug("reuse"); 421 424 ASSERTCMP(sb->s_flags, &, MS_ACTIVE); 425 + afs_put_volume(vol); 426 + kfree(as); 422 427 } 423 428 424 - afs_put_volume(params.volume); 425 429 afs_put_cell(params.cell); 426 430 kfree(new_opts); 427 431 _leave(" = 0 [%p]", sb); 428 432 return dget(sb->s_root); 429 433 430 434 error: 431 - afs_put_volume(params.volume); 432 435 afs_put_cell(params.cell); 433 436 key_put(params.key); 434 437 kfree(new_opts); ··· 436 439 return ERR_PTR(ret); 437 440 } 438 441 439 - /* 440 - * finish the unmounting process on the superblock 441 - */ 442 - static void afs_put_super(struct super_block *sb) 442 + static void afs_kill_super(struct super_block *sb) 443 443 { 444 444 struct afs_super_info *as = sb->s_fs_info; 445 - 446 - _enter(""); 447 - 445 + kill_anon_super(sb); 448 446 afs_put_volume(as->volume); 449 - 450 - _leave(""); 447 + kfree(as); 451 448 } 452 449 453 450 /*