--- /dev/null
+From 5946d089379a35dda0e531710b48fca05446a196 Mon Sep 17 00:00:00 2001
+From: Eryu Guan <guaneryu@gmail.com>
+Date: Tue, 3 Dec 2013 21:22:21 -0500
+Subject: ext4: check for overlapping extents in ext4_valid_extent_entries()
+
+From: Eryu Guan <guaneryu@gmail.com>
+
+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 <lczerner@redhat.com>
+Signed-off-by: Eryu Guan <guaneryu@gmail.com>
+Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);
--- /dev/null
+From 4e8d2139802ce4f41936a687f06c560b12115247 Mon Sep 17 00:00:00 2001
+From: Junho Ryu <jayr@google.com>
+Date: Tue, 3 Dec 2013 18:10:28 -0500
+Subject: ext4: fix use-after-free in ext4_mb_new_blocks
+
+From: Junho Ryu <jayr@google.com>
+
+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->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->pa_deleted = 0, pa_count = 0>
+ext4_mb_use_preallocated (4): increase pa->pa_count
+[pa_count incremented] <pa->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->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 <jayr@google.com>
+Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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;