From: Greg Kroah-Hartman Date: Mon, 6 Jan 2014 17:01:52 +0000 (-0800) Subject: 3.4-stable patches X-Git-Tag: v3.4.76~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bca8f90d453c36b646988dc2c99e1cb19d02b320;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: ext4-check-for-overlapping-extents-in-ext4_valid_extent_entries.patch ext4-fix-use-after-free-in-ext4_mb_new_blocks.patch --- diff --git a/queue-3.4/ext4-check-for-overlapping-extents-in-ext4_valid_extent_entries.patch b/queue-3.4/ext4-check-for-overlapping-extents-in-ext4_valid_extent_entries.patch new file mode 100644 index 00000000000..36303326336 --- /dev/null +++ b/queue-3.4/ext4-check-for-overlapping-extents-in-ext4_valid_extent_entries.patch @@ -0,0 +1,82 @@ +From 5946d089379a35dda0e531710b48fca05446a196 Mon Sep 17 00:00:00 2001 +From: Eryu Guan +Date: Tue, 3 Dec 2013 21:22:21 -0500 +Subject: ext4: check for overlapping extents in ext4_valid_extent_entries() + +From: Eryu Guan + +commit 5946d089379a35dda0e531710b48fca05446a196 upstream. + +A corrupted ext4 may have out of order leaf extents, i.e. + +extent: lblk 0--1023, len 1024, pblk 9217, flags: LEAF UNINIT +extent: lblk 1000--2047, len 1024, pblk 10241, flags: LEAF UNINIT + ^^^^ overlap with previous extent + +Reading such extent could hit BUG_ON() in ext4_es_cache_extent(). + + BUG_ON(end < lblk); + +The problem is that __read_extent_tree_block() tries to cache holes as +well but assumes 'lblk' is greater than 'prev' and passes underflowed +length to ext4_es_cache_extent(). Fix it by checking for overlapping +extents in ext4_valid_extent_entries(). + +I hit this when fuzz testing ext4, and am able to reproduce it by +modifying the on-disk extent by hand. + +Also add the check for (ee_block + len - 1) in ext4_valid_extent() to +make sure the value is not overflow. + +Ran xfstests on patched ext4 and no regression. + +Cc: Lukáš Czerner +Signed-off-by: Eryu Guan +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/extents.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +--- a/fs/ext4/extents.c ++++ b/fs/ext4/extents.c +@@ -317,8 +317,10 @@ static int ext4_valid_extent(struct inod + { + ext4_fsblk_t block = ext4_ext_pblock(ext); + int len = ext4_ext_get_actual_len(ext); ++ ext4_lblk_t lblock = le32_to_cpu(ext->ee_block); ++ ext4_lblk_t last = lblock + len - 1; + +- if (len == 0) ++ if (lblock > last) + return 0; + return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len); + } +@@ -344,11 +346,26 @@ static int ext4_valid_extent_entries(str + if (depth == 0) { + /* leaf entries */ + struct ext4_extent *ext = EXT_FIRST_EXTENT(eh); ++ struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; ++ ext4_fsblk_t pblock = 0; ++ ext4_lblk_t lblock = 0; ++ ext4_lblk_t prev = 0; ++ int len = 0; + while (entries) { + if (!ext4_valid_extent(inode, ext)) + return 0; ++ ++ /* Check for overlapping extents */ ++ lblock = le32_to_cpu(ext->ee_block); ++ len = ext4_ext_get_actual_len(ext); ++ if ((lblock <= prev) && prev) { ++ pblock = ext4_ext_pblock(ext); ++ es->s_last_error_block = cpu_to_le64(pblock); ++ return 0; ++ } + ext++; + entries--; ++ prev = lblock + len - 1; + } + } else { + struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh); diff --git a/queue-3.4/ext4-fix-use-after-free-in-ext4_mb_new_blocks.patch b/queue-3.4/ext4-fix-use-after-free-in-ext4_mb_new_blocks.patch new file mode 100644 index 00000000000..f4609464180 --- /dev/null +++ b/queue-3.4/ext4-fix-use-after-free-in-ext4_mb_new_blocks.patch @@ -0,0 +1,90 @@ +From 4e8d2139802ce4f41936a687f06c560b12115247 Mon Sep 17 00:00:00 2001 +From: Junho Ryu +Date: Tue, 3 Dec 2013 18:10:28 -0500 +Subject: ext4: fix use-after-free in ext4_mb_new_blocks + +From: Junho Ryu + +commit 4e8d2139802ce4f41936a687f06c560b12115247 upstream. + +ext4_mb_put_pa should hold pa->pa_lock before accessing pa->pa_count. +While ext4_mb_use_preallocated checks pa->pa_deleted first and then +increments pa->count later, ext4_mb_put_pa decrements pa->pa_count +before holding pa->pa_lock and then sets pa->pa_deleted. + +* Free sequence +ext4_mb_put_pa (1): atomic_dec_and_test pa->pa_count +ext4_mb_put_pa (2): lock pa->pa_lock +ext4_mb_put_pa (3): check pa->pa_deleted +ext4_mb_put_pa (4): set pa->pa_deleted=1 +ext4_mb_put_pa (5): unlock pa->pa_lock +ext4_mb_put_pa (6): remove pa from a list +ext4_mb_pa_callback: free pa + +* Use sequence +ext4_mb_use_preallocated (1): iterate over preallocation +ext4_mb_use_preallocated (2): lock pa->pa_lock +ext4_mb_use_preallocated (3): check pa->pa_deleted +ext4_mb_use_preallocated (4): increase pa->pa_count +ext4_mb_use_preallocated (5): unlock pa->pa_lock +ext4_mb_release_context: access pa + +* Use-after-free sequence +[initial status] pa_deleted = 0, pa_count = 1> +ext4_mb_use_preallocated (1): iterate over preallocation +ext4_mb_use_preallocated (2): lock pa->pa_lock +ext4_mb_use_preallocated (3): check pa->pa_deleted +ext4_mb_put_pa (1): atomic_dec_and_test pa->pa_count +[pa_count decremented] pa_deleted = 0, pa_count = 0> +ext4_mb_use_preallocated (4): increase pa->pa_count +[pa_count incremented] pa_deleted = 0, pa_count = 1> +ext4_mb_use_preallocated (5): unlock pa->pa_lock +ext4_mb_put_pa (2): lock pa->pa_lock +ext4_mb_put_pa (3): check pa->pa_deleted +ext4_mb_put_pa (4): set pa->pa_deleted=1 +[race condition!] pa_deleted = 1, pa_count = 1> +ext4_mb_put_pa (5): unlock pa->pa_lock +ext4_mb_put_pa (6): remove pa from a list +ext4_mb_pa_callback: free pa +ext4_mb_release_context: access pa + +AddressSanitizer has detected use-after-free in ext4_mb_new_blocks +Bug report: http://goo.gl/rG1On3 + +Signed-off-by: Junho Ryu +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/mballoc.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/fs/ext4/mballoc.c ++++ b/fs/ext4/mballoc.c +@@ -3326,6 +3326,9 @@ static void ext4_mb_pa_callback(struct r + { + struct ext4_prealloc_space *pa; + pa = container_of(head, struct ext4_prealloc_space, u.pa_rcu); ++ ++ BUG_ON(atomic_read(&pa->pa_count)); ++ BUG_ON(pa->pa_deleted == 0); + kmem_cache_free(ext4_pspace_cachep, pa); + } + +@@ -3339,11 +3342,13 @@ static void ext4_mb_put_pa(struct ext4_a + ext4_group_t grp; + ext4_fsblk_t grp_blk; + +- if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0) +- return; +- + /* in this short window concurrent discard can set pa_deleted */ + spin_lock(&pa->pa_lock); ++ if (!atomic_dec_and_test(&pa->pa_count) || pa->pa_free != 0) { ++ spin_unlock(&pa->pa_lock); ++ return; ++ } ++ + if (pa->pa_deleted == 1) { + spin_unlock(&pa->pa_lock); + return; diff --git a/queue-3.4/series b/queue-3.4/series index 474ee28e484..4ef2f6d535f 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -11,3 +11,5 @@ selinux-selinux_setprocattr-ptrace_parent-needs-rcu_read_lock.patch ftrace-initialize-the-ftrace-profiler-for-each-possible-cpu.patch intel_idle-initial-ivb-support.patch intel_idle-enable-ivb-xeon-support.patch +ext4-fix-use-after-free-in-ext4_mb_new_blocks.patch +ext4-check-for-overlapping-extents-in-ext4_valid_extent_entries.patch