Skip to content

Commit

Permalink
refactor and expand native-lib tests, comment fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Strophox committed Aug 30, 2024
1 parent 7bdec46 commit dcdb101
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 20 deletions.
6 changes: 4 additions & 2 deletions src/tools/miri/src/alloc_addresses/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
AllocKind::Function | AllocKind::VTable => {
// Allocate some dummy memory to get a unique address for this function/vtable.
let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
// We don't need to expose these bytes as nobody is allowed to access them.
let addr = alloc_bytes.as_ptr().addr().try_into().unwrap();
// Leak the underlying memory to ensure it remains unique.
std::mem::forget(alloc_bytes);
Expand Down Expand Up @@ -372,10 +373,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
.prepared_alloc_bytes
.remove(&id)
.unwrap_or_else(|| panic!("alloc bytes for {id:?} have not been prepared"));
// Sanity-check that the prepared allocation has the right size and alignment.
assert!(prepared_alloc_bytes.as_ptr().is_aligned_to(align.bytes_usize()));
assert_eq!(prepared_alloc_bytes.len(), bytes.len());
// SAFETY: `alloc_bytes` and `bytes` span the same number of bytes.
unsafe { prepared_alloc_bytes.as_mut_ptr().copy_from(bytes.as_ptr(), bytes.len()) };
// Copy allocation contents into prepared memory.
prepared_alloc_bytes.copy_from_slice(bytes);
prepared_alloc_bytes
} else {
MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(&*bytes), align)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
CODEABI_1.0 {
# Define which symbols to export.
global:
# scalar_arguments.c
add_one_int;
printer;
ptr_printer;
test_stack_spill;
get_unsigned_int;
add_int16;
add_short_to_long;

# ptr_read_access.c
print_pointer;
access_simple;
access_nested;
access_static;

# The rest remains private.
local: *;
};
};
89 changes: 89 additions & 0 deletions src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//@only-target-linux
//@only-on-host

fn main() {
test_pointer();

test_simple();

test_nested();

test_static();
}

// Test function that dereferences a pointer and prints its contents from C.
fn test_pointer() {
extern "C" {
fn print_pointer(ptr: *mut i32);
}

let mut x = 42;
let ptr = &mut x as *mut i32;

unsafe { print_pointer(ptr) };
}

// Test function that dereferences a simple struct pointer and accesses a field.
fn test_simple() {
#[repr(C)]
struct Simple {
field: i32
}

extern "C" {
fn access_simple(s_ptr: *mut Simple) -> i32;
}

let mut simple = Simple { field: -42 };
let s_ptr = &mut simple as *mut Simple;

let result = unsafe { access_simple(s_ptr) };
assert_eq!(result, -42);
}

// Test function that dereferences nested struct pointers and accesses fields.
fn test_nested() {
use std::ptr::NonNull;

#[derive(Debug, PartialEq, Eq)]
#[repr(C)]
struct Nested {
value: i32,
next: Option<NonNull<Nested>>,
}

extern "C" {
fn access_nested(n_ptr: *mut Nested) -> i32;
}

let mut nested_0 = Nested { value: 0, next: None };
let mut nested_1 = Nested { value: 1, next: NonNull::new(&mut nested_0) };
let mut nested_2 = Nested { value: 2, next: NonNull::new(&mut nested_1) };
let mut nested_3 = Nested { value: 3, next: NonNull::new(&mut nested_2) };
let n_ptr = &mut nested_3 as *mut Nested;

let result = unsafe { access_nested(n_ptr) };
assert_eq!(result, 0);
}

// Test function that dereferences static struct pointers and accesses fields.
fn test_static() {

#[repr(C)]
struct Static {
value: i32,
recurse: &'static Static,
}

extern "C" {
fn access_static(n_ptr: *const Static) -> i32;
}

static STATIC: Static = Static {
value: 9001,
recurse: &STATIC,
};

let result = unsafe { access_static(&STATIC) };
assert_eq!(result, 9001);
}
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
printing from C
printing pointer dereference from C: 42
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ extern "C" {
fn add_short_to_long(x: i16, y: i64) -> i64;
fn get_unsigned_int() -> u32;
fn printer();
fn ptr_printer(ptr: *mut i32);
}

fn main() {
Expand All @@ -43,10 +42,5 @@ fn main() {

// test void function that prints from C
printer();

// test void function that dereferences a pointer and prints its contents from C
let mut x = 42i32;
let ptr = &mut x as *mut i32;
ptr_printer(ptr);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
printing from C
47 changes: 47 additions & 0 deletions src/tools/miri/tests/native-lib/ptr_read_access.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <stdio.h>

/* Test. */

void print_pointer(int *ptr) {
printf("printing pointer dereference from C: %d\n", *ptr);
}

/* Test. */

typedef struct Simple {
int field;
} Simple;

int access_simple(Simple *s_ptr) {
return s_ptr->field;
}

/* Test. */

typedef struct Nested {
int value;
struct Nested *next;
} Nested;

// Returns the innermost/last `value` of the `Nested` pointer chain.
int access_nested(Nested *n_ptr) {
// Edge case: `n_ptr == NULL`, first Nested is None).
if (!n_ptr) { return 0; }

while (n_ptr->next) {
n_ptr = n_ptr->next;
}

return n_ptr->value;
}

/* Test. */

typedef struct Static {
int value;
struct Static *recurse;
} Static;

int access_static(Static *s_ptr) {
return s_ptr->recurse->recurse->value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ void printer() {
printf("printing from C\n");
}

void ptr_printer(int *ptr) {
printf("printing pointer dereference from C: %d\n", *ptr);
}

// function with many arguments, to test functionality when some args are stored
// on the stack
int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
Expand All @@ -28,4 +24,4 @@ short add_int16(short x) {

long add_short_to_long(short x, long y) {
return x + y;
}
}
11 changes: 8 additions & 3 deletions src/tools/miri/tests/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,23 @@ fn build_native_lib() -> PathBuf {
// Create the directory if it does not already exist.
std::fs::create_dir_all(&so_target_dir)
.expect("Failed to create directory for shared object file");
let so_file_path = so_target_dir.join("libtestlib.so");
/*let so_file_path = so_target_dir.join("scalar_arguments.so");*/
let so_file_path = so_target_dir.join(format!("native-lib.so"));
let cc_output = Command::new(cc)
.args([
"-shared",
"-o",
so_file_path.to_str().unwrap(),
"tests/native-lib/test.c",
&format!("tests/native-lib/scalar_arguments.c"),
&format!("tests/native-lib/ptr_read_access.c"),
// Only add the functions specified in libcode.version to the shared object file.
// This is to avoid automatically adding `malloc`, etc.
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
"-fPIC",
"-Wl,--version-script=tests/native-lib/libtest.map",
&format!("-Wl,--version-script=tests/native-lib/native-lib.map"),
"-Werror",
"-Wextra",
"-Wpedantic",
])
.output()
.expect("failed to generate shared object file for testing native function calls");
Expand Down

0 comments on commit dcdb101

Please sign in to comment.