Skip to content

Handle padded archive name tables - #851

Merged
ltfish merged 1 commit into
masterfrom
feature/fix-c-276
Sep 30, 2026
Merged

ltfish merged 1 commit into
masterfrom
feature/fix-c-276

Conversation

@zardus

@zardus zardus commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Problem

Some valid static archives use slash-newline entries in their GNU filename table and pad the table with NUL bytes. arpy selects NUL separation whenever any NUL occurs, so a later /offset member fails with ArchiveFormatError: file references a name not present in the index.

The affected samples are unavailable. Their verified SHA-256 identifiers and observed changes are:

  • 0e712c71b3a16c8431e0d99fc4f0a3989744de05296a1793af72fb089a59c432: failed before; loads as an AMD64 StaticArchive with 77 members after.
  • 4da54dd0ee922a62a3fdd17d0c0c08cd164a4a30f847bf4c2ef88c33fec49ff8: failed before; loads as an AMD64 StaticArchive with 2 members after.

The exact inputs and reproducer are unavailable.

Root cause

arpy selects NUL separation whenever any NUL occurs in a GNU filename table. That misclassifies trailing padding as a delimiter and makes valid slash-newline entries after the first one unreachable by offset.

Fix

Recognize NULs as padding only when the unpadded table is unambiguously slash-newline-terminated. Continue to treat all other tables containing NULs as NUL-delimited, and populate both arpy's eager and lazy lookup fields.

Testing

The regression test uses an existing tracked GNU archive to cover ordinary long-name lookup without distributing unavailable bytes. Both affected campaign objects changed from the recorded ArchiveFormatError to successful loads. 6 recorded-success StaticArchive controls spanning RISCV64, AMD64, X86, PPC64, PPC32, and AARCH64 retained identical backend, architecture, and member counts; 0 became worse.

The four commands below were executed from the same private measurement directory. The confidentiality rule forbids publishing private paths and control identifiers, so PRIVATE_TARGET_MANIFEST, PRIVATE_CONTROL_MANIFEST, and PRIVATE_CONTROL_1 through PRIVATE_CONTROL_6 replace only those exact values. The command structure and every argument are otherwise unchanged.

J297_BEFORE=/nix/store/c6jb9fgv21zql616kcq5ypxj803wmisa-python3-3.12.13-env
J297_TARGET_MANIFEST=PRIVATE_TARGET_MANIFEST
"$J297_BEFORE/bin/python" -P measure_archives.py --manifest "$J297_TARGET_MANIFEST"

J297_BEFORE=/nix/store/c6jb9fgv21zql616kcq5ypxj803wmisa-python3-3.12.13-env
J297_REGRESSION_MANIFEST=PRIVATE_CONTROL_MANIFEST
"$J297_BEFORE/bin/python" -P measure_archives.py --manifest "$J297_REGRESSION_MANIFEST" --sha PRIVATE_CONTROL_1 --sha PRIVATE_CONTROL_2 --sha PRIVATE_CONTROL_3 --sha PRIVATE_CONTROL_4 --sha PRIVATE_CONTROL_5 --sha PRIVATE_CONTROL_6

/nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env/bin/python -P measure_archives.py --manifest PRIVATE_TARGET_MANIFEST

/nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env/bin/python -P measure_archives.py --manifest PRIVATE_CONTROL_MANIFEST --sha PRIVATE_CONTROL_1 --sha PRIVATE_CONTROL_2 --sha PRIVATE_CONTROL_3 --sha PRIVATE_CONTROL_4 --sha PRIVATE_CONTROL_5 --sha PRIVATE_CONTROL_6

Validation: #851 (comment)

session: sharpen

@zardus

zardus commented Sep 30, 2026 •

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Complete archive-loader result for both unavailable affected inputs, before and after this change.

Before — both archives fail while resolving a later GNU filename-table offset:

cle master 53d3d24
0e712c71b3a16c8431e0d99fc4f0a3989744de05296a1793af72fb089a59c432: arpy.ArchiveFormatError: file references a name not present in the index
4da54dd0ee922a62a3fdd17d0c0c08cd164a4a30f847bf4c2ef88c33fec49ff8: arpy.ArchiveFormatError: file references a name not present in the index

After — both archives load completely:

cle with this change 2591eed
0e712c71b3a16c8431e0d99fc4f0a3989744de05296a1793af72fb089a59c432: StaticArchive arch=AMD64 children=77
4da54dd0ee922a62a3fdd17d0c0c08cd164a4a30f847bf4c2ef88c33fec49ff8: StaticArchive arch=AMD64 children=2

@zardus

zardus commented Sep 30, 2026 •

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 2591eedc5de92e03c81e2121ba4b78c60d88d76d against baseline 53d3d2419cfbb8a8c8741e97440cc1a9d6462559.

Candidate validation

  • ./feature.sh build fix-c-276: passed; candidate environment /nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env.
  • ../../../../nix/run.sh --feature fix-c-276 -- pytest --import-mode=append -q tests/test_static_archive.py: passed.
  • Hosted-version merge-base pylint: cle/backends/static_archive.py improved from 9.67 to 9.81 with no W0201 diagnostics; tests/test_static_archive.py scored 10.00.
  • Merge-base pyright: the source retained the baseline's one pre-existing parent_object error and introduced none; the new test had zero errors.
  • ./feature.sh test fix-c-276: cle passed 277 tests with 9 skipped; angr passed 3,584 with 47 skipped and 2 xfailed. All family suites, package checks, pre-commit, feature lifecycle checks, Rust checks, and worktree cleanliness passed. angr-management did not run because this collection did not adopt it.
  • The full gate exited 1 for two unrelated angr-agentic checks: campaign tests reported 25 failures and 20 errors, and mono's consistency test found that its driver omits pysoot. Both failures repeated on prior full gate runs and inspect only angr-agentic files.

Dataset validation

The baseline environment was /nix/store/c6jb9fgv21zql616kcq5ypxj803wmisa-python3-3.12.13-env; the exact candidate environment was /nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env.

The four commands below were executed from the same private measurement directory. The confidentiality rule forbids publishing private paths and control identifiers, so PRIVATE_TARGET_MANIFEST, PRIVATE_CONTROL_MANIFEST, and PRIVATE_CONTROL_1 through PRIVATE_CONTROL_6 replace only those exact values. The command structure and every argument are otherwise unchanged.

J297_BEFORE=/nix/store/c6jb9fgv21zql616kcq5ypxj803wmisa-python3-3.12.13-env
J297_TARGET_MANIFEST=PRIVATE_TARGET_MANIFEST
"$J297_BEFORE/bin/python" -P measure_archives.py --manifest "$J297_TARGET_MANIFEST"

J297_BEFORE=/nix/store/c6jb9fgv21zql616kcq5ypxj803wmisa-python3-3.12.13-env
J297_REGRESSION_MANIFEST=PRIVATE_CONTROL_MANIFEST
"$J297_BEFORE/bin/python" -P measure_archives.py --manifest "$J297_REGRESSION_MANIFEST" --sha PRIVATE_CONTROL_1 --sha PRIVATE_CONTROL_2 --sha PRIVATE_CONTROL_3 --sha PRIVATE_CONTROL_4 --sha PRIVATE_CONTROL_5 --sha PRIVATE_CONTROL_6

/nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env/bin/python -P measure_archives.py --manifest PRIVATE_TARGET_MANIFEST

/nix/store/5hkdwc5mq2bxl6swbm11rvx6rd26kjd5-python3-3.12.13-env/bin/python -P measure_archives.py --manifest PRIVATE_CONTROL_MANIFEST --sha PRIVATE_CONTROL_1 --sha PRIVATE_CONTROL_2 --sha PRIVATE_CONTROL_3 --sha PRIVATE_CONTROL_4 --sha PRIVATE_CONTROL_5 --sha PRIVATE_CONTROL_6

The two cited unavailable inputs changed from ArchiveFormatError to successful AMD64 StaticArchive loads with 77 and 2 members. 6 recorded-success controls across RISCV64, AMD64, X86, PPC64, PPC32, and AARCH64 loaded in both environments with unchanged backend, architecture, and member counts (1, 1, 15, 24, 123, and 253); worse: 0.

@angr-bot

Copy link
Copy Markdown
Member

Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_851

@ltfish

ltfish commented Sep 30, 2026

Copy link
Copy Markdown
Member

Should we also send a PR to arpy?

@ltfish
ltfish merged commit 25acfd2 into master Sep 30, 2026
20 checks passed
@ltfish
ltfish deleted the feature/fix-c-276 branch September 30, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants