diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index e6460fb93d3e9..9acbb16d73e76 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -24,3 +24,11 @@ pub fn env_var_os(name: &str) -> OsString { pub fn no_debug_assertions() -> bool { std::env::var_os("NO_DEBUG_ASSERTIONS").is_some() } + +/// A wrapper around [`std::env::set_current_dir`] which includes the directory +/// path in the panic message. +#[track_caller] +pub fn set_current_dir>(dir: P) { + std::env::set_current_dir(dir.as_ref()) + .expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display())); +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index fc20fd3b2e86a..956fa1404c7e2 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -64,7 +64,7 @@ pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; pub use diff::{diff, Diff}; /// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers. -pub use env::{env_var, env_var_os}; +pub use env::{env_var, env_var_os, set_current_dir}; /// Convenience helpers for running binaries and other commands. pub use run::{cmd, run, run_fail, run_with_args}; diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index f55abb513b80d..05e8af5e1c5ce 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -11,4 +11,3 @@ run-make/macos-deployment-target/Makefile run-make/split-debuginfo/Makefile run-make/symbol-mangling-hashed/Makefile run-make/translation/Makefile -run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_Makefile similarity index 76% rename from tests/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile rename to tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_Makefile index 3c88ec34f431e..49c87b6e2d987 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_Makefile @@ -1,3 +1,6 @@ +# FIXME(Oneirical): Disabled for now. Remove this if the rmake.rs replacement +# is shown to work, or restore it if the rmake.rs replacement does not work. + include ../tools.mk #only-x86_64-fortanix-unknown-sgx diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_script.sh similarity index 94% rename from tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh rename to tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_script.sh index a7c4ae13ecb38..3315f7d6782c8 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/_script.sh @@ -1,4 +1,8 @@ #!/bin/bash + +# FIXME(Oneirical): Disabled for now. Remove this if the rmake.rs replacement +# is shown to work, or restore it if the rmake.rs replacement does not work. + set -exuo pipefail function build { diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs new file mode 100644 index 0000000000000..272470b33040e --- /dev/null +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs @@ -0,0 +1,95 @@ +// ignore-tidy-linelength +// Reason: intel.com link + +// This security test checks that the disassembled form of certain symbols +// is "hardened" - that means, the assembly instructions match a pattern that +// mitigate potential Load Value Injection vulnerabilities. +// To do so, a test crate is compiled, and certain symbols are found, disassembled +// and checked one by one. +// See https://github.com/rust-lang/rust/pull/77008 + +// On load value injection: +// https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/load-value-injection.html + +//@ only-x86_64-fortanix-unknown-sgx + +use run_make_support::{cmd, cwd, llvm_filecheck, llvm_objdump, regex, set_current_dir, target}; + +fn main() { + let main_dir = cwd(); + set_current_dir("enclave"); + // HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. + // These come from the top-level Rust workspace, that this crate is not a + // member of, but Cargo tries to load the workspace `Cargo.toml` anyway. + cmd("cargo") + .env("RUSTC_BOOTSTRAP", "1") + .arg("-v") + .arg("run") + .arg("--target") + .arg(target()) + .run(); + set_current_dir(&main_dir); + // Rust has various ways of adding code to a binary: + // - Rust code + // - Inline assembly + // - Global assembly + // - C/C++ code compiled as part of Rust crates + // For those different kinds, we do have very small code examples that should be + // mitigated in some way. Mostly we check that ret instructions should no longer be present. + check("unw_getcontext", "unw_getcontext.checks"); + check("__libunwind_Registers_x86_64_jumpto", "jumpto.checks"); + + check("std::io::stdio::_print::[[:alnum:]]+", "print.with_frame_pointers.checks"); + + check("st_plus_one_global_asm", "rust_plus_one_global_asm.checks"); + + check("_plus_one_c", "cc_plus_one_c.checks"); + check("_plus_one_c_asm", "cc_plus_one_c_asm.checks"); + check("_plus_one_cxx", "cc_plus_one_cxx.checks"); + check("_plus_one_cxx_asm", "cc_plus_one_cxx_asm.checks"); + check("_plus_one_asm", "cc_plus_one_asm.checks"); + + check("cmake_plus_one_c", "cmake_plus_one_c.checks"); + check("cmake_plus_one_c_asm", "cmake_plus_one_c_asm.checks"); + check("cmake_plus_one_c_global_asm", "cmake_plus_one_c_global_asm.checks"); + check("cmake_plus_one_cxx", "cmake_plus_one_cxx.checks"); + check("cmake_plus_one_cxx_asm", "cmake_plus_one_cxx_asm.checks"); + check("cmake_plus_one_cxx_global_asm", "cmake_plus_one_cxx_global_asm.checks"); + check("cmake_plus_one_asm", "cmake_plus_one_asm.checks"); +} + +fn check(func_re: &str, mut checks: &str) { + let dump = llvm_objdump() + .input("enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave") + .args(&["--syms", "--demangle"]) + .run() + .stdout_utf8(); + let re = regex::Regex::new(&format!("[[:blank:]]+{func_re}")).unwrap(); + let func = re.find_iter(&dump).map(|m| m.as_str().trim()).collect::>().join(","); + let dump = llvm_objdump() + .input("enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave") + .args(&["--syms", &format!("--disassemble-symbols={func}")]) + .run() + .stdout_utf8(); + let dump = dump.as_bytes(); + + // Unique case, must succeed at one of two possible tests. + // This is because frame pointers are optional, and them being enabled requires + // an additional `popq` in the pattern checking file. + if func_re == "std::io::stdio::_print::[[:alnum:]]+" { + let output = llvm_filecheck().stdin(&dump).patterns(checks).run_unchecked(); + if !output.status().success() { + checks = "print.without_frame_pointers.checks"; + llvm_filecheck().stdin(&dump).patterns(checks).run(); + } + } else { + llvm_filecheck().stdin(&dump).patterns(checks).run(); + } + if !["rust_plus_one_global_asm", "cmake_plus_one_c_global_asm", "cmake_plus_one_cxx_global_asm"] + .contains(&func_re) + { + // The assembler cannot avoid explicit `ret` instructions. Sequences + // of `shlq $0x0, (%rsp); lfence; retq` are used instead. + llvm_filecheck().args(&["--implicit-check-not", "ret"]).stdin(dump).patterns(checks).run(); + } +}