Skip to content

-C target_cpu=cortex-a72 (and -target-cpu=native on Raspberry Pi) wrongly enables crypto features that are optional on Cortex-A72 #125033

Open

Description

@briansmith

Building with RUSTFLAGS="-C target_cpu=cortex-a72" statically enables the target_feature="aes", target_feature="crc", target_feature="pmuv3", and target_feature="sha2". However, at least the crypto features AES, CRC, and SHA2 are optional on this CPU. The definition for this target is wrong. See the upstream LLVM bug: llvm/llvm-project#90365.

The main consequence is that crypto libraries that use cfg(target_feature = ...) feature detection for these hardware instructions are getting miscompiled, causing the programs to, at best, crash with an illegal instruction exception. This particular affects Raspberry Pi users compiling with RUSTLFAGS=-target-cpu=native. From briansmith/ring#1858 (comment):

$ rustc --print cfg --target=aarch64-unknown-linux-gnu -C target_cpu=cortex-a72 | grep feature
target_feature="aes"
target_feature="crc"
target_feature="neon"
target_feature="pmuv3"
target_feature="sha2"

Without -C target_cpu=cortex-a72 we get the correct feature flags:

$ rustc --print cfg --target=aarch64-unknown-linux-gnu | grep feature
target_feature="neon"

I verified this is an issue on Rust 1.61 stable, 1.78 stable, and rustc 1.80.0-nightly (6e1d947 2024-05-10).

Although some crypto libraries may work around this issue, these workarounds have negative consequences. In the case of ring's workaround, the result of the workaround is bloat and worse performance on all AArch64 CPUs that actually are guaranteed to have the crypto extensions (except on Fuchsia, Windows, and macOS).

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 12, 2024
  2. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    O-AArch64Armv8-A or later processors in AArch64 mode
    A-target-featureArea: Enabling/disabling target features like AVX, Neon, etc.
    A-targetsArea: Concerning the implications of different compiler targets
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on May 12, 2024
  3. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on May 12, 2024
  4. apiraino commented on May 13, 2024

    @apiraino
    Contributor

    WG-prioritization assigning priority (Zulip discussion).

    Since in our support list this arch has the most support:

    @rustbot label -I-prioritize +P-critical

  5. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on May 13, 2024
  6. briansmith commented on May 17, 2024

    @briansmith
    ContributorAuthor

    From today's comment in the upstream LLVM issue:

    For -mcpu=xyz, we enable the maximal set of features for the cpu (so long as they are relatively common), which can be disabled with +nofeat. [....] The idea is that users get decent performance by default, and if they have less features can turn down the default.

    Unfortunately GCC didn't follow that scheme at the time for crypto instructions, and had them disabled by default. Clang did, so there was a difference in whether crypto was enabled. We did not decide to retro-actively change old CPU definitions (it could be a breaking change), but going forward "Armv-9" cpus have been changed to not include crypto by default.

    Assuming that is accurate, there are a few interesting things:

    • Potentially the issue is much broader than Cortex-A* CPUs, as it seems like LLVM doesn't actually have a policy of only-required-features-by-default. I.e. the current behavior seems to be by design.
    • Assuming we want -C target_cpu to be safe by default, rustc cannot delegate its defaults to LLVM. rustc should turn off feature flags for optional features by default. Basically -C target_cpu is generally not a memory-safe option. This should be documented retroactively for old versions and fixed for newer versions.
    • Assuming cc-rs also wants to be "safe by default," it also needs to manually turn off feature flags for optional features by default when the compiler is clang, by passing additional flags.
    • This all would be a potentially-compatibility-breaking change.
  7. briansmith commented on May 17, 2024

    @briansmith
    ContributorAuthor

    Also, from the LLVM issue:

    For -march=armv8.x-a we enable the minimal set of features (so all required extensions). Optional features can be added with +feat.

    So, one workaround would be to use that. But, I don't see rustc providing a mechanism to choose the ARM architecture level instead of a specific CPU.

  8. 15 remaining items

  9. added
    llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixes
    on Oct 2, 2024
  10. workingjubilee commented on Oct 2, 2024

    @workingjubilee
    Member

    heads up crabs, llvm/llvm-project#95694 got merged, I'll let y'all figure out how to backport that or what.

  11. briansmith commented on Oct 4, 2024

    @briansmith
    ContributorAuthor

    workingjubilee added llvm-fixed-upstream

    This isn't fixed upstream though? Maybe the target-cpu=native case is fixed thanks to your effort but the target_cpu=cortex-a72 case is still an issue.

  12. nikic commented on Oct 4, 2024

    @nikic
    Contributor

    The -C target-cpu=native behavior was a plain bug. The -C target-cpu=cortext-a72 behavior is just a difference in opinion.

  13. workingjubilee commented on Oct 4, 2024

    @workingjubilee
    Member

    @briansmith It's a separate issue because it applies to almost every single -Ctarget-cpu that isn't -Ctarget-cpu=native for AArch64. It requires a different solution, even if it is fixed upstream by successfully armwrestling LLVM into changing every single CPU def.

  14. RalfJung commented on Feb 3, 2025

    @RalfJung
    Member

    Does -C target_cpu=cortex-a72+nofeat (mentioned in the LLVM issue) help here, or is +nofeat a clang thing?

  15. briansmith commented on Feb 3, 2025

    @briansmith
    ContributorAuthor

    For reference, there is now a more general LLVM issue (llvm/llvm-project#113008).

    I want to emphasize that because of this, and the more general issue, basically cfg(target_feature) and std::arch::is_aarch64_feature_detected() are safety hazards for very common usage scenerios, and we (my projects) have to limit our use of them. But then that also limits which OSs we support because we have to write OS-specific code for feature detection for the crypto extensions.

    The problem with expecting this to be dealt with upstream for existing targets is that it is a substantial compatibility-breaking change for them, and/or a performance regression for some existing projects.

    Thus, I do think we should try to work around it in the Rust compiler, instead of waiting for it to be fixed upstream.

  16. briansmith commented on Feb 3, 2025

    @briansmith
    ContributorAuthor

    Does -C target_cpu=cortex-a72+nofeat (mentioned in the LLVM issue) help here, or is +nofeat a clang thing?

    $ rustc --target=aarch64-unknown-linux-gnu -C target_cpu=cortex-a72+nofeat --print cfg
    'cortex-a72+nofeat' is not a recognized processor for this target (ignoring processor)
    'cortex-a72+nofeat' is not a recognized processor for this target (ignoring processor)
    'cortex-a72+nofeat' is not a recognized processor for this target (ignoring processor)
    'cortex-a72+nofeat' is not a recognized processor for this target (ignoring processor)
    debug_assertions
    panic="unwind"
    target_abi=""
    target_arch="aarch64"
    target_endian="little"
    target_env="gnu"
    target_family="unix"
    target_has_atomic="128"
    target_has_atomic="16"
    target_has_atomic="32"
    target_has_atomic="64"
    target_has_atomic="8"
    target_has_atomic="ptr"
    target_os="linux"
    target_pointer_width="64"
    target_vendor="unknown"
    unix
    

    So it seems like nofeat "works" in the sense that it causes the -C target_cpu to be (very much not silently) ignored.

  17. RalfJung commented on Feb 3, 2025

    @RalfJung
    Member

    Hm maybe I understood the +nofeat comment. Someone also says in that issue

    -mcpu=.. enables the maximum optional features for the cpu. They can be disabled with +noxyz.

    So maybe it has to be +noaes or +nocrypto or so? But I can't get that to emit target_feature="neon" which I assume is what you are looking for.

  18. briansmith commented on Feb 3, 2025

    @briansmith
    ContributorAuthor

    it seems like when you pass -C target_cpu it disables all the default target features and then enables the target features for the given CPU? This seems to be why "neon" doesn't get enabled when we pass in an invalid/unrecognized target_cpu like -C target_cpu=cortex-a72+nofeat or -C target_cpu=cortex-a72+nocrypto.

    By the way, the issue I am working around is that some (Linux?) distributors compile all packages with -C target_cpu=cortex-a72 for Raspberry Pi. So I have to make sure I ignore the crypto extensions reported by #[cfg(target_feature)] for AArch64 because otherwise people think the bug is in ring.

  19. RalfJung commented on Feb 3, 2025

    @RalfJung
    Member

    If it's a distro-level issue, it should be possible for them to add further flags / adjust their target spec to avoid enabling features that the Raspberry Pi does not have.

    The problem with expecting this to be dealt with upstream for existing targets is that it is a substantial compatibility-breaking change for them, and/or a performance regression for some existing projects.

    LLVM hasn't cared much for compatibility around target features before, so this is not a very strong argument.

    Anyway I don't have a strong opinion for how to fix this; I just know the rustc implementation of this so I know it would be a quite terrible hack to work around this on the rustc side, and not very sustainable.

  20. moved this to Blocked By LLVM in Arm Maintainerson Dec 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-target-featureArea: Enabling/disabling target features like AVX, Neon, etc.A-targetsArea: Concerning the implications of different compiler targetsC-bugCategory: This is a bug.I-miscompileIssue: Correct Rust code lowers to incorrect machine codeI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessO-AArch64Armv8-A or later processors in AArch64 modeP-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixes

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions