Skip to content

Commit 6369a98

Browse files
committed
Auto merge of #77008 - fortanix:raoul/lvi-tests, r=Mark-Simulacrum
LVI hardening tests Mitigating the speculative execution LVI attack against SGX enclaves requires compiler changes (i.e., adding lfences). This pull requests adds various tests to check if this happens correctly.
2 parents db7ee7c + 159d11f commit 6369a98

32 files changed

+519
-2
lines changed

src/bootstrap/native.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,14 @@ impl Step for TestHelpers {
630630
if builder.config.dry_run {
631631
return;
632632
}
633-
let target = self.target;
633+
// The x86_64-fortanix-unknown-sgx target doesn't have a working C
634+
// toolchain. However, some x86_64 ELF objects can be linked
635+
// without issues. Use this hack to compile the test helpers.
636+
let target = if self.target == "x86_64-fortanix-unknown-sgx" {
637+
TargetSelection::from_user("x86_64-unknown-linux-gnu")
638+
} else {
639+
self.target
640+
};
634641
let dst = builder.test_helpers_out(target);
635642
let src = builder.src.join("src/test/auxiliary/rust_test_helpers.c");
636643
if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
@@ -654,7 +661,6 @@ impl Step for TestHelpers {
654661
}
655662
cfg.compiler(builder.cc(target));
656663
}
657-
658664
cfg.cargo_metadata(false)
659665
.out_dir(&dst)
660666
.target(&target.triple)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test LVI load hardening on SGX enclave code
2+
3+
// assembly-output: emit-asm
4+
// compile-flags: --crate-type staticlib
5+
// only-x86_64-fortanix-unknown-sgx
6+
7+
#[no_mangle]
8+
pub extern fn plus_one(r: &mut u64) {
9+
*r = *r + 1;
10+
}
11+
12+
// CHECK: plus_one
13+
// CHECK: lfence
14+
// CHECK-NEXT: addq
15+
// CHECK: popq [[REGISTER:%[a-z]+]]
16+
// CHECK-NEXT: lfence
17+
// CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Test LVI ret hardening on generic rust code
2+
3+
// assembly-output: emit-asm
4+
// compile-flags: --crate-type staticlib
5+
// only-x86_64-fortanix-unknown-sgx
6+
7+
#[no_mangle]
8+
pub extern fn myret() {}
9+
// CHECK: myret:
10+
// CHECK: popq [[REGISTER:%[a-z]+]]
11+
// CHECK-NEXT: lfence
12+
// CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Test LVI load hardening on SGX inline assembly code
2+
3+
// assembly-output: emit-asm
4+
// compile-flags: --crate-type staticlib
5+
// only-x86_64-fortanix-unknown-sgx
6+
7+
#![feature(asm)]
8+
9+
#[no_mangle]
10+
pub extern fn get(ptr: *const u64) -> u64 {
11+
let value : u64;
12+
unsafe {
13+
asm!(".start_inline_asm:",
14+
"mov {}, [{}]",
15+
".end_inline_asm:",
16+
out(reg) value,
17+
in(reg) ptr);
18+
}
19+
value
20+
}
21+
22+
// CHECK: get
23+
// CHECK: .start_inline_asm
24+
// CHECK-NEXT: movq
25+
// CHECK-NEXT: lfence
26+
// CHECK-NEXT: .end_inline_asm
27+
28+
#[no_mangle]
29+
pub extern fn myret() {
30+
unsafe {
31+
asm!(".start_myret_inline_asm:
32+
ret
33+
.end_myret_inline_asm:");
34+
}
35+
}
36+
37+
// CHECK: myret
38+
// CHECK: .start_myret_inline_asm
39+
// CHECK-NEXT: shlq $0, (%rsp)
40+
// CHECK-NEXT: lfence
41+
// CHECK-NEXT: retq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-include ../../run-make-fulldeps/tools.mk
2+
3+
#only-x86_64-fortanix-unknown-sgx
4+
5+
# For cargo setting
6+
export RUSTC := $(RUSTC_ORIGINAL)
7+
export LD_LIBRARY_PATH := $(HOST_RPATH_DIR)
8+
# We need to be outside of 'src' dir in order to run cargo
9+
export WORK_DIR := $(TMPDIR)
10+
export TEST_DIR := $(shell pwd)
11+
12+
## clean up unused env variables which might cause harm.
13+
unexport RUSTC_LINKER
14+
unexport RUSTC_BOOTSTRAP
15+
unexport RUST_BUILD_STAGE
16+
unexport RUST_TEST_THREADS
17+
unexport RUST_TEST_TMPDIR
18+
unexport AR
19+
unexport CC
20+
unexport CXX
21+
22+
all:
23+
bash script.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CHECK: cc_plus_one_asm
2+
CHECK-NEXT: movl
3+
CHECK-NEXT: lfence
4+
CHECK-NEXT: inc
5+
CHECK-NEXT: notq (%rsp)
6+
CHECK-NEXT: notq (%rsp)
7+
CHECK-NEXT: lfence
8+
CHECK-NEXT: retq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CHECK: cc_plus_one_c
2+
CHECK: lfence
3+
CHECK: popq
4+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
5+
CHECK-NEXT: lfence
6+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CHECK: cc_plus_one_c_asm
2+
CHECK: lfence
3+
CHECK: lfence
4+
CHECK: lfence
5+
CHECK: lfence
6+
CHECK: lfence
7+
CHECK-NEXT: incl
8+
CHECK-NEXT: jmp
9+
CHECK-NEXT: shlq $0, (%rsp)
10+
CHECK-NEXT: lfence
11+
CHECK-NEXT: retq
12+
CHECK: popq
13+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
14+
CHECK-NEXT: lfence
15+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CHECK: cc_plus_one_cxx
2+
CHECK: lfence
3+
CHECK: popq
4+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
5+
CHECK-NEXT: lfence
6+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CHECK: cc_plus_one_cxx_asm
2+
CHECK: lfence
3+
CHECK: lfence
4+
CHECK: lfence
5+
CHECK: movl
6+
CHECK: lfence
7+
CHECK: lfence
8+
CHECK-NEXT: incl
9+
CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cc_plus_one_cxx_asm+0x{{[[:xdigit:]]+}}>
10+
CHECK-NEXT: shlq $0, (%rsp)
11+
CHECK-NEXT: lfence
12+
CHECK-NEXT: retq
13+
CHECK: popq
14+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
15+
CHECK-NEXT: lfence
16+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHECK: cmake_plus_one_asm
2+
CHECK-NEXT: movl
3+
CHECK-NEXT: lfence
4+
CHECK-NEXT: incl
5+
CHECK-NEXT: shlq $0, (%rsp)
6+
CHECK-NEXT: lfence
7+
CHECK-NEXT: retq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CHECK: cmake_plus_one_c
2+
CHECK: lfence
3+
CHECK: popq
4+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
5+
CHECK-NEXT: lfence
6+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CHECK: cmake_plus_one_c_asm
2+
CHECK: lfence
3+
CHECK: lfence
4+
CHECK: lfence
5+
CHECK: lfence
6+
CHECK: movl
7+
CHECK: lfence
8+
CHECK-NEXT: incl
9+
CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cmake_plus_one_c_asm+0x{{[[:xdigit:]]+}}>
10+
CHECK-NEXT: shlq $0, (%rsp)
11+
CHECK-NEXT: lfence
12+
CHECK-NEXT: retq
13+
CHECK: popq
14+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
15+
CHECK-NEXT: lfence
16+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CHECK: cmake_plus_one_c_global_asm
2+
CHECK: lfence
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CHECK: cmake_plus_one_cxx
2+
CHECK: lfence
3+
CHECK: popq
4+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
5+
CHECK-NEXT: lfence
6+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CHECK: cmake_plus_one_cxx_asm
2+
CHECK: lfence
3+
CHECK: lfence
4+
CHECK: lfence
5+
CHECK: lfence
6+
CHECK: movl
7+
CHECK: lfence
8+
CHECK-NEXT: incl
9+
CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cmake_plus_one_cxx_asm+0x{{[[:xdigit:]]+}}>
10+
CHECK-NEXT: shlq $0, (%rsp)
11+
CHECK-NEXT: lfence
12+
CHECK-NEXT: retq
13+
CHECK: popq
14+
CHECK-NEXT: popq [[REGISTER:%[a-z]+]]
15+
CHECK-NEXT: lfence
16+
CHECK-NEXT: jmpq *[[REGISTER]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CHECK: cmake_plus_one_cxx_global_asm
2+
CHECK: lfence
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "enclave"
3+
version = "0.1.0"
4+
authors = ["Raoul Strackx <[email protected]>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
11+
[build-dependencies]
12+
cc = "1.0"
13+
cmake = "0.1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
fn main() {
2+
cc::Build::new()
3+
.file("foo.c")
4+
.compile("foo_c");
5+
6+
cc::Build::new()
7+
.file("foo_asm.s")
8+
.compile("foo_asm");
9+
10+
cc::Build::new()
11+
.cpp(true)
12+
.cpp_set_stdlib(None)
13+
.file("foo_cxx.cpp")
14+
.compile("foo_cxx");
15+
16+
// When the cmake crate detects the clang compiler, it passes the
17+
// "--target" argument to the linker which subsequently fails. The
18+
// `CMAKE_C_COMPILER_FORCED` option makes sure that `cmake` does not
19+
// tries to test the compiler. From version 3.6 the option
20+
// `CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY` can be used
21+
// https://cmake.org/cmake/help/v3.5/module/CMakeForceCompiler.html
22+
let dst = cmake::Config::new("libcmake_foo")
23+
.build_target("cmake_foo")
24+
.define("CMAKE_C_COMPILER_FORCED", "1")
25+
.define("CMAKE_CXX_COMPILER_FORCED", "1")
26+
.define("CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY", "1")
27+
.build();
28+
println!("cargo:rustc-link-search=native={}/build/", dst.display());
29+
println!("cargo:rustc-link-lib=static=cmake_foo");
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
int cc_plus_one_c(int *arg) {
2+
return *arg + 1;
3+
}
4+
5+
int cc_plus_one_c_asm(int *arg) {
6+
int value = 0;
7+
8+
asm volatile ( " movl (%1), %0\n"
9+
" inc %0\n"
10+
" jmp 1f\n"
11+
" retq\n" // never executed, but a shortcut to determine how
12+
// the assembler deals with `ret` instructions
13+
"1:\n"
14+
: "=r"(value)
15+
: "r"(arg) );
16+
17+
return value;
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.text
2+
.global cc_plus_one_asm
3+
.type cc_plus_one_asm, @function
4+
cc_plus_one_asm:
5+
movl (%rdi), %eax
6+
inc %eax
7+
retq
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
extern "C" int cc_plus_one_cxx(int *arg);
2+
extern "C" int cc_plus_one_cxx_asm(int *arg);
3+
4+
int cc_plus_one_cxx(int *arg) {
5+
return *arg + 1;
6+
}
7+
8+
int cc_plus_one_cxx_asm(int *arg) {
9+
int value = 0;
10+
11+
asm volatile ( " movl (%1), %0\n"
12+
" inc %0\n"
13+
" jmp 1f\n"
14+
" retq\n" // never executed, but a shortcut to determine how
15+
// the assembler deals with `ret` instructions
16+
"1:\n"
17+
: "=r"(value)
18+
: "r"(arg) );
19+
20+
return value;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
enable_language(C CXX ASM)
2+
3+
set(C_SOURCES
4+
src/foo.c
5+
)
6+
7+
set_source_files_properties(${C_SOURCES}
8+
PROPERTIES
9+
LANGUAGE C)
10+
11+
set(CXX_SOURCES
12+
src/foo_cxx.cpp
13+
)
14+
15+
set_source_files_properties(${CXX_SOURCES}
16+
PROPERTIES
17+
LANGUAGE CXX)
18+
19+
set(ASM_SOURCES
20+
src/foo_asm.s
21+
)
22+
23+
set_source_files_properties(${ASM_SOURCES}
24+
PROPERTIES
25+
LANGUAGE ASM)
26+
27+
set(SOURCES
28+
${C_SOURCES}
29+
${CXX_SOURCES}
30+
${ASM_SOURCES})
31+
32+
add_library(cmake_foo STATIC
33+
${SOURCES})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
int cmake_plus_one_c(int *arg) {
2+
return *arg + 1;
3+
}
4+
5+
int cmake_plus_one_c_asm(int *arg) {
6+
int value = 0;
7+
8+
asm volatile ( " movl (%1), %0\n"
9+
" inc %0\n"
10+
" jmp 1f\n"
11+
" retq\n" // never executed, but a shortcut to determine how
12+
// the assembler deals with `ret` instructions
13+
"1:\n"
14+
: "=r"(value)
15+
: "r"(arg) );
16+
17+
return value;
18+
}
19+
20+
asm(".text\n"
21+
" .global cmake_plus_one_c_global_asm\n"
22+
" .type cmake_plus_one_c_global_asm, @function\n"
23+
"cmake_plus_one_c_global_asm:\n"
24+
" movl (%rdi), %eax\n"
25+
" inc %eax\n"
26+
" retq\n" );

0 commit comments

Comments
 (0)