concept

Release Tarball Divergence

also called Archive-Repository Gap, Distributed-Source Divergence

The gap between a project's reviewed source repository and the source archive that downstream actually builds, which is a trust boundary most supply-chain controls assume does not exist.

xzsupply-chainprovenancereproducible-buildsautotools

Distribution packagers do not usually build from a project's repository. They build from a release archive, and for many projects that archive is deliberately not a copy of the repository: it contains generated build files so that consumers do not need matching versions of the generator tooling. Everybody in that ecosystem knows this, and almost nobody treats the difference as a trust boundary.

That is where the xz-utils backdoor lived, for roughly 35 days before anyone noticed. CVE-2024-3094, disclosed on 29 March 2024, was present in the release tarballs of versions 5.6.0 and 5.6.1 and not in the equivalent state of the public git tree: a malicious build-to-host.m4 shipped only in the archive and, during the build, assembled a payload from binary files that looked like ordinary test fixtures for a compression library. The repository was clean. Code review was clean. The artefact everyone built was one nobody had reviewed.

Why it matters

Nearly every supply-chain control in common use assumes an unbroken chain from a reviewed commit to a running artefact. Divergence breaks that assumption in the one place nobody is looking:

  • Signed commits and protected branches guard a repository that was never modified.
  • A software bill of materials lists the dependency at a legitimate version, and is correct and useless.
  • Vulnerability scanning matches known identifiers, and there was none.
  • A signed release proves the maintainer produced the archive, which they did.

The same shape appears well beyond autotools: a published package whose contents differ from its source repository, a container image whose Dockerfile is not the one in the repo, a binary release built on a maintainer's laptop, a vendored dependency edited after vendoring. Any place where the thing built is not the thing reviewed is an instance.

Implementation patterns

  • Build from the repository, with the generator tooling run inside the build. This removes the gap rather than inspecting it, and it is the direction several distributions moved afterwards.
  • Reproducible builds with independent verification. If the artefact is a deterministic function of a reviewed tree, a second builder gets the same bytes and any injection outside the tree appears as a mismatch. This is the only control that scales, because anyone can verify.
  • Diff the archive against the repository and review what exists only in the archive. The set is small and it is exactly where this class hides.
  • Treat binary fixtures the build touches as executable input. Test data that a build script reads deserves the scrutiny of code.
  • Provenance naming the source tree. A build attestation in the SLSA sense records which commit produced which artefact on which builder, so "what was this built from?" is answerable rather than assumed.
  • Pin by digest everywhere downstream, so a changed archive is a changed input rather than a silent substitution.

Industry example

The xz case is the canonical one and it is worth noting what made it work socially as well as technically: a long campaign to gain maintainer trust on an under-resourced project, a payload split between a build macro and test data so neither looked alarming alone, and an ecosystem convention that made the divergence unremarkable. It was found by an engineer investigating a performance anomaly, not by any control.

The earlier and larger reference point is the SolarWinds compromise disclosed in December 2020, where the injection was in the build system rather than in the source. Both say the same thing: the build is a production system, and the artefact is the thing to attest.

Failure scenarios

  • A dependency whose published package differs from its repository, so reading the source on a code-hosting site tells you nothing about what you installed.
  • A vendored copy modified after vendoring, undetectable without a diff against upstream.
  • Generated files committed and then edited by hand, where the generator would produce something else.
  • An image built from a Dockerfile that was later changed, with the artefact promoted for months.
  • A reproducible-build effort that never publishes its expected digests, so nobody can verify and the property is unused.

Trade-offs

Choose Gains Pays
Build from the repository The gap disappears Generator toolchain must be available and pinned for every consumer
Verify the archive against the repository Cheap; catches this class Someone must actually read the diff, every release
Full reproducible builds Independent verification by anyone Months of determinism work, some of it upstream
Accept the convention No work An unreviewed artefact in your supply chain

When not to use it

If your builds already run from a checkout of your own repository with dependencies pinned by digest, this specific gap is closed and effort belongs elsewhere — most likely on the dependency tree itself, since a compromised package inside a pinned-but-unaudited tree is the far more probable route in. It is also not worth pursuing full reproducibility to defend against this if you consume only first-party code and managed base images. Rank the work by which gate is actually missing, not by which incident was in the news, and start with the cheap question: can you say whether what you build is what you reviewed?

Interview question

Q: An auditor asks you to prove that the binary in production corresponds to the source your team reviewed. Walk me through what you can prove today and what you would need to build.

What a strong answer covers: distinguishing the reviewed tree, the build inputs and the artefact, and naming the gaps between them; what signed commits, an SBOM and a signed release each do and do not prove; pinning every input by digest as the prerequisite; build provenance recording commit and builder; reproducible builds as the control that allows independent verification, with an honest estimate of its cost; and the xz case as the concrete reason the archive-versus-repository distinction matters.

Quick check

Quiz: Why did signed commits and an SBOM both fail to detect the xz backdoor? Because the repository was unmodified and the dependency was at a legitimate version — the malicious code existed only in the release archive that was actually built.

Flashcard: What is the cheapest control against release tarball divergence? — Diff the archive against the repository and review what exists only in the archive; the set is small generated build files, which is exactly where this class hides.