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>
This commit is contained in:
parent
8e52acf704
commit
cdc6a39525
1 changed files with 21 additions and 15 deletions
|
@ -402,6 +402,15 @@ static int split_state(struct extent_io_tree *tree, struct extent_state *orig,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct extent_state *next_state(struct extent_state *state)
|
||||||
|
{
|
||||||
|
struct rb_node *next = rb_next(&state->rb_node);
|
||||||
|
if (next)
|
||||||
|
return rb_entry(next, struct extent_state, rb_node);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* utility function to clear some bits in an extent state struct.
|
* utility function to clear some bits in an extent state struct.
|
||||||
* it will optionally wake up any one waiting on this state (wake == 1)
|
* 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,
|
||||||
* If no bits are set on the state struct after clearing things, the
|
* If no bits are set on the state struct after clearing things, the
|
||||||
* struct is freed and removed from the tree
|
* struct is freed and removed from the tree
|
||||||
*/
|
*/
|
||||||
static void clear_state_bit(struct extent_io_tree *tree,
|
static struct extent_state *clear_state_bit(struct extent_io_tree *tree,
|
||||||
struct extent_state *state,
|
struct extent_state *state,
|
||||||
int *bits, int wake)
|
int *bits, int wake)
|
||||||
{
|
{
|
||||||
|
struct extent_state *next;
|
||||||
int bits_to_clear = *bits & ~EXTENT_CTLBITS;
|
int bits_to_clear = *bits & ~EXTENT_CTLBITS;
|
||||||
|
|
||||||
if ((bits_to_clear & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) {
|
if ((bits_to_clear & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) {
|
||||||
|
@ -425,6 +435,7 @@ static void clear_state_bit(struct extent_io_tree *tree,
|
||||||
if (wake)
|
if (wake)
|
||||||
wake_up(&state->wq);
|
wake_up(&state->wq);
|
||||||
if (state->state == 0) {
|
if (state->state == 0) {
|
||||||
|
next = next_state(state);
|
||||||
if (state->tree) {
|
if (state->tree) {
|
||||||
rb_erase(&state->rb_node, &tree->state);
|
rb_erase(&state->rb_node, &tree->state);
|
||||||
state->tree = NULL;
|
state->tree = NULL;
|
||||||
|
@ -434,7 +445,9 @@ static void clear_state_bit(struct extent_io_tree *tree,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
merge_state(tree, state);
|
merge_state(tree, state);
|
||||||
|
next = next_state(state);
|
||||||
}
|
}
|
||||||
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct extent_state *
|
static struct extent_state *
|
||||||
|
@ -473,7 +486,6 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||||
struct extent_state *state;
|
struct extent_state *state;
|
||||||
struct extent_state *cached;
|
struct extent_state *cached;
|
||||||
struct extent_state *prealloc = NULL;
|
struct extent_state *prealloc = NULL;
|
||||||
struct rb_node *next_node;
|
|
||||||
struct rb_node *node;
|
struct rb_node *node;
|
||||||
u64 last_end;
|
u64 last_end;
|
||||||
int err;
|
int err;
|
||||||
|
@ -525,14 +537,11 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||||
WARN_ON(state->end < start);
|
WARN_ON(state->end < start);
|
||||||
last_end = state->end;
|
last_end = state->end;
|
||||||
|
|
||||||
if (state->end < end && !need_resched())
|
|
||||||
next_node = rb_next(&state->rb_node);
|
|
||||||
else
|
|
||||||
next_node = NULL;
|
|
||||||
|
|
||||||
/* the state doesn't have the wanted bits, go ahead */
|
/* the state doesn't have the wanted bits, go ahead */
|
||||||
if (!(state->state & bits))
|
if (!(state->state & bits)) {
|
||||||
|
state = next_state(state);
|
||||||
goto next;
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* | ---- desired range ---- |
|
* | ---- desired range ---- |
|
||||||
|
@ -590,16 +599,13 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_state_bit(tree, state, &bits, wake);
|
state = clear_state_bit(tree, state, &bits, wake);
|
||||||
next:
|
next:
|
||||||
if (last_end == (u64)-1)
|
if (last_end == (u64)-1)
|
||||||
goto out;
|
goto out;
|
||||||
start = last_end + 1;
|
start = last_end + 1;
|
||||||
if (start <= end && next_node) {
|
if (start <= end && state && !need_resched())
|
||||||
state = rb_entry(next_node, struct extent_state,
|
|
||||||
rb_node);
|
|
||||||
goto hit_next;
|
goto hit_next;
|
||||||
}
|
|
||||||
goto search_again;
|
goto search_again;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
Loading…
Reference in a new issue