mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 11:06:41 -05:00
gfs2: Validate i_depth for exhash directories
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>
This commit is contained in:
committed by
Andreas Gruenbacher
parent
5c8f12cf1e
commit
557c024ca7
@@ -60,6 +60,7 @@
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
#include "incore.h"
|
||||
@@ -912,7 +913,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;
|
||||
@@ -979,9 +979,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);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/bio.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include "gfs2.h"
|
||||
#include "incore.h"
|
||||
@@ -450,6 +451,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
|
||||
gfs2_consist_inode(ip);
|
||||
return -EIO;
|
||||
}
|
||||
if ((ip->i_diskflags & GFS2_DIF_EXHASH) &&
|
||||
depth < ilog2(sdp->sd_hash_ptrs)) {
|
||||
gfs2_consist_inode(ip);
|
||||
return -EIO;
|
||||
}
|
||||
ip->i_depth = (u8)depth;
|
||||
ip->i_entries = be32_to_cpu(str->di_entries);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user