can1357/oh-my-pi · error · ArchiveError

Invalid CPIO archive: name size must include a NUL terminato

Error message

Invalid CPIO archive: name size must include a NUL terminator

What it means

Every CPIO member name field must contain at least the terminating NUL byte, so header nameSize must be >= 1; the library throws ArchiveError otherwise. A zero nameSize is structurally impossible in conforming archives (even the 'TRAILER!!!' entry has nameSize 11) and would make the name/data offset math nonsensical, so it is rejected as corruption or a crafted header.

Source

Thrown at packages/utils/src/ar/cpio.ts:227

	if (path.posix.isAbsolute(portableTarget)) return { path: portableTarget, resolveTarget: false };
	const normalized = normalizeArchiveLookupPath(path.posix.join(path.posix.dirname(recordPath), portableTarget));
	return normalized === undefined
		? { path: portableTarget, resolveTarget: false }
		: { path: normalized, resolveTarget: true };
}

/** Parse an already-materialized CPIO stream for direct and RPM-composed readers. */
export function readCpioEntriesFromBuffer(bytes: Uint8Array, options: FormatReadOptions): ArchiveIndexEntry[] {
	assertInMemorySize(bytes.byteLength, options.limits);
	const records: ParsedRecord[] = [];
	let offset = 0;
	let metadataSize = 0;
	let foundTrailer = false;

	while (offset < bytes.byteLength) {
		const header = parseHeader(bytes, offset);
		if (header.mode > 0xffff) throw new ArchiveError("Invalid CPIO archive: mode exceeds 16 bits");
		if (header.nameSize < 1) throw new ArchiveError("Invalid CPIO archive: name size must include a NUL terminator");
		assertArchivePathBytes(header.nameSize - 1, "member path", options.limits.maxPathBytes);
		assertArchiveMemberSize(header.fileSize, "(CPIO entry)", options.limits);

		const nameStart = offset + header.headerSize;
		const nameEnd = nameStart + header.nameSize;
		const dataOffset = align(nameEnd, header.alignment);
		const dataEnd = dataOffset + header.fileSize;
		const nextOffset = align(dataEnd, header.alignment);
		requireRange(bytes, nameStart, nameEnd, "member name");
		requireRange(bytes, dataOffset, dataEnd, "member data");
		requireRange(bytes, dataEnd, nextOffset, "member padding");
		if (bytes[nameEnd - 1] !== 0) throw new ArchiveError("Invalid CPIO archive: member name is not NUL-terminated");
		for (let index = nameStart; index < nameEnd - 1; index++) {
			if (bytes[index] === 0) throw new ArchiveError("Invalid CPIO archive: member name contains an embedded NUL");
		}
		validateZeroPadding(bytes, nameEnd, dataOffset, "name");
		validateZeroPadding(bytes, dataEnd, nextOffset, "data");

View on GitHub (pinned to 9690622007)

Solutions

  1. Regenerate the archive with standard cpio tooling; nameSize must include the trailing NUL
  2. Fix a custom writer to write name bytes plus a NUL and set nameSize = nameBytes.length + 1
  3. Verify the archive against a checksum to distinguish corruption from a hostile input and reject untrusted sources
  4. Locate the offending entry by scanning headers for the zero name-size field before ingestion

Example fix

// before: writer omits NUL from size
const name = Buffer.from(entryPath);
header.nameSize = name.length;
// after: include the NUL terminator
const name = Buffer.from(entryPath + '\0');
header.nameSize = name.length;
Defensive patterns

Strategy: validation

Validate before calling

// pre-check first header before full parse
const head = Buffer.from(new Uint8Array(await source.slice(0, 110)));
const nameSize = parseInt(head.toString('ascii', 6 + 11 * 8, 6 + 12 * 8), 16);
if (nameSize < 1) throw new Error('header nameSize must be >= 1');

Try / catch

try {
  const entries = await readCpio(source, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('name size must include')) {
    // fix writer to count the NUL or reject the corrupt archive
  } else throw err;
}

Prevention

When it happens

Trigger: Parsing an archive whose header declares nameSize = 0 — from a broken writer, corruption zeroing the field, or a crafted archive (newc hex field '00000000', odc octal '000000').

Common situations: Malicious/fuzzed archives in ingestion pipelines; truncated or bit-rotted archives; buggy custom archive writers that forget to count the NUL terminator in nameSize.

Related errors


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