Safeguard
Developer Tools•

Do Cargo features change what ships in embedded Rust firmware?

Yes. Cargo unions features across the dependency graph, so a crate can pull in code you did not expect. Check the real set before building the SBOM.

Safeguard Security Team
3 min read

Yes. Cargo features switch optional code and optional dependencies on or off, and when several crates depend on the same package Cargo uses the union of the features they request. The set that ships in your firmware can be larger than the one you wrote down.

Why this matters for an SBOM

A lockfile tells you which crate versions were resolved. It does not by itself tell you which features of each crate were compiled in. For firmware, that difference changes the attack surface, the binary size and the list of advisories that apply.

The Cargo reference explains the rule. When a dependency is used by multiple packages, Cargo uses the union of all features enabled on it. A consequence, the reference says, is that features should be additive: enabling a feature should not break code that does not enable it.

Where surprises come from

  • A crate in your tree turns on a default feature you did not choose, such as a standard library dependent or a networking option.
  • Two dependencies ask for different features of a shared crate, and both get the combined set.
  • A workspace member enables a feature that leaks into the firmware build because they share a resolver result.

The reference also notes that the version 2 resolver avoids unifying features in a few situations, for instance across different targets and for build dependencies, so the resolver version in your manifest changes the answer. Check which one you use.

Steps to see what you ship

  1. Run the Cargo tree command with the features flag for the exact target triple and profile you flash to devices. It prints which crates are included and which features enabled each.
  2. Run it again with the inverted option on any crate you did not expect, to see who pulled it in.
  3. Disable default features on dependencies where you can, and enable only those you need.
  4. Commit the resolved result alongside the lockfile in your release notes, so the SBOM can be reproduced.
  5. Generate the SBOM from the same invocation you use for release, not from a developer default build.

What to put in the SBOM

Include each crate and exact version that is compiled into the image. Note feature selections in a property or a comment if your format allows it. Exclude crates that only run on the build machine, or record them separately as build tooling.

That split matters. A vulnerability in a build script crate and one in a crate linked into the device are not the same finding, even if both appear in the lockfile.

Try it yourself

Open the dependency inventory demo and look at direct versus transitive entries. Think about which of your own features would add or remove rows.

Click through it at your own pace, or open it full screen.

Safeguard scans dependency manifests and generates SBOMs. For feature level accuracy, run your scan against the release build configuration and compare it with the Cargo tree output.

Quick check

Does Cargo.lock list enabled features?

No. It records resolved package versions. Use the tree command with feature output to see what is enabled.

Should firmware crates disable default features?

Often yes, where the crate allows it. Enable only what the device needs, then verify with the tree.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.