Skip to content

Instantly share code, notes, and snippets.

@huonw

huonw/example.rs Secret

Last active October 1, 2017 16:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huonw/be05427dc80e44f1a594 to your computer and use it in GitHub Desktop.
Save huonw/be05427dc80e44f1a594 to your computer and use it in GitHub Desktop.
Syntax extension to randomise the order of struct fields, e.g. for security, like Grsecurity http://en.wikibooks.org/wiki/Grsecurity/Appendix/Grsecurity_and_PaX_Configuration_Options#Randomize_layout_of_sensitive_kernel_structures
#![feature(phase, macro_rules, struct_variant)]
// compile with `--pretty expanded` to see the order of the fields/variants change
#[phase(syntax)] extern crate random_layout;
#[random_layout]
struct Foo {
x: uint,
y: u8,
z: f64,
w: i8,
t: StrBuf
}
#[random_layout]
enum Bar {
A,
// this order doesn't change:
B(uint, u8, f64, i8),
C { x: uint, y: u8, z: f64 }
}
// Errors:
// #[random_layout] struct Baz(u8, uint);
// #[random_layout] enum Qux { X = 1, Y, Z }
// #[random_layout] trait Wat {}
#![crate_id = "random_layout"]
#![crate_type = "dylib"]
#![experimental]
#![feature(macro_registrar, macro_rules, managed_boxes)]
//! Randomises the ordering of enum variants and named struct fields
//! (including struct variant fields).
//!
//! This cannot (and does not) touch enums with any defined
//! discriminants (e.g. enum Foo { A = 1, B, C }) or the ordering of
//! fields without names (tuple structs and normal enum variants).
extern crate syntax;
extern crate rand;
use rand::{TaskRng, Rng};
use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{SyntaxExtension, ExtCtxt, ItemModifier};
use syntax::parse::token;
#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |ast::Name, SyntaxExtension|) {
register(token::intern("random_layout"),
ItemModifier(random_layout));
}
fn random_layout(cx: &mut ExtCtxt, sp: codemap::Span,
_attr: @ast::MetaItem, item: @ast::Item) -> @ast::Item {
let mut rng = rand::task_rng();
let node = match item.node {
ast::ItemStruct(sd, ref gen) => {
if sd.ctor_id.is_some() {
cx.span_err(sp, "cannot randomize layout of tuple struct");
return item
}
ast::ItemStruct(@randomise_struct_def(&mut rng, sd), gen.clone())
}
ast::ItemEnum(ref ed, ref gen) => {
for v in ed.variants.iter() {
if v.node.disr_expr.is_some() {
cx.span_err(sp,
"cannot randomize layout of enum with defined discriminants");
return item
}
}
let mut variants = ed.variants.iter().map(|v| {
let kind = match v.node.kind {
// need names to randomise
ref k @ ast::TupleVariantKind(_) => k.clone(),
ast::StructVariantKind(sd) => {
ast::StructVariantKind(@randomise_struct_def(&mut rng, sd))
}
};
@codemap::respan(v.span,
ast::Variant_ {
kind: kind,
attrs: v.node.attrs.clone(),
.. v.node
})
}).collect::<Vec<_>>();
// randomise the order of the variants (i.e. randomise
// their discriminants)
rng.shuffle(variants.as_mut_slice());
ast::ItemEnum(ast::EnumDef { variants: variants }, gen.clone())
}
_ => {
cx.span_err(sp,
"`#[random_layout]` can only be used on enum and struct definitions");
return item
}
};
@ast::Item {
node: node,
attrs: item.attrs.clone(),
.. *item
}
}
fn randomise_struct_def(rng: &mut TaskRng, sd: &ast::StructDef) -> ast::StructDef {
assert!(sd.ctor_id.is_none());
let mut fields = sd.fields.clone();
rng.shuffle(fields.as_mut_slice());
ast::StructDef {
fields: fields,
.. *sd
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment