mirror of
https://github.com/LineageOS/android_kernel_fxtec_sm6115.git
synced 2026-08-18 13:21:06 +00:00
gfs2: Validate i_depth for exhash directories
[ Upstream commit 557c024ca7250bb65ae60f16c02074106c2f197b ] A fuzzer test introduced corruption that ends up with a depth of 0 in dir_e_read(), causing an undefined shift by 32 at: index = hash >> (32 - dip->i_depth); As calculated in an open-coded way in dir_make_exhash(), the minimum depth for an exhash directory is ilog2(sdp->sd_hash_ptrs) and 0 is invalid as sdp->sd_hash_ptrs is fixed as sdp->bsize / 16 at mount time. So we can avoid the undefined behaviour by checking for depth values lower than the minimum in gfs2_dinode_in(). Values greater than the maximum are already being checked for there. Also switch the calculation in dir_make_exhash() to use ilog2() to clarify how the depth is calculated. Tested with the syzkaller repro.c and xfstests '-g quick'. Reported-by: syzbot+4708579bb230a0582a57@syzkaller.appspotmail.com Signed-off-by: Andrew Price <anprice@redhat.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> [ To maintain consistency in error handling in gfs2_dinode_in(), use "goto corrupt" in v5.10. ] Signed-off-by: Ruohan Lan <ruohanlan@aliyun.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Ulrich Hecht <uli@kernel.org>
This commit is contained in:
committed by
Ulrich Hecht
parent
90abf9a7b3
commit
a6775c113c
@ -63,6 +63,7 @@
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
#include "incore.h"
|
||||
@ -914,7 +915,6 @@ static int dir_make_exhash(struct inode *inode)
|
||||
struct qstr args;
|
||||
struct buffer_head *bh, *dibh;
|
||||
struct gfs2_leaf *leaf;
|
||||
int y;
|
||||
u32 x;
|
||||
__be64 *lp;
|
||||
u64 bn;
|
||||
@ -981,9 +981,7 @@ static int dir_make_exhash(struct inode *inode)
|
||||
i_size_write(inode, sdp->sd_sb.sb_bsize / 2);
|
||||
gfs2_add_inode_blocks(&dip->i_inode, 1);
|
||||
dip->i_diskflags |= GFS2_DIF_EXHASH;
|
||||
|
||||
for (x = sdp->sd_hash_ptrs, y = -1; x; x >>= 1, y++) ;
|
||||
dip->i_depth = y;
|
||||
dip->i_depth = ilog2(sdp->sd_hash_ptrs);
|
||||
|
||||
gfs2_dinode_out(dip, dibh->b_data);
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include <linux/bio.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
#include "incore.h"
|
||||
@ -386,6 +387,9 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
|
||||
depth = be16_to_cpu(str->di_depth);
|
||||
if (unlikely(depth > GFS2_DIR_MAX_DEPTH))
|
||||
goto corrupt;
|
||||
if ((ip->i_diskflags & GFS2_DIF_EXHASH) &&
|
||||
depth < ilog2(sdp->sd_hash_ptrs))
|
||||
goto corrupt;
|
||||
ip->i_depth = (u8)depth;
|
||||
ip->i_entries = be32_to_cpu(str->di_entries);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user