aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/extent_io.c
diff options
context:
space:
mode:
authorLi Zefan <lizf@cn.fujitsu.com>2012-03-12 04:39:48 -0400
committerDavid Sterba <dsterba@suse.cz>2012-04-18 13:22:18 -0400
commitcdc6a3952558f00b1bc3b6401e1cf98797632fe2 (patch)
treeb97cf714429b439c6887b2fe0acf9065e1d09f1f /fs/btrfs/extent_io.c
parent8e52acf70459020d7e9e9fda25066be4da520943 (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.c36
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
405static 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 */
412static void clear_state_bit(struct extent_io_tree *tree, 421static 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
440static struct extent_state * 453static 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);
594next: 603next:
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
605out: 611out: