Auto merge of #128384 - dheaton-arm:mte-test, r= · rust-lang/rust@ff7236e (original) (raw)
File tree
11 files changed
lines changed
- src/tools/compiletest/src
11 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -170,6 +170,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ | ||
170 | 170 | "only-32bit", |
171 | 171 | "only-64bit", |
172 | 172 | "only-aarch64", |
173 | +"only-aarch64-unknown-linux-gnu", | |
173 | 174 | "only-apple", |
174 | 175 | "only-arm", |
175 | 176 | "only-avr", |
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
@@ -0,0 +1,43 @@ | |||
1 | +#ifndef __BAR_H | ||
2 | +#define __BAR_H | ||
3 | + | ||
4 | +#include <sys/mman.h> | ||
5 | +#include <sys/auxv.h> | ||
6 | +#include <sys/prctl.h> | ||
7 | +#include <unistd.h> | ||
8 | +#include <stdio.h> | ||
9 | + | ||
10 | +// Set the allocation tag on the destination address using the STG instruction. | ||
11 | +#define set_tag(tagged_addr) do { \ | ||
12 | + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ | ||
13 | +} while (0) | ||
14 | + | ||
15 | +int mte_enabled() { | ||
16 | +return (getauxval(AT_HWCAP2)) & HWCAP2_MTE; | ||
17 | +} | ||
18 | + | ||
19 | +void *alloc_page() { | ||
20 | +// Enable MTE with synchronous checking | ||
21 | +if (prctl(PR_SET_TAGGED_ADDR_CTRL, | ||
22 | +PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT), | |
23 | +0, 0, 0)) | ||
24 | + { | ||
25 | +perror("prctl() failed"); | ||
26 | + } | ||
27 | + | ||
28 | +// Using `mmap` allows us to ensure that, on systems which support MTE, the allocated | ||
29 | +// memory is 16-byte aligned for MTE. | ||
30 | +// This also allows us to explicitly specify whether the region should be protected by | ||
31 | +// MTE or not. | ||
32 | +if (mte_enabled()) { | ||
33 | +void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), | ||
34 | +PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | |
35 | +-1, 0); | ||
36 | + } else { | ||
37 | +void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), | ||
38 | +PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, | |
39 | +-1, 0); | ||
40 | + } | ||
41 | +} | ||
42 | + | ||
43 | +#endif // __BAR_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <stdint.h> | |
4 | +#include "bar.h" | |
5 | + | |
6 | +extern void foo(char*); | |
7 | + | |
8 | +void bar(char *ptr) { | |
9 | +if (((uintptr_t)ptr >> 56) != 0x1f) { | |
10 | +fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); | |
11 | +exit(1); | |
12 | + } | |
13 | +} | |
14 | + | |
15 | +int main(void) | |
16 | +{ | |
17 | +float *ptr = alloc_page(); | |
18 | +if (ptr == MAP_FAILED) | |
19 | + { | |
20 | +perror("mmap() failed"); | |
21 | +return EXIT_FAILURE; | |
22 | + } | |
23 | + | |
24 | +// Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), | |
25 | +// and a different value in the ignored top 4 bits. | |
26 | +ptr = (float *)((uintptr_t)ptr | 0x1fl << 56); | |
27 | + | |
28 | +if (mte_enabled()) { | |
29 | +set_tag(ptr); | |
30 | + } | |
31 | + | |
32 | +ptr[0] = 2.0f; | |
33 | +ptr[1] = 1.5f; | |
34 | + | |
35 | +foo(ptr); // should change the contents of the page and call `bar` | |
36 | + | |
37 | +if (ptr[0] != 0.5f | | |
38 | +fprintf(stderr, "invalid data in memory; expected '0.5 0.2', got '%f %f'\n", | |
39 | +ptr[0], ptr[1]); | |
40 | +return EXIT_FAILURE; | |
41 | + } | |
42 | + | |
43 | +return 0; | |
44 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <stdint.h> | |
4 | +#include "bar.h" | |
5 | + | |
6 | +typedef void (*fp)(int (*)()); | |
7 | + | |
8 | +extern void foo(fp); | |
9 | + | |
10 | +void bar(int (*ptr)()) { | |
11 | +if (((uintptr_t)ptr >> 56) != 0x2f) { | |
12 | +fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); | |
13 | +exit(1); | |
14 | + } | |
15 | + | |
16 | +int r = (*ptr)(); | |
17 | +if (r != 32) { | |
18 | +fprintf(stderr, "invalid return value; expected 32, got '%d'\n", r); | |
19 | +exit(1); | |
20 | + } | |
21 | +} | |
22 | + | |
23 | +int main(void) | |
24 | +{ | |
25 | +fp ptr = alloc_page(); | |
26 | +if (ptr == MAP_FAILED) | |
27 | + { | |
28 | +perror("mmap() failed"); | |
29 | +return EXIT_FAILURE; | |
30 | + } | |
31 | + | |
32 | +// Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), | |
33 | +// and a different value in the ignored top 4 bits. | |
34 | +ptr = (fp)((uintptr_t)&bar | 0x1fl << 56); | |
35 | + | |
36 | +foo(ptr); | |
37 | + | |
38 | +return 0; | |
39 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <stdint.h> | |
4 | +#include "bar.h" | |
5 | + | |
6 | +extern void foo(unsigned int *); | |
7 | + | |
8 | +void bar(char *ptr) { | |
9 | +if (((uintptr_t)ptr >> 56) != 0x1f) { | |
10 | +fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); | |
11 | +exit(1); | |
12 | + } | |
13 | +} | |
14 | + | |
15 | +int main(void) | |
16 | +{ | |
17 | +// Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. | |
18 | +// It's only necessary that the tag is preserved across FFI bounds for this test. | |
19 | +unsigned int *ptr; | |
20 | + | |
21 | +ptr = alloc_page(); | |
22 | +if (ptr == MAP_FAILED) | |
23 | + { | |
24 | +perror("mmap() failed"); | |
25 | +return EXIT_FAILURE; | |
26 | + } | |
27 | + | |
28 | +// Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), | |
29 | +// and a different value in the ignored top 4 bits. | |
30 | +ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); | |
31 | + | |
32 | +if (mte_enabled()) { | |
33 | +set_tag(ptr); | |
34 | + } | |
35 | + | |
36 | +ptr[0] = 61; | |
37 | +ptr[1] = 62; | |
38 | + | |
39 | +foo(ptr); // should change the contents of the page to start with 0x63 0x64 and call `bar` | |
40 | + | |
41 | +if (ptr[0] != 0x63 | | |
42 | +fprintf(stderr, "invalid data in memory; expected '63 64', got '%d %d'\n", ptr[0], ptr[1]); | |
43 | +return EXIT_FAILURE; | |
44 | + } | |
45 | + | |
46 | +return 0; | |
47 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <stdint.h> | |
4 | +#include "bar.h" | |
5 | + | |
6 | +extern void foo(char*); | |
7 | + | |
8 | +void bar(char *ptr) { | |
9 | +if (((uintptr_t)ptr >> 56) != 0x2f) { | |
10 | +fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); | |
11 | +exit(1); | |
12 | + } | |
13 | + | |
14 | +if (strcmp(ptr, "cd")) { | |
15 | +fprintf(stderr, "invalid data in memory; expected 'cd', got '%s'\n", ptr); | |
16 | +exit(1); | |
17 | + } | |
18 | +} | |
19 | + | |
20 | +int main(void) | |
21 | +{ | |
22 | +// Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. | |
23 | +// It's only necessary that the tag is preserved across FFI bounds for this test. | |
24 | +char *ptr; | |
25 | + | |
26 | +ptr = alloc_page(); | |
27 | +if (ptr == MAP_FAILED) | |
28 | + { | |
29 | +perror("mmap() failed"); | |
30 | +return EXIT_FAILURE; | |
31 | + } | |
32 | + | |
33 | +// Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), | |
34 | +// and a different value in the ignored top 4 bits. | |
35 | +ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); | |
36 | + | |
37 | +if (mte_enabled()) { | |
38 | +set_tag(ptr); | |
39 | + } | |
40 | + | |
41 | +ptr[0] = 'a'; | |
42 | +ptr[1] = 'b'; | |
43 | +ptr[2] = '\0'; | |
44 | + | |
45 | +foo(ptr); | |
46 | + | |
47 | +return 0; | |
48 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
1 | +#![crate_type = "cdylib"] | |
2 | +#![crate_name = "foo"] | |
3 | + | |
4 | +use std::os::raw::c_float; | |
5 | + | |
6 | +extern "C" { | |
7 | +fn bar(ptr: *const c_float); | |
8 | +} | |
9 | + | |
10 | +#[no_mangle] | |
11 | +pub extern "C" fn foo(ptr: *mut c_float) { | |
12 | +assert_eq!((ptr as usize) >> 56, 0x1f); | |
13 | + | |
14 | +unsafe { | |
15 | +*ptr = 0.5; | |
16 | +*ptr.wrapping_add(1) = 0.2; | |
17 | +bar(ptr); | |
18 | +} | |
19 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
1 | +#![crate_type = "cdylib"] | |
2 | +#![crate_name = "foo"] | |
3 | + | |
4 | +extern "C" fn ret32() -> i32 { | |
5 | +32 | |
6 | +} | |
7 | + | |
8 | +#[no_mangle] | |
9 | +pub extern "C" fn foo(ptr: extern "C" fn(extern "C" fn() -> i32)) { | |
10 | +assert_eq!((ptr as usize) >> 56, 0x1f); | |
11 | + | |
12 | +// Store an arbitrary tag in the tag bits, and convert back to the correct pointer type. | |
13 | +let p = ((ret32 as usize) | (0x2f << 56)) as *const (); | |
14 | +let p: extern "C" fn() -> i32 = unsafe { std::mem::transmute(p) }; | |
15 | + | |
16 | +unsafe { ptr(p) } | |
17 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
1 | +#![crate_type = "cdylib"] | |
2 | +#![crate_name = "foo"] | |
3 | + | |
4 | +use std::os::raw::c_uint; | |
5 | + | |
6 | +extern "C" { | |
7 | +fn bar(ptr: *const c_uint); | |
8 | +} | |
9 | + | |
10 | +#[no_mangle] | |
11 | +pub extern "C" fn foo(ptr: *mut c_uint) { | |
12 | +assert_eq!((ptr as usize) >> 56, 0x1f); | |
13 | + | |
14 | +unsafe { | |
15 | +*ptr = 0x63; | |
16 | +*ptr.wrapping_add(1) = 0x64; | |
17 | +bar(ptr); | |
18 | +} | |
19 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
1 | +#![crate_type = "cdylib"] | |
2 | +#![crate_name = "foo"] | |
3 | + | |
4 | +use std::arch::asm; | |
5 | +use std::ffi::{CStr, CString}; | |
6 | +use std::os::raw::c_char; | |
7 | + | |
8 | +extern "C" { | |
9 | +fn bar(ptr: *const c_char); | |
10 | +} | |
11 | + | |
12 | +#[no_mangle] | |
13 | +pub extern "C" fn foo(ptr: *const c_char) { | |
14 | +assert_eq!((ptr as usize) >> 56, 0x1f); | |
15 | + | |
16 | +let s = unsafe { CStr::from_ptr(ptr) }; | |
17 | +assert_eq!(s.to_str().unwrap(), "ab"); | |
18 | + | |
19 | +let s = CString::from_vec_with_nul("cd\0".into()).unwrap(); | |
20 | +let mut p = ((s.as_ptr() as usize) | (0x2f << 56)) as *const c_char; | |
21 | +unsafe { | |
22 | +#[cfg(target_feature = "mte")] | |
23 | +asm!("stg {p}, [{p}]", p = inout(reg) p); | |
24 | + | |
25 | +bar(p); | |
26 | +} | |
27 | +} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
1 | +// Tests that MTE tags and values stored in the top byte of a pointer (TBI) are | |
2 | +// preserved across FFI boundaries (C <-> Rust). | |
3 | +// This test does not require MTE: whilst the test will use MTE if available, if it is not, | |
4 | +// arbitrary tag bits are set using TBI. | |
5 | + | |
6 | +// This test is only valid for AArch64. | |
7 | +// The linker must be explicitly specified when cross-compiling, so it is limited to | |
8 | +// `aarch64-unknown-linux-gnu`. | |
9 | +//@ only-aarch64-unknown-linux-gnu | |
10 | + | |
11 | +use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target}; | |
12 | + | |
13 | +fn main() { | |
14 | +run_test("int"); | |
15 | +run_test("float"); | |
16 | +run_test("string"); | |
17 | +run_test("function"); | |
18 | +} | |
19 | + | |
20 | +fn run_test(variant: &str) { | |
21 | +let flags = { | |
22 | +let mut flags = extra_c_flags(); | |
23 | + flags.push("-march=armv8.5-a+memtag"); | |
24 | + flags | |
25 | +}; | |
26 | +println!("{variant} test..."); | |
27 | +rustc() | |
28 | +.input(format!("foo_{variant}.rs")) | |
29 | +.target(target()) | |
30 | +.linker("aarch64-linux-gnu-gcc") | |
31 | +.run(); | |
32 | +cc().input(format!("bar_{variant}.c")) | |
33 | +.input(dynamic_lib_name("foo")) | |
34 | +.out_exe("test") | |
35 | +.args(&flags) | |
36 | +.run(); | |
37 | +run("test"); | |
38 | +} |