Crates.io | randstruct |
lib.rs | randstruct |
version | 1.0.2 |
source | src |
created_at | 2024-10-27 16:01:23.95955 |
updated_at | 2024-10-27 16:14:32.504434 |
description | This crate implements a subset of the features of the GCC randomize_layout plugin. |
homepage | |
repository | https://github.com/LethargicLeprechaun/randstruct |
max_upload_size | |
id | 1424725 |
size | 39,158 |
This crate implements a subset of the features of the GCC randomize_layout plugin.
[dependencies]
randstruct = "1.0"
The randomize_layout plugin is used by software to harden structs that contain sensitive/privileged data. One notable use case is a hardened Linux Kernel which uses it to remove predictable offsets of sensitive data in it's structures.
Consider the following as a trivial example:
struct TestStruct {
const char *elem0;
const char *elem1;
unsigned int elem3;
unsigned short elem4;
} __attribute__((randomize_layout));
Which may produce the following:
struct TestStruct {
const char *elem1;
unsigned short elem4;
const char *elem0;
unsigned int elem3;
} __attribute__((randomize_layout));
As is evident in the above example, the offsets of all the members have been changed.
Rust uses #[repr(C)]
to force the underlying C layout of a particular structure. In the above example, a Rust equivalent structure would be:
#[repr(C)]
struct TestStruct {
elem0: *const core::ffi::c_char,
elem1: *const core::ffi::c_char,
elem2: core::ffi::c_uint,
elem3: core::ffi::c_ushort,
}
These Rust structures are typically either programmatically generated through a tool like bindgen or manually created by hand.
The generation of these structures is source-based; however, the GCC randomize_layout attribute will randomize at build time. Meaning, the source-level representation of the structure is not an accurate depiction of the structure once compiled.
This presents an issue in FFI when you want to process a C structure or pass a Rust structure through C code.
To replicate the exact structure member shuffling that randomize_layout achieves, this crate reimplements the logic to operate on Rust proc-macro TokenStreams instead of the GCC AST values.
Continuing the example above, we have a C structure that uses the randomize_layout plugin.
struct TestStruct {
const char *elem0;
const char *elem1;
unsigned int elem3;
unsigned short elem4;
} __attribute__((randomize_layout));
The equivalent Rust structure (with this crate) looks like the following:
#[repr(C)]
#[randomize_layout]
struct TestStruct {
elem0: *const core::ffi::c_char,
elem1: *const core::ffi::c_char,
elem2: core::ffi::c_uint,
elem3: core::ffi::c_ushort,
}
That is it. That is all that is required. The C and Rust can be compiled as separate compilation units, and they will be able to interoperate safely.
You need to tell the crate where the header file is that contains the randstruct seed through the SEED_HEADER_FILE
environment variable. For example, the below will suffice:
SEED_HEADER_FILE=`pwd`/seed.h cargo run
Where seed.h should contain a string called randstruct_seed
, this header should be the same header as the one used by the GCC plugin. An exemplar header file is shown below:
const char *randstruct_seed = "123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234";