syzbot reported a KASAN out-of-bounds Read in ext4_xattr_set_entry()[1].
When xattr_find_entry() returns -ENODATA, search.here still points to the position after the last valid entry. ext4_xattr_block_set() clones the xattr block because the original block maybe shared and must not be modified in place.
In the clone_block, search.here is recomputed unconditionally from the old offset, which may place it past search.first. This results in a negative reset size and an out-of-bounds memmove() in ext4_xattr_set_entry().
Fix this by initializing search.here correctly when search.not_found is set.
#syz test
[1] https://syzkaller.appspot.com/bug?extid=f792df426ff0f5ceb8d1
Fixes: fd48e9acdf2 (ext4: Unindent codeblock in ext4_xattr_block_set) Cc: stable@vger.kernel.org Reported-by: syzbot+f792df426ff0f5ceb8d1@syzkaller.appspotmail.com Signed-off-by: Jinchao Wang wangjinchao600@gmail.com --- fs/ext4/xattr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 2e02efbddaac..cc30abeb7f30 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1980,7 +1980,10 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, goto cleanup; s->first = ENTRY(header(s->base)+1); header(s->base)->h_refcount = cpu_to_le32(1); - s->here = ENTRY(s->base + offset); + if (s->not_found) + s->here = s->first; + else + s->here = ENTRY(s->base + offset); s->end = s->base + bs->bh->b_size;
/*
linux-stable-mirror@lists.linaro.org