diff options
author | Mike Snitzer <snitzer@redhat.com> | 2012-07-03 07:55:35 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2012-07-03 07:55:35 -0400 |
commit | 62662303e7f590fdfbb0070ab820a0ad4267c119 (patch) | |
tree | 48f5e40d7daf73bdc1d435f8c6cfbe70453ce855 /drivers/md/persistent-data | |
parent | 25d7cd6faa7ae6ed2565617c3ee2500ccb8a9f7f (diff) |
dm persistent data: handle space map checker creation failure
If CONFIG_DM_DEBUG_SPACE_MAPS is enabled and dm_sm_checker_create()
fails, dm_tm_create_internal() would still return success even though it
cleaned up all resources it was supposed to have created. This will
lead to a kernel crash:
general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC
...
RIP: 0010:[<ffffffff81593659>] [<ffffffff81593659>] dm_bufio_get_block_size+0x9/0x20
Call Trace:
[<ffffffff81599bae>] dm_bm_block_size+0xe/0x10
[<ffffffff8159b8b8>] sm_ll_init+0x78/0xd0
[<ffffffff8159c1a6>] sm_ll_new_disk+0x16/0xa0
[<ffffffff8159c98e>] dm_sm_disk_create+0xfe/0x160
[<ffffffff815abf6e>] dm_pool_metadata_open+0x16e/0x6a0
[<ffffffff815aa010>] pool_ctr+0x3f0/0x900
[<ffffffff8158d565>] dm_table_add_target+0x195/0x450
[<ffffffff815904c4>] table_load+0xe4/0x330
[<ffffffff815917ea>] ctl_ioctl+0x15a/0x2c0
[<ffffffff81591963>] dm_ctl_ioctl+0x13/0x20
[<ffffffff8116a4f8>] do_vfs_ioctl+0x98/0x560
[<ffffffff8116aa51>] sys_ioctl+0x91/0xa0
[<ffffffff81869f52>] system_call_fastpath+0x16/0x1b
Fix the space map checker code to return an appropriate ERR_PTR and have
dm_sm_disk_create() and dm_tm_create_internal() check for it with
IS_ERR.
Reported-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/persistent-data')
-rw-r--r-- | drivers/md/persistent-data/dm-space-map-checker.c | 24 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-space-map-disk.c | 11 | ||||
-rw-r--r-- | drivers/md/persistent-data/dm-transaction-manager.c | 8 |
3 files changed, 28 insertions, 15 deletions
diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c index 50ed53bf4aa2..6d7c8329250f 100644 --- a/drivers/md/persistent-data/dm-space-map-checker.c +++ b/drivers/md/persistent-data/dm-space-map-checker.c | |||
@@ -343,25 +343,25 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) | |||
343 | int r; | 343 | int r; |
344 | struct sm_checker *smc; | 344 | struct sm_checker *smc; |
345 | 345 | ||
346 | if (!sm) | 346 | if (IS_ERR_OR_NULL(sm)) |
347 | return NULL; | 347 | return ERR_PTR(-EINVAL); |
348 | 348 | ||
349 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); | 349 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); |
350 | if (!smc) | 350 | if (!smc) |
351 | return NULL; | 351 | return ERR_PTR(-ENOMEM); |
352 | 352 | ||
353 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); | 353 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); |
354 | r = ca_create(&smc->old_counts, sm); | 354 | r = ca_create(&smc->old_counts, sm); |
355 | if (r) { | 355 | if (r) { |
356 | kfree(smc); | 356 | kfree(smc); |
357 | return NULL; | 357 | return ERR_PTR(r); |
358 | } | 358 | } |
359 | 359 | ||
360 | r = ca_create(&smc->counts, sm); | 360 | r = ca_create(&smc->counts, sm); |
361 | if (r) { | 361 | if (r) { |
362 | ca_destroy(&smc->old_counts); | 362 | ca_destroy(&smc->old_counts); |
363 | kfree(smc); | 363 | kfree(smc); |
364 | return NULL; | 364 | return ERR_PTR(r); |
365 | } | 365 | } |
366 | 366 | ||
367 | smc->real_sm = sm; | 367 | smc->real_sm = sm; |
@@ -371,7 +371,7 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) | |||
371 | ca_destroy(&smc->counts); | 371 | ca_destroy(&smc->counts); |
372 | ca_destroy(&smc->old_counts); | 372 | ca_destroy(&smc->old_counts); |
373 | kfree(smc); | 373 | kfree(smc); |
374 | return NULL; | 374 | return ERR_PTR(r); |
375 | } | 375 | } |
376 | 376 | ||
377 | r = ca_commit(&smc->old_counts, &smc->counts); | 377 | r = ca_commit(&smc->old_counts, &smc->counts); |
@@ -379,7 +379,7 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) | |||
379 | ca_destroy(&smc->counts); | 379 | ca_destroy(&smc->counts); |
380 | ca_destroy(&smc->old_counts); | 380 | ca_destroy(&smc->old_counts); |
381 | kfree(smc); | 381 | kfree(smc); |
382 | return NULL; | 382 | return ERR_PTR(r); |
383 | } | 383 | } |
384 | 384 | ||
385 | return &smc->sm; | 385 | return &smc->sm; |
@@ -391,25 +391,25 @@ struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) | |||
391 | int r; | 391 | int r; |
392 | struct sm_checker *smc; | 392 | struct sm_checker *smc; |
393 | 393 | ||
394 | if (!sm) | 394 | if (IS_ERR_OR_NULL(sm)) |
395 | return NULL; | 395 | return ERR_PTR(-EINVAL); |
396 | 396 | ||
397 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); | 397 | smc = kmalloc(sizeof(*smc), GFP_KERNEL); |
398 | if (!smc) | 398 | if (!smc) |
399 | return NULL; | 399 | return ERR_PTR(-ENOMEM); |
400 | 400 | ||
401 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); | 401 | memcpy(&smc->sm, &ops_, sizeof(smc->sm)); |
402 | r = ca_create(&smc->old_counts, sm); | 402 | r = ca_create(&smc->old_counts, sm); |
403 | if (r) { | 403 | if (r) { |
404 | kfree(smc); | 404 | kfree(smc); |
405 | return NULL; | 405 | return ERR_PTR(r); |
406 | } | 406 | } |
407 | 407 | ||
408 | r = ca_create(&smc->counts, sm); | 408 | r = ca_create(&smc->counts, sm); |
409 | if (r) { | 409 | if (r) { |
410 | ca_destroy(&smc->old_counts); | 410 | ca_destroy(&smc->old_counts); |
411 | kfree(smc); | 411 | kfree(smc); |
412 | return NULL; | 412 | return ERR_PTR(r); |
413 | } | 413 | } |
414 | 414 | ||
415 | smc->real_sm = sm; | 415 | smc->real_sm = sm; |
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index fc469ba9f627..3d0ed5332883 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c | |||
@@ -290,7 +290,16 @@ struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm, | |||
290 | dm_block_t nr_blocks) | 290 | dm_block_t nr_blocks) |
291 | { | 291 | { |
292 | struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks); | 292 | struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks); |
293 | return dm_sm_checker_create_fresh(sm); | 293 | struct dm_space_map *smc; |
294 | |||
295 | if (IS_ERR_OR_NULL(sm)) | ||
296 | return sm; | ||
297 | |||
298 | smc = dm_sm_checker_create_fresh(sm); | ||
299 | if (IS_ERR(smc)) | ||
300 | dm_sm_destroy(sm); | ||
301 | |||
302 | return smc; | ||
294 | } | 303 | } |
295 | EXPORT_SYMBOL_GPL(dm_sm_disk_create); | 304 | EXPORT_SYMBOL_GPL(dm_sm_disk_create); |
296 | 305 | ||
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index 02bf78e9d10d..e5604b32d91f 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c | |||
@@ -347,8 +347,10 @@ static int dm_tm_create_internal(struct dm_block_manager *bm, | |||
347 | } | 347 | } |
348 | 348 | ||
349 | *sm = dm_sm_checker_create(inner); | 349 | *sm = dm_sm_checker_create(inner); |
350 | if (!*sm) | 350 | if (IS_ERR(*sm)) { |
351 | r = PTR_ERR(*sm); | ||
351 | goto bad2; | 352 | goto bad2; |
353 | } | ||
352 | 354 | ||
353 | } else { | 355 | } else { |
354 | r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location, | 356 | r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location, |
@@ -367,8 +369,10 @@ static int dm_tm_create_internal(struct dm_block_manager *bm, | |||
367 | } | 369 | } |
368 | 370 | ||
369 | *sm = dm_sm_checker_create(inner); | 371 | *sm = dm_sm_checker_create(inner); |
370 | if (!*sm) | 372 | if (IS_ERR(*sm)) { |
373 | r = PTR_ERR(*sm); | ||
371 | goto bad2; | 374 | goto bad2; |
375 | } | ||
372 | } | 376 | } |
373 | 377 | ||
374 | return 0; | 378 | return 0; |