Skip to content

Commit bb11485

Browse files
fs/ntfs3: add bounds check to run_get_highest_vcn()
run_get_highest_vcn() parses a packed NTFS mapping-pairs buffer without any length bound, relying solely on a 0x00 terminator to stop. A crafted $LogFile UpdateMappingPairs record whose embedded attribute contains mapping-pairs runs without a terminator causes the function to read past the slab allocation, triggering a KASAN slab-out-of-bounds read on mount. The sibling function run_unpack() received an analogous bounds-check in commit b62567b ("ntfs3: add buffer boundary checks to run_unpack()"), but run_get_highest_vcn() was missed. Take a run_buf_size parameter and reject any run header whose payload would extend past the buffer end, mirroring the pattern used by run_unpack(). The caller in fslog.c passes the remaining attribute bytes after the mapping-pairs offset. KASAN report (on mainline v7.1 merge window HEAD): BUG: KASAN: slab-out-of-bounds in run_get_highest_vcn+0x3c0/0x410 Read of size 1 at addr ffff88800e2d5400 by task mount/72 Call Trace: run_get_highest_vcn+0x3c0/0x410 do_action.isra.0+0x3ba8/0x7b50 log_replay+0x9ddd/0x10200 ntfs_loadlog_and_replay+0x4ad/0x610 ntfs_fill_super+0x214a/0x4540 Fixes: b62567b ("ntfs3: add buffer boundary checks to run_unpack()") Signed-off-by: Jaeyeong Lee <lee@jaeyeong.cc> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
1 parent f1df9d7 commit bb11485

3 files changed

Lines changed: 13 additions & 4 deletions

File tree

fs/ntfs3/fslog.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3368,7 +3368,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
33683368
memmove(Add2Ptr(attr, aoff), data, dlen);
33693369

33703370
if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
3371-
attr_run(attr), &t64)) {
3371+
attr_run(attr),
3372+
le32_to_cpu(attr->size) -
3373+
le16_to_cpu(attr->nres.run_off),
3374+
&t64)) {
33723375
goto dirty_vol;
33733376
}
33743377

fs/ntfs3/ntfs_fs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
877877
#else
878878
#define run_unpack_ex run_unpack
879879
#endif
880-
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
880+
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
881+
u64 *highest_vcn);
881882
int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
882883
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done);
883884
CLST run_len(const struct runs_tree *run);

fs/ntfs3/run.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,18 +1205,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
12051205
* Return the highest vcn from a mapping pairs array
12061206
* it used while replaying log file.
12071207
*/
1208-
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn)
1208+
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
1209+
u64 *highest_vcn)
12091210
{
1211+
const u8 *run_last = run_buf + run_buf_size;
12101212
u64 vcn64 = vcn;
12111213
u8 size_size;
12121214

1213-
while ((size_size = *run_buf & 0xF)) {
1215+
while (run_buf < run_last && (size_size = *run_buf & 0xF)) {
12141216
u8 offset_size = *run_buf++ >> 4;
12151217
u64 len;
12161218

12171219
if (size_size > 8 || offset_size > 8)
12181220
return -EINVAL;
12191221

1222+
if (run_buf + size_size + offset_size > run_last)
1223+
return -EINVAL;
1224+
12201225
len = run_unpack_s64(run_buf, size_size, 0);
12211226
if (!len)
12221227
return -EINVAL;

0 commit comments

Comments
 (0)