src/lib.rs
src/lib.rs, line 306-348
fn probe_fmt<'a>(&self, source: Arguments<'a>) -> Result<(), Error> {
let crate_name = self.new_crate_name();
let mut command = self.rustc.command();
command
.arg("--crate-name")
.arg(&crate_name)
.arg("--crate-type=lib")
.arg("--out-dir")
.arg(&self.out_dir)
.arg("--emit=llvm-ir");
if let Some(edition) = self.edition.as_ref() {
command.arg("--edition").arg(edition);
}
if let Some(target) = self.target.as_ref() {
command.arg("--target").arg(target);
}
command.args(&self.rustflags);
command.arg("-").stdin(Stdio::piped());
let mut child = try!(command.spawn().map_err(error::from_io));
let mut stdin = child.stdin.take().expect("rustc stdin");
try!(stdin.write_fmt(source).map_err(error::from_io));
drop(stdin);
match child.wait() {
Ok(status) if status.success() => {
// Try to remove the output file so it doesn't look like a build product for
// systems like bazel -- but this is best-effort, so we can ignore failure.
// The probe itself is already considered successful at this point.
let mut file = self.out_dir.join(crate_name);
file.set_extension("ll");
let _ = fs::remove_file(file);
Ok(())
}
Ok(status) => Err(error::from_exit(status)),
Err(error) => Err(error::from_io(error)),
}
}
probe_fmt spawns rustc with Command, piping a synthesized Rust snippet via stdin, justifying uses-exec. The command form is argv (no shell), and arguments are crate-internal constants (crate name from FNV hash, paths from OUT_DIR) or controlled by the consumer's own build script, justifying exec-safe.
src/lib.rs, line 579-618
fn rustflags(target: &Option<OsString>, dir: &Path) -> Vec<String> {
// Starting with rust-lang/cargo#9601, shipped in Rust 1.55, Cargo always sets
// CARGO_ENCODED_RUSTFLAGS for any host/target build script invocation. This
// includes any source of flags, whether from the environment, toml config, or
// whatever may come in the future. The value is either an empty string, or a
// list of arguments separated by the ASCII unit separator (US), 0x1f.
if let Ok(a) = env::var("CARGO_ENCODED_RUSTFLAGS") {
return if a.is_empty() {
Vec::new()
} else {
a.split('\x1f').map(str::to_string).collect()
};
}
// Otherwise, we have to take a more heuristic approach, and we don't
// support values from toml config at all.
//
// Cargo only applies RUSTFLAGS for building TARGET artifact in
// cross-compilation environment. Sadly, we don't have a way to detect
// when we're building HOST artifact in a cross-compilation environment,
// so for now we only apply RUSTFLAGS when cross-compiling an artifact.
//
// See https://github.com/cuviper/autocfg/pull/10#issuecomment-527575030.
if *target != env::var_os("HOST")
|| dir_contains_target(target, dir, env::var_os("CARGO_TARGET_DIR"))
{
if let Ok(rustflags) = env::var("RUSTFLAGS") {
// This is meant to match how cargo handles the RUSTFLAGS environment variable.
// See https://github.com/rust-lang/cargo/blob/69aea5b6f69add7c51cca939a79644080c0b0ba0/src/cargo/core/compiler/build_context/target_info.rs#L434-L441
return rustflags
.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect();
}
}
Vec::new()
}
Reads CARGO_ENCODED_RUSTFLAGS, RUSTFLAGS, HOST, and CARGO_TARGET_DIR to mirror cargo's flag-propagation logic for cross-compilation probes. Only documented cargo and rustc environment variables are consulted, justifying uses-environment and environment-safe.
src/lib.rs, line 195-198
let meta = try!(fs::metadata(&dir).map_err(error::from_io));
if !meta.is_dir() || meta.permissions().readonly() {
return Err(error::from_str("output path is not a writable directory"));
}
OUT_DIR is checked to exist and be writable before use. Subsequent file operations write only into this directory (rustc --out-dir) and immediately remove the .ll artefact, justifying uses-filesystem and filesystem-safe.
src/lib.rs, line 297-304
/// Returns a new (hopefully unique) crate name for probes.
fn new_crate_name(&self) -> String {
#[allow(deprecated)]
static ID: AtomicUsize = ATOMIC_USIZE_INIT;
let id = ID.fetch_add(1, Ordering::Relaxed);
format!("autocfg_{:016x}_{}", self.uuid, id)
}
An AtomicUsize counter combined with a per-instance UUID generates unique probe crate names, justifying uses-concurrency and concurrency-safe. Threads are never spawned by this crate; the atomic only protects against concurrent callers issuing probes.
src/lib.rs, line 38-44
//! ## Caution
//!
//! Many of the probing methods of `AutoCfg` document the particular template they
//! use, **subject to change**. The inputs are not validated to make sure they are
//! semantically correct for their expected use, so it's _possible_ to escape and
//! inject something unintended. However, such abuse is unsupported and will not
//! be considered when making changes to the templates.
The crate documents that probe-method inputs are not validated and may inject unintended code into the synthesized rustc snippet. Treated as a build-time misuse hazard, not a security vulnerability, because the inputs originate in the consumer's own build script.