Skip to content

Commit 72173a6

Browse files
committed
xfs: don't walk off the end of a directory data block
jira LE-3201 cve CVE-2024-41013 Rebuild_History Non-Buildable kernel-rt-4.18.0-553.22.1.rt7.363.el8_10 commit-author lei lu <[email protected]> commit 0c7fcdb This adds sanity checks for xfs_dir2_data_unused and xfs_dir2_data_entry to make sure don't stray beyond valid memory region. Before patching, the loop simply checks that the start offset of the dup and dep is within the range. So in a crafted image, if last entry is xfs_dir2_data_unused, we can change dup->length to dup->length-1 and leave 1 byte of space. In the next traversal, this space will be considered as dup or dep. We may encounter an out of bound read when accessing the fixed members. In the patch, we make sure that the remaining bytes large enough to hold an unused entry before accessing xfs_dir2_data_unused and xfs_dir2_data_unused is XFS_DIR2_DATA_ALIGN byte aligned. We also make sure that the remaining bytes large enough to hold a dirent with a single-byte name before accessing xfs_dir2_data_entry. Signed-off-by: lei lu <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]> (cherry picked from commit 0c7fcdb) Signed-off-by: Jonathan Maple <[email protected]>
1 parent d4edd79 commit 72173a6

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

fs/xfs/libxfs/xfs_dir2_data.c

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ __xfs_dir3_data_check(
177177
while (offset < end) {
178178
struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
179179
struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
180+
unsigned int reclen;
181+
182+
/*
183+
* Are the remaining bytes large enough to hold an
184+
* unused entry?
185+
*/
186+
if (offset > end - xfs_dir2_data_unusedsize(1))
187+
return __this_address;
180188

181189
/*
182190
* If it's unused, look for the space in the bestfree table.
@@ -186,9 +194,13 @@ __xfs_dir3_data_check(
186194
if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
187195
xfs_failaddr_t fa;
188196

197+
reclen = xfs_dir2_data_unusedsize(
198+
be16_to_cpu(dup->length));
189199
if (lastfree != 0)
190200
return __this_address;
191-
if (offset + be16_to_cpu(dup->length) > end)
201+
if (be16_to_cpu(dup->length) != reclen)
202+
return __this_address;
203+
if (offset + reclen > end)
192204
return __this_address;
193205
if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) !=
194206
offset)
@@ -206,10 +218,18 @@ __xfs_dir3_data_check(
206218
be16_to_cpu(bf[2].length))
207219
return __this_address;
208220
}
209-
offset += be16_to_cpu(dup->length);
221+
offset += reclen;
210222
lastfree = 1;
211223
continue;
212224
}
225+
226+
/*
227+
* This is not an unused entry. Are the remaining bytes
228+
* large enough for a dirent with a single-byte name?
229+
*/
230+
if (offset > end - xfs_dir2_data_entsize(mp, 1))
231+
return __this_address;
232+
213233
/*
214234
* It's a real entry. Validate the fields.
215235
* If this is a block directory then make sure it's
@@ -218,9 +238,10 @@ __xfs_dir3_data_check(
218238
*/
219239
if (dep->namelen == 0)
220240
return __this_address;
221-
if (!xfs_verify_dir_ino(mp, be64_to_cpu(dep->inumber)))
241+
reclen = xfs_dir2_data_entsize(mp, dep->namelen);
242+
if (offset + reclen > end)
222243
return __this_address;
223-
if (offset + xfs_dir2_data_entsize(mp, dep->namelen) > end)
244+
if (!xfs_verify_dir_ino(mp, be64_to_cpu(dep->inumber)))
224245
return __this_address;
225246
if (be16_to_cpu(*xfs_dir2_data_entry_tag_p(mp, dep)) != offset)
226247
return __this_address;
@@ -244,7 +265,7 @@ __xfs_dir3_data_check(
244265
if (i >= be32_to_cpu(btp->count))
245266
return __this_address;
246267
}
247-
offset += xfs_dir2_data_entsize(mp, dep->namelen);
268+
offset += reclen;
248269
}
249270
/*
250271
* Need to have seen all the entries and all the bestfree slots.

fs/xfs/libxfs/xfs_dir2_priv.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ void xfs_dir2_sf_put_ftype(struct xfs_mount *mp,
187187
extern int xfs_readdir(struct xfs_trans *tp, struct xfs_inode *dp,
188188
struct dir_context *ctx, size_t bufsize);
189189

190+
static inline unsigned int
191+
xfs_dir2_data_unusedsize(
192+
unsigned int len)
193+
{
194+
return round_up(len, XFS_DIR2_DATA_ALIGN);
195+
}
196+
190197
static inline unsigned int
191198
xfs_dir2_data_entsize(
192199
struct xfs_mount *mp,

0 commit comments

Comments
 (0)