fix(ntfs): reject malformed MFT update sequence arrays

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
N0zoM1z0andgensokyo-dev authored and gensokyo-dev committed 2026-11-11 10:03:35 +08:00
1 parent 9df3f7174f
commit d7338876d6
2 files changed
+26 -3

No files matched your search

+22
View File
@@ -143,6 +143,28 @@ describe("parseMftImage", () => {
);
});
it.each([
[48, 4],
[49, 3],
[508, 3],
])(
"rejects a malformed update sequence array at %i with %i entries",
(offset, count) => {
const bytes = recordOf();
const view = new DataView(bytes.buffer);
view.setUint16(4, offset, true);
view.setUint16(6, count, true);
const parsed = parseMftImage(bytes);
if (!parsed.ok) throw new Error(parsed.error.message);
expect(parsed.value.records[0]?.fixups).toMatchObject({
malformed: true,
verifiedSectors: 0,
});
expect(parsed.value.metadata.fixup_unverified_record_count).toBe(1);
},
);
it("reports an array that covers fewer sectors than the record has", () => {
// A 4 KiB record holds eight sectors; a three-entry array leaves five unverified.
const parsed = parseMftImage(
+4 -3
View File
@@ -323,13 +323,14 @@ const verifyFixups = (record: Uint8Array, view: DataView): MftRecordFixups => {
const usaCount = view.getUint16(RECORD_HEADER.updateSequenceCount, true);
const sectors = Math.floor(record.length / FIXUP_SECTOR_BYTES);
// Entry 0 holds the sequence number, so sector n's saved trailer is entry n+1.
const coveredSectors = Math.min(sectors, Math.max(0, usaCount - 1));
const coveredSectors = Math.max(0, usaCount - 1);
if (
usaCount < USA_MIN_ENTRIES ||
coveredSectors > sectors ||
usaOffset < RECORD_HEADER_BYTES ||
usaOffset + usaCount * 2 > record.length ||
coveredSectors === 0
usaOffset % 2 !== 0 ||
usaOffset + usaCount * 2 > FIXUP_SECTOR_BYTES - 2
)
return {
sequenceNumber: 0,