can1357/oh-my-pi · error · ArchiveError

Invalid RPM package: tag ${tag} points outside header data

Error message

Invalid RPM package: tag ${tag} points outside header data

What it means

ArchiveError thrown by validateHeaderBody() while iterating index entries: an entry's data offset exceeds intro.dataSize, meaning the tag claims to point outside the header's data area. Prevents out-of-bounds reads when decoding tag values.

Source

Thrown at packages/utils/src/ar/rpm.ts:81

	const dataSize = readUInt32BE(bytes, 12);
	assertEntryCount(indexCount, options.limits);
	const indexSize = indexCount * RPM_INDEX_ENTRY_SIZE;
	const bodySize = indexSize + dataSize;
	if (!Number.isSafeInteger(bodySize)) throw new ArchiveError(`Invalid RPM package: ${what} header is too large`);
	assertIndexSize(RPM_HEADER_INTRO_SIZE + bodySize, options.limits, `RPM ${what} header`);
	return { indexCount, dataSize, bodySize, totalSize: RPM_HEADER_INTRO_SIZE + bodySize };
}

function validateHeaderBody(body: Uint8Array, intro: HeaderIntro, what: string): void {
	const indexSize = intro.indexCount * RPM_INDEX_ENTRY_SIZE;
	if (body.byteLength !== intro.bodySize) throw new ArchiveError(`Invalid RPM package: truncated ${what} header`);
	for (let index = 0; index < intro.indexCount; index++) {
		const recordOffset = index * RPM_INDEX_ENTRY_SIZE;
		const tag = readUInt32BE(body, recordOffset);
		const type = readUInt32BE(body, recordOffset + 4);
		const offset = readUInt32BE(body, recordOffset + 8);
		const count = readUInt32BE(body, recordOffset + 12);
		if (offset > intro.dataSize) throw new ArchiveError(`Invalid RPM package: tag ${tag} points outside header data`);
		const remaining = intro.dataSize - offset;
		let elementSize = 0;
		if (type === 1 || type === 2 || type === 7) elementSize = 1;
		else if (type === 3) elementSize = 2;
		else if (type === 4) elementSize = 4;
		else if (type === 5) elementSize = 8;
		else if (type === 0) {
			if (count !== 0) throw new ArchiveError(`Invalid RPM package: null tag ${tag} has values`);
			continue;
		} else if (type === RPM_TYPE_STRING || type === 8 || type === 9) {
			const stringCount = type === RPM_TYPE_STRING ? 1 : count;
			if (type === RPM_TYPE_STRING && count !== 1) {
				throw new ArchiveError(`Invalid RPM package: string tag ${tag} has an invalid count`);
			}
			if (stringCount > remaining) {
				throw new ArchiveError(`Invalid RPM package: string tag ${tag} exceeds header data`);
			}
			let cursor = indexSize + offset;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download the package from a trusted mirror
  2. Verify package checksum/signature before parsing
  3. If you build RPMs programmatically, ensure index offsets stay within dataSize

Example fix

// before
// trusting offsets from an untrusted header
await readRpmArchive(buffer);
// after
assertRpmChecksumMatches(buffer); // verify integrity first
await readRpmArchive(buffer);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!await verifyRpmSignature(path)) throw new Error('untrusted rpm: bad signature');

Type guard

null

Try / catch

try {
  const rpm = await readRpmArchive(buf);
} catch (err) {
  if (err instanceof ArchiveError && /tag \d+ points outside header data/.test(err.message)) {
    // reject as corrupt/forged header
  } else throw err;
}

Prevention

When it happens

Trigger: Header index entry whose offset field (readUInt32BE at recordOffset+8) is greater than dataSize — corrupt or forged header entries.

Common situations: Corrupted downloads, hand-modified packages, fuzzed/malicious RPMs designed to cause OOB reads.

Related errors


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