mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
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 commitb62567bca4("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:b62567bca4("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>
This commit is contained in:
parent
f1df9d771d
commit
bb11485a87
|
|
@ -3368,7 +3368,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
|
|||
memmove(Add2Ptr(attr, aoff), data, dlen);
|
||||
|
||||
if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
|
||||
attr_run(attr), &t64)) {
|
||||
attr_run(attr),
|
||||
le32_to_cpu(attr->size) -
|
||||
le16_to_cpu(attr->nres.run_off),
|
||||
&t64)) {
|
||||
goto dirty_vol;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -877,7 +877,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
|
|||
#else
|
||||
#define run_unpack_ex run_unpack
|
||||
#endif
|
||||
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
|
||||
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
|
||||
u64 *highest_vcn);
|
||||
int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
|
||||
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done);
|
||||
CLST run_len(const struct runs_tree *run);
|
||||
|
|
|
|||
|
|
@ -1205,18 +1205,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
|
|||
* Return the highest vcn from a mapping pairs array
|
||||
* it used while replaying log file.
|
||||
*/
|
||||
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn)
|
||||
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
|
||||
u64 *highest_vcn)
|
||||
{
|
||||
const u8 *run_last = run_buf + run_buf_size;
|
||||
u64 vcn64 = vcn;
|
||||
u8 size_size;
|
||||
|
||||
while ((size_size = *run_buf & 0xF)) {
|
||||
while (run_buf < run_last && (size_size = *run_buf & 0xF)) {
|
||||
u8 offset_size = *run_buf++ >> 4;
|
||||
u64 len;
|
||||
|
||||
if (size_size > 8 || offset_size > 8)
|
||||
return -EINVAL;
|
||||
|
||||
if (run_buf + size_size + offset_size > run_last)
|
||||
return -EINVAL;
|
||||
|
||||
len = run_unpack_s64(run_buf, size_size, 0);
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user