Skip to content

Commit 557c024

Browse files
andypriceAndreas Gruenbacher
authored andcommitted
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>
1 parent 5c8f12c commit 557c024

2 files changed

Lines changed: 8 additions & 4 deletions

File tree

fs/gfs2/dir.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include <linux/crc32.h>
6161
#include <linux/vmalloc.h>
6262
#include <linux/bio.h>
63+
#include <linux/log2.h>
6364

6465
#include "gfs2.h"
6566
#include "incore.h"
@@ -912,7 +913,6 @@ static int dir_make_exhash(struct inode *inode)
912913
struct qstr args;
913914
struct buffer_head *bh, *dibh;
914915
struct gfs2_leaf *leaf;
915-
int y;
916916
u32 x;
917917
__be64 *lp;
918918
u64 bn;
@@ -979,9 +979,7 @@ static int dir_make_exhash(struct inode *inode)
979979
i_size_write(inode, sdp->sd_sb.sb_bsize / 2);
980980
gfs2_add_inode_blocks(&dip->i_inode, 1);
981981
dip->i_diskflags |= GFS2_DIF_EXHASH;
982-
983-
for (x = sdp->sd_hash_ptrs, y = -1; x; x >>= 1, y++) ;
984-
dip->i_depth = y;
982+
dip->i_depth = ilog2(sdp->sd_hash_ptrs);
985983

986984
gfs2_dinode_out(dip, dibh->b_data);
987985

fs/gfs2/glops.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/bio.h>
1212
#include <linux/posix_acl.h>
1313
#include <linux/security.h>
14+
#include <linux/log2.h>
1415

1516
#include "gfs2.h"
1617
#include "incore.h"
@@ -450,6 +451,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
450451
gfs2_consist_inode(ip);
451452
return -EIO;
452453
}
454+
if ((ip->i_diskflags & GFS2_DIF_EXHASH) &&
455+
depth < ilog2(sdp->sd_hash_ptrs)) {
456+
gfs2_consist_inode(ip);
457+
return -EIO;
458+
}
453459
ip->i_depth = (u8)depth;
454460
ip->i_entries = be32_to_cpu(str->di_entries);
455461

0 commit comments

Comments
 (0)