Rollup merge of #129055 - Oneirical:fortanix-fortification, r=jieyouxu
Migrate `x86_64-fortanix-unknown-sgx-lvi` `run-make` test to rmake Part of #121876 and the associated [Google Summer of Code project](https://blog.rust-lang.org/2024/05/01/gsoc-2024-selected-projects.html). The final Makefile! Every Makefile test is now claimed. This is difficult to test due to the uncommon architecture it is specific to. I don't think it is in the CI (I didn't find it in `jobs.yml`, but if there is a way to test it, please do. Locally, on Linux, it compiles and panics at the `llvm_filecheck` part (if I replace the `x86_64-fortanix-unknown-sgx` with `x86_64-unknown-linux-gnu`, of course), which is expected. For this reason, the Makefile and associated script have been kept, but with a leading underscore.
This commit is contained in:
commit
fe87433e8a
6 changed files with 105 additions and 94 deletions
|
@ -24,3 +24,11 @@ pub fn env_var_os(name: &str) -> OsString {
|
||||||
pub fn no_debug_assertions() -> bool {
|
pub fn no_debug_assertions() -> bool {
|
||||||
std::env::var_os("NO_DEBUG_ASSERTIONS").is_some()
|
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<P: AsRef<std::path::Path>>(dir: P) {
|
||||||
|
std::env::set_current_dir(dir.as_ref())
|
||||||
|
.expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display()));
|
||||||
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
|
||||||
pub use diff::{diff, Diff};
|
pub use diff::{diff, Diff};
|
||||||
|
|
||||||
/// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers.
|
/// 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.
|
/// Convenience helpers for running binaries and other commands.
|
||||||
pub use run::{cmd, run, run_fail, run_with_args};
|
pub use run::{cmd, run, run_fail, run_with_args};
|
||||||
|
|
|
@ -11,4 +11,3 @@ run-make/macos-deployment-target/Makefile
|
||||||
run-make/split-debuginfo/Makefile
|
run-make/split-debuginfo/Makefile
|
||||||
run-make/symbol-mangling-hashed/Makefile
|
run-make/symbol-mangling-hashed/Makefile
|
||||||
run-make/translation/Makefile
|
run-make/translation/Makefile
|
||||||
run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
include ../tools.mk
|
|
||||||
|
|
||||||
#only-x86_64-fortanix-unknown-sgx
|
|
||||||
|
|
||||||
# For cargo setting
|
|
||||||
export RUSTC := $(RUSTC_ORIGINAL)
|
|
||||||
export LD_LIBRARY_PATH := $(HOST_RPATH_DIR)
|
|
||||||
# We need to be outside of 'src' dir in order to run cargo
|
|
||||||
export WORK_DIR := $(TMPDIR)
|
|
||||||
export TEST_DIR := $(shell pwd)
|
|
||||||
|
|
||||||
## clean up unused env variables which might cause harm.
|
|
||||||
unexport RUSTC_LINKER
|
|
||||||
unexport RUSTC_BOOTSTRAP
|
|
||||||
unexport RUST_BUILD_STAGE
|
|
||||||
unexport RUST_TEST_THREADS
|
|
||||||
unexport RUST_TEST_TMPDIR
|
|
||||||
unexport AR
|
|
||||||
unexport CC
|
|
||||||
unexport CXX
|
|
||||||
|
|
||||||
all:
|
|
||||||
bash script.sh
|
|
96
tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs
Normal file
96
tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// 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("rust_plus_one_global_asm", "rust_plus_one_global_asm.checks");
|
||||||
|
|
||||||
|
check("cc_plus_one_c", "cc_plus_one_c.checks");
|
||||||
|
check("cc_plus_one_c_asm", "cc_plus_one_c_asm.checks");
|
||||||
|
check("cc_plus_one_cxx", "cc_plus_one_cxx.checks");
|
||||||
|
check("cc_plus_one_cxx_asm", "cc_plus_one_cxx_asm.checks");
|
||||||
|
check("cc_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::<Vec<&str>>().join(",");
|
||||||
|
assert!(!func.is_empty());
|
||||||
|
let dump = llvm_objdump()
|
||||||
|
.input("enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave")
|
||||||
|
.args(&["--demangle", &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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -exuo pipefail
|
|
||||||
|
|
||||||
function build {
|
|
||||||
CRATE=enclave
|
|
||||||
|
|
||||||
mkdir -p "${WORK_DIR}"
|
|
||||||
pushd "${WORK_DIR}"
|
|
||||||
rm -rf "${CRATE}"
|
|
||||||
cp -a "${TEST_DIR}"/enclave .
|
|
||||||
pushd $CRATE
|
|
||||||
echo "${WORK_DIR}"
|
|
||||||
# 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.
|
|
||||||
env RUSTC_BOOTSTRAP=1
|
|
||||||
cargo -v run --target "${TARGET}"
|
|
||||||
popd
|
|
||||||
popd
|
|
||||||
}
|
|
||||||
|
|
||||||
function check {
|
|
||||||
local func_re="$1"
|
|
||||||
local checks="${TEST_DIR}/$2"
|
|
||||||
local asm=""
|
|
||||||
local objdump="${LLVM_BIN_DIR}/llvm-objdump"
|
|
||||||
local filecheck="${LLVM_BIN_DIR}/FileCheck"
|
|
||||||
local enclave=${WORK_DIR}/enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave
|
|
||||||
|
|
||||||
asm=$(mktemp)
|
|
||||||
func="$(${objdump} --syms --demangle "${enclave}" | \
|
|
||||||
grep --only-matching -E "[[:blank:]]+${func_re}\$" | \
|
|
||||||
sed -e 's/^[[:space:]]*//' )"
|
|
||||||
${objdump} --disassemble-symbols="${func}" --demangle \
|
|
||||||
"${enclave}" > "${asm}"
|
|
||||||
${filecheck} --input-file "${asm}" "${checks}"
|
|
||||||
|
|
||||||
if [ "${func_re}" != "rust_plus_one_global_asm" ] &&
|
|
||||||
[ "${func_re}" != "cmake_plus_one_c_global_asm" ] &&
|
|
||||||
[ "${func_re}" != "cmake_plus_one_cxx_global_asm" ]; then
|
|
||||||
# The assembler cannot avoid explicit `ret` instructions. Sequences
|
|
||||||
# of `shlq $0x0, (%rsp); lfence; retq` are used instead.
|
|
||||||
# https://www.intel.com/content/www/us/en/developer/articles/technical/
|
|
||||||
# software-security-guidance/technical-documentation/load-value-injection.html
|
|
||||||
${filecheck} --implicit-check-not ret --input-file "${asm}" "${checks}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
build
|
|
||||||
|
|
||||||
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 'std::io::stdio::_print::[[:alnum:]]+' print.without_frame_pointers.checks
|
|
||||||
check rust_plus_one_global_asm rust_plus_one_global_asm.checks
|
|
||||||
|
|
||||||
check cc_plus_one_c cc_plus_one_c.checks
|
|
||||||
check cc_plus_one_c_asm cc_plus_one_c_asm.checks
|
|
||||||
check cc_plus_one_cxx cc_plus_one_cxx.checks
|
|
||||||
check cc_plus_one_cxx_asm cc_plus_one_cxx_asm.checks
|
|
||||||
check cc_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
|
|
Loading…
Add table
Add a link
Reference in a new issue