On March 29, 2024, Microsoft engineer Andres Freund noticed SSH logins on a Debian sid box slowing from roughly 300 milliseconds to roughly 800 milliseconds — a latency anomaly he chased into liblzma, a C compression library dynamically linked into sshd via its dependency on libsystemd. What he found was a deliberate backdoor, planted across versions 5.6.0 and 5.6.1 of XZ Utils by a maintainer known as "Jia Tan," who had spent roughly two years building trust in the project before shipping malicious code hidden in test-data binary blobs and build-script obfuscation. CVE-2024-3094 scored a 10.0 CVSS, and the payload was distributed only in release tarballs that differed from the public git history — a distribution trick that is far harder to pull off against a package that lives in one canonical, hash-verified registry. That's the crux of a problem specific to C and C++: there is no npm, no PyPI, no crates.io for this ecosystem. Dependency resolution is scattered across vcpkg, Conan, raw git submodules, CMake's FetchContent, and plain vendored source trees, each with different (or no) integrity guarantees. This piece looks at why that fragmentation is a distinct supply-chain risk, not just an inconvenience, and what actually mitigates it.
Why doesn't C++ have a single package registry like npm or PyPI?
C++ predates the centralized-registry model by decades, and no single tool ever achieved the ecosystem-wide adoption that npm has for Node or PyPI has for Python. Instead, the community settled into parallel, non-interoperable systems: Microsoft's vcpkg, which curates roughly 2,800-plus "ports" built from source against a fixed baseline, and JFrog's ConanCenter, backing the Conan package manager with roughly 1,900-plus recipes. Neither is the default — CMake, the dominant build system, ships with no package manager opinion at all, so many projects never adopt either and instead pull dependencies via git submodules, FetchContent, or by copying source directly into a third_party/ or vendor/ directory. The result is that "what dependencies does this C++ project have" is not a question with one authoritative answer the way package-lock.json or requirements.txt gives you one. A security team auditing ten C++ repositories may find ten different resolution strategies, several of them undocumented, layered inside the same organization.
How does vendoring hide provenance and break version tracking?
Vendoring hides provenance because copying source into a repository severs the link back to where that code came from, what version it is, and whether it has been modified from upstream. Chromium's third_party/ directory and curl's bundled dependency handling are widely cited examples of large, well-run projects that still rely heavily on vendored code rather than resolvable, versioned dependency declarations. Once source is vendored, there is typically no lockfile-equivalent with cryptographic integrity hashes — nothing analogous to package-lock.json, Cargo.lock, or a requirements.txt pinned with --hash. That absence has two concrete consequences: automated SBOM generation tools can't reliably enumerate what's actually embedded in the binary, and vulnerability-matching services can't map a CVE against a package name and version because there is no declared package name and version to match against — only a directory of .c and .h files that may or may not have been hand-patched since the last time anyone checked upstream.
What did CVE-2024-3094 actually demonstrate about this risk class?
CVE-2024-3094 demonstrated that a distribution-channel attack against a C library can bypass exactly the kind of verification that registry-based ecosystems increasingly enforce by default. The malicious code was not visible in the XZ Utils public GitHub repository — it was injected only into the release tarballs that distributions like Debian and Fedora pulled from, via a build-time script that decoded an obfuscated binary blob disguised as test data. Because liblzma is a C library consumed by countless downstream projects through system package managers and direct linking rather than through a manifest a scanner could parse, the anomaly was caught not by automated tooling but by one engineer noticing an unrelated performance regression. Datadog Security Labs and multiple subsequent writeups have since used the incident as the canonical case study for why build-time integrity — not just source-repository review — matters most in ecosystems without registry-enforced provenance checks.
Why do standard SCA tools struggle to cover C/C++ dependencies?
Standard software composition analysis tools key off manifests and lockfiles — package.json, pom.xml, go.mod, Cargo.toml, composer.lock, Gemfile.lock — and resolve those declarations against a vulnerability database. C and C++ frequently offer no such manifest to resolve against, because the dependency was vendored, submoduled, or compiled from a third_party/ tree with no declared version string. This is an honest, industry-wide gap rather than a shortcoming of any one vendor: reviewing supported-ecosystem lists across SCA tooling — including Safeguard's own deep dependency scanning, which resolves npm, PyPI, Maven/Gradle, Go modules, Cargo, NuGet, Composer, and Ruby Bundler manifests to a depth of 100 levels — shows no C/C++-specific manifest resolver in that list. Where C/C++ artifacts do get partial coverage industry-wide is at the binary and container-layer level: scanners that read package metadata and binaries embedded inside container image layers can sometimes surface a bundled library's identity even without a source-level manifest, though this is meaningfully shallower than manifest-driven resolution with a full transitive graph.
What should teams shipping C++ actually do about it?
Teams shipping C++ should treat the absence of a unified registry as a reason to be more deliberate, not less, about tracking what's in their binaries. That means preferring Conan or vcpkg over ad hoc vendoring wherever feasible, since both at least produce a declared, versioned dependency list that can be diffed and audited — vcpkg pins against a manifest-mode vcpkg.json and a baseline commit, and Conan generates a lockfile via conan lock create. Where vendoring is unavoidable (as it often is for performance-critical or hard-to-package libraries), track the exact upstream commit hash in version control rather than a loose "as of some date" comment, and diff vendored trees against upstream on a schedule rather than never. Reproducible builds — verifying that a build from source matches a distributed binary bit-for-bit — is the structural defense against exactly the tarball-vs-repository mismatch that made CVE-2024-3094 possible, and Debian's reproducible-builds initiative is the most mature public effort pushing in that direction. None of this closes the gap to what npm or Cargo offer natively, but each step narrows the blind spot between "what we think we shipped" and what actually got compiled in.