ruvnet/ruflo · error

Section " " has negative offset or size

Error message

Section "${sec.id}" has negative offset or size

What it means

During fromBuffer's per-section bounds pass, a section in the parsed header declared a negative offset or negative size. Offsets/sizes are unsigned quantities by contract; validateHeader only checks they are numbers, so this loop is the explicit sanity gate before any subarray call. A negative value means the header was deliberately malformed (integer-overflow/underflow style payload) or randomly corrupted.

Solutions

  1. Do not retry or 'fix' the image — a negative offset/size means the header is malformed or malicious; discard the file and re-fetch from a trusted source
  2. Verify the image out-of-band: the 32-byte footer hash and detached Ed25519 signature (rvfa-signing.ts) should be checked before parsing untrusted images
  3. If it's your own writer, audit addSection/build for anything that could compute a negative offset (e.g. subtracting a larger padding from a smaller base)
  4. Add a pre-parse rejection for untrusted inputs using the signing verification APIs before RvfaReader.fromBuffer

Example fix

// before — parse untrusted bytes directly
const reader = await RvfaReader.fromFile(untrustedPath);

// after — verify signature first, then parse
const ok = await verifyFile(untrustedPath, trustedPublicKey);
if (!ok) throw new Error('image signature invalid — refusing to parse');
const reader = await RvfaReader.fromFile(untrustedPath);
Defensive patterns

Strategy: validation

Validate before calling

const parsed = JSON.parse(buf.subarray(12, 12 + buf.readUInt32LE(8)).toString('utf8'));
const sane = (parsed.sections ?? []).every(
  (s: any) => Number.isInteger(s?.offset) && s.offset >= 0 &&
           Number.isInteger(s?.size) && s.size >= 0,
);
if (!sane) throw new Error('malformed section table — treat as tampered');

Type guard

function hasSaneSections(h: unknown): h is { sections: Array<{ offset: number; size: number }> } {
  if (typeof h !== 'object' || h === null || !Array.isArray((h as any).sections)) return false;
  return (h as any).sections.every(
    (s: any) => Number.isInteger(s?.offset) && s.offset >= 0 &&
             Number.isInteger(s?.size) && s.size >= 0,
  );
}

Try / catch

try { reader = RvfaReader.fromBuffer(buf); }
catch (e) {
  if (/negative offset or size/.test(String((e as Error).message))) {
    // tamper signal: quarantine the file, alert — do not retry
  }
  throw e;
}

Prevention

When it happens

Trigger: RvfaReader.fromBuffer on a hostile or corrupted image where any header.sections[] entry has offset < 0 or size < 0 — e.g. a fuzzed .rvfa downloaded from an untrusted source, or a header crafted to make sec.offset + sec.size overflow/wrap past the buffer-end check that follows.

Common situations: Security testing / fuzzing feeds of appliance images; corrupted headers after bit-rot; untrusted mirrors serving modified images. Note the subsequent check (offset + size > totalSize - 32) can be bypassed with large unsigned values that wrap, so treat this error as a tamper signal, not a fluke.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/ce131978ee95f895. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/appliance/rvfa-format.ts:358

    // Parse header JSON
    const headerSlice = buf.subarray(PREAMBLE_SIZE, PREAMBLE_SIZE + headerLen);
    let parsed: unknown;
    try {
      parsed = JSON.parse(headerSlice.toString('utf-8'));
    } catch {
      throw new Error('Failed to parse RVFA header JSON');
    }

    if (!validateHeader(parsed)) {
      throw new Error('RVFA header failed validation');
    }
    const header = parsed as RvfaHeader;

    // Bounds-check every section offset
    const totalSize = buf.length;
    for (const sec of header.sections) {
      if (sec.offset < 0 || sec.size < 0) {
        throw new Error(`Section "${sec.id}" has negative offset or size`);
      }
      if (sec.offset + sec.size > totalSize - SHA256_SIZE) {
        throw new Error(
          `Section "${sec.id}" extends beyond buffer ` +
            `(offset=${sec.offset}, size=${sec.size}, bufLen=${totalSize})`,
        );
      }
    }

    // Check for overlapping sections
    const sorted = [...header.sections].sort((a, b) => a.offset - b.offset);
    for (let i = 1; i < sorted.length; i++) {
      const prev = sorted[i - 1];
      const curr = sorted[i];
      if (prev.offset + prev.size > curr.offset) {
        throw new Error(
          `Sections "${prev.id}" and "${curr.id}" overlap ` +
            `(${prev.offset}+${prev.size} > ${curr.offset})`,

View on GitHub (pinned to fa13ee4ad6)