diff options
author | Li Zefan <lizf@cn.fujitsu.com> | 2012-03-12 04:39:48 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.cz> | 2012-04-18 13:22:18 -0400 |
commit | cdc6a3952558f00b1bc3b6401e1cf98797632fe2 (patch) | |
tree | b97cf714429b439c6887b2fe0acf9065e1d09f1f /fs/btrfs/extent_io.c | |
parent | 8e52acf70459020d7e9e9fda25066be4da520943 (diff) |
Btrfs: avoid possible use-after-free in clear_extent_bit()
clear_extent_bit()
{
next_node = rb_next(&state->rb_node);
...
clear_state_bit(state); <-- this may free next_node
if (next_node) {
state = rb_entry(next_node);
...
}
}
clear_state_bit() calls merge_state() which may free the next node
of the passing extent_state, so clear_extent_bit() may end up
referencing freed memory.
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Diffstat (limited to 'fs/btrfs/extent_io.c')
-rw-r--r-- | fs/btrfs/extent_io.c | 36 |
1 files changed, 21 insertions, 15 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 05951bdf72cc..11eeb81fe695 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
@@ -402,6 +402,15 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig, | |||
402 | return 0; | 402 | return 0; |
403 | } | 403 | } |
404 | 404 | ||
405 | static struct extent_state *next_state(struct extent_state *state) | ||
406 | { | ||
407 | struct rb_node *next = rb_next(&state->rb_node); | ||
408 | if (next) | ||
409 | return rb_entry(next, struct extent_state, rb_node); | ||
410 | else | ||
411 | return NULL; | ||
412 | } | ||
413 | |||
405 | /* | 414 | /* |
406 | * utility function to clear some bits in an extent state struct. | 415 | * utility function to clear some bits in an extent state struct. |
407 | * it will optionally wake up any one waiting on this state (wake == 1) | 416 | * it will optionally wake up any one waiting on this state (wake == 1) |
@@ -409,10 +418,11 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig, | |||
409 | * If no bits are set on the state struct after clearing things, the | 418 | * If no bits are set on the state struct after clearing things, the |
410 | * struct is freed and removed from the tree | 419 | * struct is freed and removed from the tree |
411 | */ | 420 | */ |
412 | static void clear_state_bit(struct extent_io_tree *tree, | 421 | static struct extent_state *clear_state_bit(struct extent_io_tree *tree, |
413 | struct extent_state *state, | 422 | struct extent_state *state, |
414 | int *bits, int wake) | 423 | int *bits, int wake) |
415 | { | 424 | { |
425 | struct extent_state *next; | ||
416 | int bits_to_clear = *bits & ~EXTENT_CTLBITS; | 426 | int bits_to_clear = *bits & ~EXTENT_CTLBITS; |
417 | 427 | ||
418 | if ((bits_to_clear & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) { | 428 | if ((bits_to_clear & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) { |
@@ -425,6 +435,7 @@ static void clear_state_bit(struct extent_io_tree *tree, | |||
425 | if (wake) | 435 | if (wake) |
426 | wake_up(&state->wq); | 436 | wake_up(&state->wq); |
427 | if (state->state == 0) { | 437 | if (state->state == 0) { |
438 | next = next_state(state); | ||
428 | if (state->tree) { | 439 | if (state->tree) { |
429 | rb_erase(&state->rb_node, &tree->state); | 440 | rb_erase(&state->rb_node, &tree->state); |
430 | state->tree = NULL; | 441 | state->tree = NULL; |
@@ -434,7 +445,9 @@ static void clear_state_bit(struct extent_io_tree *tree, | |||
434 | } | 445 | } |
435 | } else { | 446 | } else { |
436 | merge_state(tree, state); | 447 | merge_state(tree, state); |
448 | next = next_state(state); | ||
437 | } | 449 | } |
450 | return next; | ||
438 | } | 451 | } |
439 | 452 | ||
440 | static struct extent_state * | 453 | static struct extent_state * |
@@ -473,7 +486,6 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, | |||
473 | struct extent_state *state; | 486 | struct extent_state *state; |
474 | struct extent_state *cached; | 487 | struct extent_state *cached; |
475 | struct extent_state *prealloc = NULL; | 488 | struct extent_state *prealloc = NULL; |
476 | struct rb_node *next_node; | ||
477 | struct rb_node *node; | 489 | struct rb_node *node; |
478 | u64 last_end; | 490 | u64 last_end; |
479 | int err; | 491 | int err; |
@@ -525,14 +537,11 @@ hit_next: | |||
525 | WARN_ON(state->end < start); | 537 | WARN_ON(state->end < start); |
526 | last_end = state->end; | 538 | last_end = state->end; |
527 | 539 | ||
528 | if (state->end < end && !need_resched()) | ||
529 | next_node = rb_next(&state->rb_node); | ||
530 | else | ||
531 | next_node = NULL; | ||
532 | |||
533 | /* the state doesn't have the wanted bits, go ahead */ | 540 | /* the state doesn't have the wanted bits, go ahead */ |
534 | if (!(state->state & bits)) | 541 | if (!(state->state & bits)) { |
542 | state = next_state(state); | ||
535 | goto next; | 543 | goto next; |
544 | } | ||
536 | 545 | ||
537 | /* | 546 | /* |
538 | * | ---- desired range ---- | | 547 | * | ---- desired range ---- | |
@@ -590,16 +599,13 @@ hit_next: | |||
590 | goto out; | 599 | goto out; |
591 | } | 600 | } |
592 | 601 | ||
593 | clear_state_bit(tree, state, &bits, wake); | 602 | state = clear_state_bit(tree, state, &bits, wake); |
594 | next: | 603 | next: |
595 | if (last_end == (u64)-1) | 604 | if (last_end == (u64)-1) |
596 | goto out; | 605 | goto out; |
597 | start = last_end + 1; | 606 | start = last_end + 1; |
598 | if (start <= end && next_node) { | 607 | if (start <= end && state && !need_resched()) |
599 | state = rb_entry(next_node, struct extent_state, | ||
600 | rb_node); | ||
601 | goto hit_next; | 608 | goto hit_next; |
602 | } | ||
603 | goto search_again; | 609 | goto search_again; |
604 | 610 | ||
605 | out: | 611 | out: |