can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: CFFILE table offset is out of bounds

Error message

Invalid CAB archive: CFFILE table offset is out of bounds

What it means

The CFHEADER's coffFiles field (offset 16) points to the start of the CFFILE table. The library requires it to fall within the cabinet: at or after the fixed header and at or before the declared cabinet size. An out-of-range offset means the header's internal layout is inconsistent.

Source

Thrown at packages/utils/src/ar/cab.ts:256

		}
		return folder.slice(this.#offset, end);
	}
}

async function readCabArchive(source: ByteSource, options: Parameters<FormatReader>[1]): Promise<ArchiveIndexEntry[]> {
	if (source.size < FIXED_HEADER_SIZE) throw new ArchiveError("Invalid CAB archive: truncated CFHEADER");
	const fixed = await readExact(source, 0, FIXED_HEADER_SIZE);
	if (!hasSignature(fixed)) throw new ArchiveError(`Invalid CAB archive: expected ${CAB_SIGNATURE} signature`);
	if (readUInt32LE(fixed, 4) !== 0 || readUInt32LE(fixed, 12) !== 0 || readUInt32LE(fixed, 20) !== 0) {
		throw new ArchiveError("Invalid CAB archive: reserved CFHEADER fields must be zero");
	}
	const cabinetSize = readUInt32LE(fixed, 8);
	if (cabinetSize < FIXED_HEADER_SIZE || cabinetSize > source.size) {
		throw new ArchiveError("Invalid CAB archive: declared cabinet size is out of bounds");
	}
	const fileTableOffset = readUInt32LE(fixed, 16);
	if (fileTableOffset < FIXED_HEADER_SIZE || fileTableOffset > cabinetSize) {
		throw new ArchiveError("Invalid CAB archive: CFFILE table offset is out of bounds");
	}
	if (fixed[24] !== 3 || fixed[25] !== 1) {
		throw new ArchiveError(`Unsupported CAB format version ${fixed[25]}.${fixed[24]} (expected 1.3)`);
	}
	const folderCount = readUInt16LE(fixed, 26);
	const fileCount = readUInt16LE(fixed, 28);
	const flags = readUInt16LE(fixed, 30);
	if (flags & 0x0003) throw new ArchiveError("Unsupported multi-volume CAB archive (previous/next cabinet link)");
	assertEntryCount(folderCount + fileCount, options.limits);
	if (folderCount === 0 && fileCount !== 0)
		throw new ArchiveError("Invalid CAB archive: files exist without a folder");

	let headerReserveSize = 0;
	let folderReserveSize = 0;
	let dataReserveSize = 0;
	let folderTableOffset = FIXED_HEADER_SIZE;
	if (flags & 0x0004) {
		const reserveHeader = await readExact(source, FIXED_HEADER_SIZE, FIXED_HEADER_SIZE + 4, cabinetSize);

View on GitHub (pinned to 9690622007)

Solutions

  1. Treat the archive as corrupt and obtain a fresh copy; the library will not follow an untrusted offset.
  2. Hex-dump bytes 16-19 and 8-11 and verify coffFiles lies between 36 and cbCabinet.
  3. If a custom packer produced the file, fix its coffFiles computation to point at the first CFFILE entry.
  4. Validate the cabinet with cabextract or 7-Zip to confirm the header is genuinely inconsistent.
Defensive patterns

Strategy: validation

Validate before calling

const buf = new Uint8Array(await Bun.file(path).arrayBuffer());
const coffFiles = (buf[16] | (buf[17]! << 8) | (buf[18]! << 16) | (buf[19]! << 24)) >>> 0;
const cbCabinet = (buf[8] | (buf[9]! << 8) | (buf[10]! << 16) | (buf[11]! << 24)) >>> 0;
if (coffFiles < 36 || coffFiles > cbCabinet) throw new Error("CAB CFFILE offset out of range; archive is corrupt");

Try / catch

try {
	const entries = await readCab(source);
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("CFFILE table offset is out of bounds")) {
		logger.warn("Skipping CAB with inconsistent file-table offset", { path });
		return null;
	}
	throw err;
}

Prevention

When it happens

Trigger: Calling readCab() on a signed CAB whose first-file-table offset (bytes 16-19) is smaller than 36 or greater than cbCabinet — produced by corruption, a faulty writer, or deliberate malformation.

Common situations: Cabinets produced by non-conforming or buggy custom packers, files damaged by byte-level corruption, and hand-crafted or fuzzed inputs where the file-table pointer was miswritten.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9a531398dfa6686e. Report an issue: GitHub.