mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ntfs: fix kunmap_local() of advanced pointers in check_mft_mirror()
check_mft_mirror() compares the mft records of a page with the mirror
records by advancing kmft and kmirr by the record size for every record.
It then passes the advanced pointers to kunmap_local(). After the last
record of a page they point one page past the mapping.
With HIGHMEM kunmap_local() warns that the address does not match the
top of the kmap_local stack. What it does next depends on how the
architecture finds the page table entry:
- 32-bit ARM looks the entry up by that address. It clears the entry of
the neighbouring fixmap slot and leaves the real mapping in place.
The next kmap_local() on that CPU that reaches the slot hits the
BUG_ON(!pte_none()) in __kmap_local_pfn_prot().
- x86-32 takes the entry from the stack index, so the wrong address
does not select a wrong entry. In QEMU the mount carried on after the
warning.
With 4 KiB pages and 1 KiB mft records the four mirror records fill
exactly one page, so every mount whose $MFT and $MFTMirr folios are in
highmem hits it. A fresh mkntfs volume on 32-bit ARM
(multi_v7_defconfig, 256 MiB of highmem) fails on the first mount:
WARNING: mm/highmem.c:623 at kunmap_local_indexed+0x254/0x26c, CPU#1: mount/85
kunmap_local_indexed from ntfs_fill_super+0x224c/0x35a0
ntfs_fill_super from get_tree_bdev_flags+0x1d8/0x2a4
ntfs: volume version 3.1, dev loop0, cluster size 4096
kernel BUG at mm/highmem.c:565!
PC is at __kmap_local_pfn_prot+0x210/0x214
__kmap_local_page_prot from ntfs_check_logfile+0x1fc/0x1210
Found with syzkaller. Kernels without HIGHMEM do not show it.
kmap_local_folio() returns the direct map address there and
kunmap_local() has no mapping to remove.
Keep the addresses returned by kmap_local_folio() and unmap with those.
Fixes: 6251f0b0de ("ntfs: update super block operations")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
c10e0c3769
commit
16fcb1c446
|
|
@ -1132,7 +1132,7 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
struct super_block *sb = vol->sb;
|
||||
struct ntfs_inode *mirr_ni;
|
||||
struct folio *mft_folio = NULL, *mirr_folio = NULL;
|
||||
u8 *kmft = NULL, *kmirr = NULL;
|
||||
u8 *kmft = NULL, *kmirr = NULL, *kmft_base = NULL, *kmirr_base = NULL;
|
||||
struct runlist_element *rl, rl2[2];
|
||||
pgoff_t index;
|
||||
int mrecs_per_page, i;
|
||||
|
|
@ -1147,9 +1147,9 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
/* Switch pages if necessary. */
|
||||
if (!(i % mrecs_per_page)) {
|
||||
if (index) {
|
||||
kunmap_local(kmirr);
|
||||
kunmap_local(kmirr_base);
|
||||
folio_put(mirr_folio);
|
||||
kunmap_local(kmft);
|
||||
kunmap_local(kmft_base);
|
||||
folio_put(mft_folio);
|
||||
}
|
||||
/* Get the $MFT page. */
|
||||
|
|
@ -1159,7 +1159,8 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
ntfs_error(sb, "Failed to read $MFT.");
|
||||
return false;
|
||||
}
|
||||
kmft = kmap_local_folio(mft_folio, 0);
|
||||
kmft_base = kmap_local_folio(mft_folio, 0);
|
||||
kmft = kmft_base;
|
||||
/* Get the $MFTMirr page. */
|
||||
mirr_folio = read_mapping_folio(vol->mftmirr_ino->i_mapping,
|
||||
index, NULL);
|
||||
|
|
@ -1167,7 +1168,8 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
ntfs_error(sb, "Failed to read $MFTMirr.");
|
||||
goto mft_unmap_out;
|
||||
}
|
||||
kmirr = kmap_local_folio(mirr_folio, 0);
|
||||
kmirr_base = kmap_local_folio(mirr_folio, 0);
|
||||
kmirr = kmirr_base;
|
||||
++index;
|
||||
}
|
||||
|
||||
|
|
@ -1179,10 +1181,10 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
"Incomplete multi sector transfer detected in mft record %i.",
|
||||
i);
|
||||
mm_unmap_out:
|
||||
kunmap_local(kmirr);
|
||||
kunmap_local(kmirr_base);
|
||||
folio_put(mirr_folio);
|
||||
mft_unmap_out:
|
||||
kunmap_local(kmft);
|
||||
kunmap_local(kmft_base);
|
||||
folio_put(mft_folio);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1218,9 +1220,9 @@ static bool check_mft_mirror(struct ntfs_volume *vol)
|
|||
kmirr += vol->mft_record_size;
|
||||
} while (++i < vol->mftmirr_size);
|
||||
/* Release the last folios. */
|
||||
kunmap_local(kmirr);
|
||||
kunmap_local(kmirr_base);
|
||||
folio_put(mirr_folio);
|
||||
kunmap_local(kmft);
|
||||
kunmap_local(kmft_base);
|
||||
folio_put(mft_folio);
|
||||
|
||||
/* Construct the mft mirror runlist by hand. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user