[GH-ISSUE #6793] [Bug] Rust dependency inspection rejects PE alignment padding when VirtualSize is zero #4145

Closed
opened 2026-10-31 00:47:09 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6793

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

inspect-rust-binary rejects a synthetic PE32+ .dep-v0 section containing a complete 92-byte zlib stream followed by zero alignment padding when VirtualSize is zero and SizeOfRawData is 512. It exits 1 with invalid_request, reason invalid_format, and Dependency section is not a complete zlib stream.

Changing only VirtualSize to 92 returns the package metadata, exit 0. Ordinary Node zlib decompression of the original file's 512-byte raw section recovers the same package without changing the artifact.

Reproduction

With locked dependencies and the compiled CLI available at the revision below, run this from the checkout root. The fixtures are inert and never executed. Each CLI case was run once in the reported matrix.

node --input-type=module <<'NODE'
import { deflateSync } from 'node:zlib';
import { mkdirSync, writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
const root='/tmp/rea-repro';
mkdirSync(root+'/home',{recursive:true});
const payload=deflateSync(Buffer.from(JSON.stringify({format:1,packages:[
  {name:'padding_sample',version:'0.1.0',source:'local',root:true}
]})));
const b=Buffer.alloc(1024);
const w16=(o,n)=>b.writeUInt16LE(n,o),w32=(o,n)=>b.writeUInt32LE(n,o);
b.write('MZ');w32(60,128);b.write('PE\0\0',128);
w16(132,0x8664);w16(134,1);w16(148,112);w16(150,0x22);
w16(152,0x20b);b.writeBigUInt64LE(0x140000000n,176);
w32(184,4096);w32(188,512);w32(208,8192);w32(212,512);w16(220,3);
b.write('.dep-v0',264);w32(276,4096);w32(280,512);
w32(284,512);w32(300,0x40000040);payload.copy(b,512);
const env={PATH:process.env.PATH,HOME:root+'/home',
  XDG_CONFIG_HOME:root+'/home/config',XDG_CACHE_HOME:root+'/home/cache',
  REA_LOG_LEVEL:'silent'};
for(const [name,virtualSize] of [['exact',payload.length],['zero',0]]){
  w32(272,virtualSize);
  const path=root+'/'+name+'.exe';writeFileSync(path,b);
  const r=spawnSync(process.execPath,
    ['scripts/rea.mjs','inspect-rust-binary',path,'--json'],
    {env,encoding:'utf8',timeout:30000});
  const result=JSON.parse(r.stdout);
  console.log(JSON.stringify({name,virtualSize,exit:r.status,
    packages:result.normalized_result?.auditable_metadata?.packages,
    failure:result.details?.issues}));
}
NODE

Expected behavior

Recover the dependency records from this explicitly supported PE raw-size fallback while preserving the selected artifact's identity. PE file-alignment padding should be distinguished from unexpected bytes in the metadata payload.

The native section reader explicitly uses raw size when VirtualSize is zero. The Rust padding test requires exclusion of PE file-alignment padding, but exercises only exact nonzero VirtualSize.

Qualification: documented trailing-zlib-byte rejection is intentional. This report concerns its interaction with the container's zero-size fallback; it does not establish that arbitrary payload suffixes should be accepted. Released cargo-auditable parsing guidance directs section extraction and zlib decompression, and its binfarce PE reader extracts raw size. Those upstream implementations were source-inspected, not runtime-tested here.

Impact and scope

The CLI returns no Rust dependency metadata for the reproduced zero-VirtualSize PE shape. The input remains unchanged, and manually inflating its raw section recovers the package records. The exact-size control succeeds.

Only small synthetic PE32+ files were exercised. Occurrence in released cargo-auditable/linker output, PE32, Windows-host behavior, and MCP projection remain unverified. No severe harm or broad workflow failure was observed.

REA version

6.7.0; source revision bd4f5e9c197ba942ed026c9fed6ff3fae663901d.

Runtime and platform

Node.js v24.22.1; Linux x64. No Rust compiler or external analysis provider was used.

Target and artifact context

Two self-authored 1024-byte PE32+ x86-64 containers. Their only difference is the four-byte section VirtualSize field at offset 272. .dep-v0 starts at file offset 512, contains a 92-byte zlib stream, and has SizeOfRawData 512. Remaining raw bytes are zero; FileAlignment is 512. The embedded document contains one local package, padding_sample version 0.1.0, marked root. These are not compiler-produced programs.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed public CLI results, reduced:

exact: VirtualSize=92, SizeOfRawData=512, exit=0
packages=[{"name":"padding_sample","version":"0.1.0","source":"local","kind":null,"dependencies":[],"features":null,"root":true}]

zero: VirtualSize=0, SizeOfRawData=512, exit=1
code=invalid_request
issues[0].reason=invalid_format
issues[0].message=Selected artifact /tmp/rea-repro/zero.exe: Dependency section is not a complete zlib stream

A separate executed recovery read each original file, inflated bytes.subarray(512,1024) with Node inflateSync, and parsed the original package document successfully. This checks payload availability, not REA Evidence production.

Evidence provenance: observed public CLI behavior and source-derived cause. Local verification: reproduced on the stated synthetic inputs. Reproduction completeness: complete for those inputs; real-producer prevalence remains unknown.

Typed outcome or recovery expected

Return successful Rust metadata for the zero-size fallback with the package records preserved and the original file digest retained. Continue to reject genuinely malformed zlib streams and unsupported metadata. Precise container-padding recognition should remain owned at the container/metadata boundary.

Analysis or suspected cause

Source-derived: the PE section owner returns 512 content bytes for zero VirtualSize. RustBinaryImage passes that complete range to the strict inflater. With exact VirtualSize, the section owner trims to 92 bytes before inflation. The zero-size fallback loses the distinction between compressed content and alignment bytes.

Additional context

Related #5333 concerns Go build-info discovery skipping zero-VirtualSize data. This case reaches Rust section decoding and fails during inflation; current native tests already retain the zero-size raw range. The supplied open-issue inventory had no exact Rust-padding match; live open and closed search remains for submission.

The executed matrix also included a rounded nonzero VirtualSize; that variant is excluded from this claim because its zeros lie inside declared virtual content. No last-known-good version is established.

Triage assessment

  • Impact level: P2
  • Assessment status: provisional
  • Impact basis: Public CLI inspection fails to recover present dependency metadata for the tested raw-size-fallback PE shape, while an exact-size control succeeds. The existing PE padding contract supports treating this as a compatibility defect, but real released producer occurrence and the intended boundary of the trailing-byte policy remain unverified.
  • Workaround status: available
  • Workaround basis: Reading the original fixture's declared raw section and using Node inflateSync plus JSON.parse recovered its complete package document without changing the input; this manual recovery does not produce REA Evidence.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6793 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior `inspect-rust-binary` rejects a synthetic PE32+ `.dep-v0` section containing a complete 92-byte zlib stream followed by zero alignment padding when `VirtualSize` is zero and `SizeOfRawData` is 512. It exits 1 with `invalid_request`, reason `invalid_format`, and `Dependency section is not a complete zlib stream`. Changing only `VirtualSize` to 92 returns the package metadata, exit 0. Ordinary Node zlib decompression of the original file's 512-byte raw section recovers the same package without changing the artifact. ### Reproduction With locked dependencies and the compiled CLI available at the revision below, run this from the checkout root. The fixtures are inert and never executed. Each CLI case was run once in the reported matrix. ```sh node --input-type=module <<'NODE' import { deflateSync } from 'node:zlib'; import { mkdirSync, writeFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; const root='/tmp/rea-repro'; mkdirSync(root+'/home',{recursive:true}); const payload=deflateSync(Buffer.from(JSON.stringify({format:1,packages:[ {name:'padding_sample',version:'0.1.0',source:'local',root:true} ]}))); const b=Buffer.alloc(1024); const w16=(o,n)=>b.writeUInt16LE(n,o),w32=(o,n)=>b.writeUInt32LE(n,o); b.write('MZ');w32(60,128);b.write('PE\0\0',128); w16(132,0x8664);w16(134,1);w16(148,112);w16(150,0x22); w16(152,0x20b);b.writeBigUInt64LE(0x140000000n,176); w32(184,4096);w32(188,512);w32(208,8192);w32(212,512);w16(220,3); b.write('.dep-v0',264);w32(276,4096);w32(280,512); w32(284,512);w32(300,0x40000040);payload.copy(b,512); const env={PATH:process.env.PATH,HOME:root+'/home', XDG_CONFIG_HOME:root+'/home/config',XDG_CACHE_HOME:root+'/home/cache', REA_LOG_LEVEL:'silent'}; for(const [name,virtualSize] of [['exact',payload.length],['zero',0]]){ w32(272,virtualSize); const path=root+'/'+name+'.exe';writeFileSync(path,b); const r=spawnSync(process.execPath, ['scripts/rea.mjs','inspect-rust-binary',path,'--json'], {env,encoding:'utf8',timeout:30000}); const result=JSON.parse(r.stdout); console.log(JSON.stringify({name,virtualSize,exit:r.status, packages:result.normalized_result?.auditable_metadata?.packages, failure:result.details?.issues})); } NODE ``` ### Expected behavior Recover the dependency records from this explicitly supported PE raw-size fallback while preserving the selected artifact's identity. PE file-alignment padding should be distinguished from unexpected bytes in the metadata payload. The [native section reader](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/src/native/metadata/NativeSectionDirectory.ts#L280) explicitly uses raw size when `VirtualSize` is zero. The [Rust padding test](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/tests/boundary/rust/rustBinaryImage.test.ts#L107) requires exclusion of PE file-alignment padding, but exercises only exact nonzero `VirtualSize`. Qualification: [documented trailing-zlib-byte rejection](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/docs/rust-binaries.md#L129) is intentional. This report concerns its interaction with the container's zero-size fallback; it does not establish that arbitrary payload suffixes should be accepted. Released cargo-auditable [parsing guidance](https://github.com/rust-secure-code/cargo-auditable/blob/v0.7.7/PARSING.md#step-2-decompress-the-data) directs section extraction and zlib decompression, and its [binfarce PE reader](https://github.com/Shnatsel/binfarce/blob/v0.2.1/src/pe.rs#L41-L46) extracts raw size. Those upstream implementations were source-inspected, not runtime-tested here. ### Impact and scope The CLI returns no Rust dependency metadata for the reproduced zero-`VirtualSize` PE shape. The input remains unchanged, and manually inflating its raw section recovers the package records. The exact-size control succeeds. Only small synthetic PE32+ files were exercised. Occurrence in released cargo-auditable/linker output, PE32, Windows-host behavior, and MCP projection remain unverified. No severe harm or broad workflow failure was observed. ### REA version 6.7.0; source revision `bd4f5e9c197ba942ed026c9fed6ff3fae663901d`. ### Runtime and platform Node.js v24.22.1; Linux x64. No Rust compiler or external analysis provider was used. ### Target and artifact context Two self-authored 1024-byte PE32+ x86-64 containers. Their only difference is the four-byte section `VirtualSize` field at offset 272. `.dep-v0` starts at file offset 512, contains a 92-byte zlib stream, and has `SizeOfRawData` 512. Remaining raw bytes are zero; `FileAlignment` is 512. The embedded document contains one local package, `padding_sample` version `0.1.0`, marked root. These are not compiler-produced programs. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed public CLI results, reduced: ```text exact: VirtualSize=92, SizeOfRawData=512, exit=0 packages=[{"name":"padding_sample","version":"0.1.0","source":"local","kind":null,"dependencies":[],"features":null,"root":true}] zero: VirtualSize=0, SizeOfRawData=512, exit=1 code=invalid_request issues[0].reason=invalid_format issues[0].message=Selected artifact /tmp/rea-repro/zero.exe: Dependency section is not a complete zlib stream ``` A separate executed recovery read each original file, inflated `bytes.subarray(512,1024)` with Node `inflateSync`, and parsed the original package document successfully. This checks payload availability, not REA Evidence production. Evidence provenance: observed public CLI behavior and source-derived cause. Local verification: reproduced on the stated synthetic inputs. Reproduction completeness: complete for those inputs; real-producer prevalence remains unknown. ### Typed outcome or recovery expected Return successful Rust metadata for the zero-size fallback with the package records preserved and the original file digest retained. Continue to reject genuinely malformed zlib streams and unsupported metadata. Precise container-padding recognition should remain owned at the container/metadata boundary. ### Analysis or suspected cause Source-derived: the [PE section owner](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/src/native/metadata/NativeSectionDirectory.ts#L280) returns 512 content bytes for zero `VirtualSize`. [RustBinaryImage](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/src/rust/RustBinaryImage.ts#L111) passes that complete range to [the strict inflater](https://github.com/morluto/rea/blob/bd4f5e9c197ba942ed026c9fed6ff3fae663901d/src/rust/RustAuditableReader.ts#L127). With exact `VirtualSize`, the section owner trims to 92 bytes before inflation. The zero-size fallback loses the distinction between compressed content and alignment bytes. ### Additional context Related #5333 concerns Go build-info discovery skipping zero-`VirtualSize` data. This case reaches Rust section decoding and fails during inflation; current native tests already retain the zero-size raw range. The supplied open-issue inventory had no exact Rust-padding match; live open and closed search remains for submission. The executed matrix also included a rounded nonzero `VirtualSize`; that variant is excluded from this claim because its zeros lie inside declared virtual content. No last-known-good version is established. ## Triage assessment - Impact level: P2 - Assessment status: provisional - Impact basis: Public CLI inspection fails to recover present dependency metadata for the tested raw-size-fallback PE shape, while an exact-size control succeeds. The existing PE padding contract supports treating this as a compatibility defect, but real released producer occurrence and the intended boundary of the trailing-byte policy remain unverified. - Workaround status: available - Workaround basis: Reading the original fixture's declared raw section and using Node inflateSync plus JSON.parse recovered its complete package document without changing the input; this manual recovery does not produce REA Evidence.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4145
No description provided.