about summary refs log tree commit diff
path: root/src/tools/miri/tests/native-lib
diff options
context:
space:
mode:
authorNia Espera <a5b6@riseup.net>2025-07-13 18:40:27 +0200
committerRalf Jung <post@ralfj.de>2025-08-31 20:49:47 +0200
commit4dbadd05f853414501401984e7888564df7291d4 (patch)
tree35689b85bc49183f8788ae1006267a2bc8bb0667 /src/tools/miri/tests/native-lib
parent7f6f741ebaa6a396fee50b107a0dbfadc08d6a6c (diff)
native-lib: pass structs to native code
Diffstat (limited to 'src/tools/miri/tests/native-lib')
-rw-r--r--src/tools/miri/tests/native-lib/aggregate_arguments.c42
-rw-r--r--src/tools/miri/tests/native-lib/fail/multi_struct_alloc.rs21
-rw-r--r--src/tools/miri/tests/native-lib/fail/multi_struct_alloc.stderr15
-rw-r--r--src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs19
-rw-r--r--src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr14
-rw-r--r--src/tools/miri/tests/native-lib/fail/uninit_struct.rs27
-rw-r--r--src/tools/miri/tests/native-lib/fail/uninit_struct.stderr15
-rw-r--r--src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs51
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.rs20
-rw-r--r--src/tools/miri/tests/native-lib/ptr_read_access.c10
10 files changed, 234 insertions, 0 deletions
diff --git a/src/tools/miri/tests/native-lib/aggregate_arguments.c b/src/tools/miri/tests/native-lib/aggregate_arguments.c
new file mode 100644
index 00000000000..9c29485e799
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/aggregate_arguments.c
@@ -0,0 +1,42 @@
+#include <stdint.h>
+
+// See comments in build_native_lib()
+#define EXPORT __attribute__((visibility("default")))
+
+/* Test: test_pass_struct */
+
+typedef struct PassMe {
+    int32_t value;
+    int64_t other_value;
+} PassMe;
+
+EXPORT int64_t pass_struct(const PassMe pass_me) {
+  return pass_me.value + pass_me.other_value;
+}
+
+/* Test: test_pass_struct_complex */
+
+typedef struct Part1 {
+    uint16_t high;
+    uint16_t low;
+} Part1;
+
+typedef struct Part2 {
+    uint32_t bits;
+} Part2;
+
+typedef struct ComplexStruct {
+    Part1 part_1;
+    Part2 part_2;
+    uint32_t part_3;
+} ComplexStruct;
+
+EXPORT int32_t pass_struct_complex(const ComplexStruct complex, uint16_t high, uint16_t low, uint32_t bits) {
+  if (complex.part_1.high == high && complex.part_1.low == low
+      && complex.part_2.bits == bits
+      && complex.part_3 == bits)
+    return 0;
+  else {
+    return 1;
+  }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.rs b/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.rs
new file mode 100644
index 00000000000..058beab0594
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.rs
@@ -0,0 +1,21 @@
+//@compile-flags: -Zmiri-permissive-provenance
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct HasPointer {
+    ptr: *const u8,
+}
+
+extern "C" {
+    fn access_struct_ptr(s: HasPointer) -> u8;
+}
+
+fn main() {
+    let vals = [10u8, 20u8];
+    let structs =
+        vec![HasPointer { ptr: &raw const vals[0] }, HasPointer { ptr: &raw const vals[1] }];
+    unsafe {
+        access_struct_ptr(structs[1]);
+        let _val = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr()); //~ ERROR: Undefined Behavior: attempting a read access using <wildcard>
+    };
+}
diff --git a/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.stderr b/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.stderr
new file mode 100644
index 00000000000..44656f9ee50
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/multi_struct_alloc.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: attempting a read access using <wildcard> at ALLOC[0x0], but no exposed tags have suitable permission in the borrow stack for this location
+  --> tests/native-lib/fail/multi_struct_alloc.rs:LL:CC
+   |
+LL | ...al = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x1]
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/multi_struct_alloc.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs
new file mode 100644
index 00000000000..cf8315e0fd9
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs
@@ -0,0 +1,19 @@
+// Only works on Unix targets
+//@ignore-target: windows wasm
+//@only-on-host
+
+#![allow(improper_ctypes)]
+
+pub struct PassMe {
+    pub value: i32,
+    pub other_value: i64,
+}
+
+extern "C" {
+    fn pass_struct(s: PassMe) -> i64;
+}
+
+fn main() {
+    let pass_me = PassMe { value: 42, other_value: 1337 };
+    unsafe { pass_struct(pass_me) }; //~ ERROR: unsupported operation: passing a non-#[repr(C)] struct over FFI
+}
diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr
new file mode 100644
index 00000000000..90e59a31da4
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr
@@ -0,0 +1,14 @@
+error: unsupported operation: passing a non-#[repr(C)] struct over FFI: PassMe
+  --> tests/native-lib/fail/struct_not_extern_c.rs:LL:CC
+   |
+LL |     unsafe { pass_struct(pass_me) };
+   |              ^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here
+   |
+   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/struct_not_extern_c.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.rs b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs
new file mode 100644
index 00000000000..cf61c7f3915
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs
@@ -0,0 +1,27 @@
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct ComplexStruct {
+    part_1: Part1,
+    part_2: Part2,
+    part_3: u32,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct Part1 {
+    high: u16,
+    low: u16,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct Part2 {
+    bits: u32,
+}
+
+extern "C" {
+    fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32;
+}
+
+fn main() {
+    let arg = std::mem::MaybeUninit::<ComplexStruct>::uninit();
+    unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) }; //~ ERROR: Undefined Behavior: constructing invalid value
+}
diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr
new file mode 100644
index 00000000000..0fe6ad9c77b
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value at .part_1.high: encountered uninitialized memory, but expected an integer
+  --> tests/native-lib/fail/uninit_struct.rs:LL:CC
+   |
+LL |     unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) };
+   |                                  ^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/uninit_struct.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
new file mode 100644
index 00000000000..15137c37d7c
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
@@ -0,0 +1,51 @@
+fn main() {
+    test_pass_struct();
+    test_pass_struct_complex();
+}
+
+/// Test passing a basic struct as an argument.
+fn test_pass_struct() {
+    #[repr(C)]
+    struct PassMe {
+        value: i32,
+        other_value: i64,
+    }
+
+    extern "C" {
+        fn pass_struct(s: PassMe) -> i64;
+    }
+
+    let pass_me = PassMe { value: 42, other_value: 1337 };
+    assert_eq!(unsafe { pass_struct(pass_me) }, 42 + 1337);
+}
+
+/// Test passing a more complex struct as an argument.
+fn test_pass_struct_complex() {
+    #[repr(C)]
+    struct ComplexStruct {
+        part_1: Part1,
+        part_2: Part2,
+        part_3: u32,
+    }
+    #[repr(C)]
+    struct Part1 {
+        high: u16,
+        low: u16,
+    }
+    #[repr(C)]
+    struct Part2 {
+        bits: u32,
+    }
+
+    extern "C" {
+        fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32;
+    }
+
+    let high = 0xabcd;
+    let low = 0xef01;
+    let bits = 0xabcdef01;
+
+    let complex =
+        ComplexStruct { part_1: Part1 { high, low }, part_2: Part2 { bits }, part_3: bits };
+    assert_eq!(unsafe { pass_struct_complex(complex, high, low, bits) }, 0);
+}
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
index 4f3c37f00c1..49750a734d6 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
@@ -1,12 +1,14 @@
 //@revisions: trace notrace
 //@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
 //@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
+//@compile-flags: -Zmiri-permissive-provenance
 
 fn main() {
     test_access_pointer();
     test_access_simple();
     test_access_nested();
     test_access_static();
+    test_access_struct_ptr();
 }
 
 /// Test function that dereferences an int pointer and prints its contents from C.
@@ -74,3 +76,21 @@ fn test_access_static() {
 
     assert_eq!(unsafe { access_static(&STATIC) }, 9001);
 }
+
+/// Test exposing provenance from a field within a struct.
+fn test_access_struct_ptr() {
+    #[repr(C)]
+    struct HasPointer {
+        ptr: *const u8,
+    }
+
+    extern "C" {
+        // Return value exists only so the access isn't optimised away.
+        fn access_struct_ptr(s: HasPointer) -> u8;
+    }
+
+    let some_val = 42u8;
+    let ptr = &raw const some_val;
+    unsafe { access_struct_ptr(HasPointer { ptr }) };
+    assert_eq!(some_val, unsafe { *std::ptr::with_exposed_provenance::<u8>(ptr.addr()) })
+}
diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c
index 021eb6adca4..2107d2bc21f 100644
--- a/src/tools/miri/tests/native-lib/ptr_read_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_read_access.c
@@ -55,3 +55,13 @@ EXPORT int32_t access_static(const Static *s_ptr) {
 EXPORT uintptr_t do_one_deref(const int32_t ***ptr) {
   return (uintptr_t)*ptr;
 }
+
+/* Test: test_access_struct_ptr */
+
+typedef struct HasPointer {
+  uint8_t *ptr;
+} HasPointer;
+
+EXPORT uint8_t access_struct_ptr(const HasPointer s) {
+  return *s.ptr;
+}