Re: [PATCH v2] gfs2: reject oversized dinode sizes before i_size_write
Jiaming Zhang <[email protected]> Thu, 23 Jul 2026 15:10:34 +0800
| Newsgroups | dev.linux.lists.gfs2,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <CANypQFZTFMxk5W4d3GViW6T1Nkxua3nVk3RNx0fie-26fpmXSg@mail.gmail.com> |
Jiaming Zhang <[email protected]> =E4=BA=8E2026=E5=B9=B47=E6=9C=887=E6= =97=A5=E5=91=A8=E4=BA=8C 02:00=E5=86=99=E9=81=93=EF=BC=9A Hi, > > A corrupted GFS2 image can store a dinode size that is larger than what V= FS > i_size can represent. gfs2_dinode_in() reads the on-disk di_size as a u64= and > writes it directly into inode->i_size. If the value is larger than S64_MA= X, the > incore i_size becomes negative. That negative value can bypass the existi= ng > stuffed inode size check: > > inode->i_size > gfs2_max_stuffed_size(ip) > > Later, gfs2_quotad may try to sync the quota file and unstuff the quota i= node. > gfs2_unstuffer_folio() reads the negative i_size into an unsigned length = and > passes it to memcpy(), turning it into a huge copy size and triggering a > out-of-bound issue. > > Reject dinodes whose size exceeds sb->s_maxbytes before storing the value= in > inode->i_size. Also make the stuffed inode check use the raw on-disk size= while > it is still unsigned. > > Fixes: 70376c7ff312 ("gfs2: Always check inode size of inline inodes") > Closes: https://lore.kernel.org/lkml/CANypQFaF6bvORKKbRALvEL0k_epFaneFiOQ= [email protected]/ > Assisted-by: Codex:gpt-5.5-xhigh > Cc: [email protected] > Signed-off-by: Jiaming Zhang <[email protected]> > --- > Changes in v2: > - Drop the defensive unstuffing changes in bmap.c. > > fs/gfs2/glops.c | 9 +++++++-- > 1 file changed, 7 insertions(+), 2 deletions(-) > > diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c > index 28f32424ee64..8c5d257451d5 100644 > --- a/fs/gfs2/glops.c > +++ b/fs/gfs2/glops.c > @@ -393,11 +393,16 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, co= nst void *buf) > umode_t mode =3D be32_to_cpu(str->di_mode); > struct inode *inode =3D &ip->i_inode; > bool is_new =3D inode_state_read_once(inode) & I_NEW; > + u64 size =3D be64_to_cpu(str->di_size); > > if (unlikely(ip->i_no_addr !=3D be64_to_cpu(str->di_num.no_addr))= ) { > gfs2_consist_inode(ip); > return -EIO; > } > + if (unlikely(size > (u64)inode->i_sb->s_maxbytes)) { > + gfs2_consist_inode(ip); > + return -EIO; > + } > if (unlikely(!is_new && inode_wrong_type(inode, mode))) { > gfs2_consist_inode(ip); > return -EIO; > @@ -418,7 +423,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, cons= t void *buf) > i_uid_write(inode, be32_to_cpu(str->di_uid)); > i_gid_write(inode, be32_to_cpu(str->di_gid)); > set_nlink(inode, be32_to_cpu(str->di_nlink)); > - i_size_write(inode, be64_to_cpu(str->di_size)); > + i_size_write(inode, size); > gfs2_set_inode_blocks(inode, be64_to_cpu(str->di_blocks)); > atime.tv_sec =3D be64_to_cpu(str->di_atime); > atime.tv_nsec =3D be32_to_cpu(str->di_atime_nsec); > @@ -462,7 +467,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, cons= t void *buf) > return -EIO; > } > > - if (gfs2_is_stuffed(ip) && inode->i_size > gfs2_max_stuffed_size(= ip)) { > + if (gfs2_is_stuffed(ip) && size > gfs2_max_stuffed_size(ip)) { > gfs2_consist_inode(ip); > return -EIO; > } > -- > 2.43.0 > Friendly ping. Please let me know if there's anything you'd like me to chan= ge. Best Regards, Jiaming Zhang