Reconsider how merging works for "string or array of strings" Cargo configurations

This becomes especially interesting around the unsound codegen options. Rustc necessarily has some codegen options which are, strictly speaking, unsafe, as they make unchecked assumptions about the target runtime environment. The canonical example is than enabling target CPU features is (or at least can be) unsound if run on a CPU without those features[1]. So far Cargo avoids addressing this theoretical unsoundness hole by just not exposing it except via the "I know what I'm doing" knob that is setting RUSTFLAGS directly, but first-class Cargo support means needing to explicitly acknowledge any potentially unsound flag usage.

-Ctarget-cpu has a fairly straightforward solution — test target features before main (or on dylib load) to generate a guaranteed crash before execution of any code compiled with an assumption of feature availability. I don't know off the top of my head what other rustc options are technically unsound. I'm 95% certain other flags exist that pass on a soundness proof to the person distributing/running the binary. (E.g. the implicit assumption that any linked life before main is safe independent of runtime environment.)

"I'm going to run this binary blob" (hopefully) doesn't have UB at the OS level, but it absolutely can cause UB at the program level when language-known properties about the host don't hold. People already familiar with native builds for C or C++ are hopefully aware of this, but for people who don't have any unmanaged background and don't touch unsafe can absolutely be burnt by this[2].


  1. Specifically, Rust (and LLVM) declare it UB to run code in an environment with improperly set target features, as optimizations are allowed to introduce usage of those features (e.g. for auto-vectorization). And even then, IIRC is_x86_feature_detected! has an optimization to skip hitting the feature detection cache if the feature is globally statically available. (I know I've seen a proposal for it to try to skip the check if during monomorphization it's locally statically enabled by a use of #[target_feature]. I don't think we do that currently, as it is in conflict with bottom-up optimization/inning order, and discussion leaned towards using a separate macro to avoid changing the behavior of the current one, but it's desirable for rustc-managed multiversioned function.) ↩︎

  2. It's not uncommon to see "use -Ctarget-cpu=native for release builds as a performance tip without any further caveats, especially on x86_64 targets where the default v1 target is behind a still reasonable assumption for an audience of "running newly acquired software" systems to have. But if a less aware user follows this advice and then runs the binary on an older CPU than was used to do the build — easily accomplishable if the build was done on "latest" CI and then run from an older local machine — they're opening themselves up to UB (in the best case, an illegal instruction fault, but in a worst case, processor level nondeterminism from decoding the "malformed" instruction encoding). ↩︎

1 Like