Skip to content

Commit

Permalink
Add validity check, which is used by safer_ffi
Browse files Browse the repository at this point in the history
  • Loading branch information
p-avital committed Jun 23, 2024
1 parent b4716a7 commit 0adae35
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 217 deletions.
63 changes: 47 additions & 16 deletions stabby-abi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
// Pierre Avital, <[email protected]>
//

use std::{
fmt::Write as FmtWrite,
fs::File,
io::{BufWriter, Write},
path::PathBuf,
};

fn u(mut i: u128) -> String {
let mut result = "UTerm".into();
let mut ids = Vec::new();
Expand All @@ -26,35 +33,59 @@ fn u(mut i: u128) -> String {
result
}

fn main() {
use std::{
fs::File,
io::{BufWriter, Write},
path::PathBuf,
};
fn typenum_unsigned() -> std::io::Result<()> {
const SEQ_MAX: u128 = 1000;
let padding_rs = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("unsigned.rs");
let mut padding_file = BufWriter::new(File::create(padding_rs).unwrap());
let filename = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("unsigned.rs");
let mut file = BufWriter::new(File::create(filename).unwrap());
for i in 0..=SEQ_MAX {
let u = u(i);
writeln!(padding_file, "/// {i}\npub type U{i} = {u};").unwrap();
writeln!(padding_file, "/// {i}\npub type Ux{i:X} = {u};").unwrap();
writeln!(padding_file, "/// {i}\npub type Ub{i:b} = {u};").unwrap();
writeln!(file, "/// {i}\npub type U{i} = {u};")?;
writeln!(file, "/// {i}\npub type Ux{i:X} = {u};")?;
writeln!(file, "/// {i}\npub type Ub{i:b} = {u};")?;
}
for i in 0..39 {
let ipow = 10u128.pow(i);
let u = u(ipow);
writeln!(padding_file, "/// {i}\npub type U10pow{i} = {u};").unwrap();
writeln!(file, "/// {i}\npub type U10pow{i} = {u};")?;
if ipow > SEQ_MAX {
writeln!(padding_file, "/// {i}\npub type U{ipow} = {u};").unwrap();
writeln!(padding_file, "/// {i}\npub type Ux{ipow:X} = {u};").unwrap();
writeln!(padding_file, "/// {i}\npub type Ub{ipow:b} = {u};").unwrap();
writeln!(file, "/// {i}\npub type U{ipow} = {u};")?;
writeln!(file, "/// {i}\npub type Ux{ipow:X} = {u};")?;
writeln!(file, "/// {i}\npub type Ub{ipow:b} = {u};")?;
}
}
for i in 0..128 {
let u = u(1 << i);
writeln!(padding_file, "/// {i}\npub type U2pow{i} = {u};").unwrap();
writeln!(file, "/// {i}\npub type U2pow{i} = {u};")?;
}
Ok(())
}

fn tuples() -> std::io::Result<()> {
let filename = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("tuples.rs");
let mut file = BufWriter::new(File::create(filename).unwrap());
for i in 0..128 {
writeln!(
file,
r##"/// An ABI stable tuple
#[crate::stabby]
pub struct Tuple{i}<{generics}>({fields});
"##,
generics = (0..i).fold(String::new(), |mut acc, it| {
write!(acc, "T{it}, ").unwrap();
acc
}),
fields = (0..i).fold(String::new(), |mut acc, it| {
write!(acc, "pub T{it}, ").unwrap();
acc
}),
)?;
}
Ok(())
}

fn main() {
typenum_unsigned().unwrap();
tuples().unwrap();
if let Ok(toolchain) = std::env::var("RUSTUP_TOOLCHAIN") {
if toolchain.starts_with("nightly") {
println!("cargo:rustc-cfg=stabby_nightly");
Expand Down
46 changes: 45 additions & 1 deletion stabby-abi/src/istable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ pub unsafe trait IStable: Sized {
fn align() -> usize {
Self::Align::USIZE
}
/// Returns `true` if `ptr` points to memory that cannot be a valid value of `Self`.
///
/// Note that this function returning `false` is not a guarantee that the value is valid,
/// as no heuristic can guarantee that. Notably, this heuristic will generally not look
/// through indirections.
///
/// # Safety
/// Calling this may result in UB if `ptr` points to uninitialized memory at offsets where a forbidden value in `Self` exists.
unsafe fn is_invalid(ptr: *const u8) -> bool {
Self::ForbiddenValues::is_invalid(ptr)
}
}

/// A static proof that a type is "Plain Old Data".
Expand Down Expand Up @@ -270,6 +281,11 @@ pub trait IForbiddenValues {
type SelectFrom<Mask: IBitMask>: ISingleForbiddenValue;
/// Extract the first available forbidden value.
type SelectOne: ISingleForbiddenValue;
/// Returns `true` if `ptr` points to a forbidden value.
///
/// # Safety
/// Calling this on uninitialized memory is UB.
unsafe fn is_invalid(ptr: *const u8) -> bool;
}
/// A single multi-byte forbidden value.
pub trait ISingleForbiddenValue {
Expand All @@ -287,6 +303,9 @@ impl IForbiddenValues for End {
type Or<T: IForbiddenValues> = T;
type SelectFrom<Mask: IBitMask> = End;
type SelectOne = End;
unsafe fn is_invalid(_: *const u8) -> bool {
false
}
}
impl ISingleForbiddenValue for Saturator {
type Push<O: Unsigned, T> = Saturator;
Expand All @@ -308,21 +327,46 @@ impl<Offset: Unsigned, T, Rest: ISingleForbiddenValue> ISingleForbiddenValue
type And<V: ISingleForbiddenValue> = V;
type Resolve = Self;
}
impl<Offset: Unsigned, T, Rest: IForbiddenValues> IForbiddenValues for Array<Offset, T, Rest> {
impl<Offset: Unsigned, T: Unsigned, Rest: IForbiddenValues> IForbiddenValues
for Array<Offset, T, Rest>
{
type Shift<O: Unsigned> = Array<Offset::Add<O>, T, Rest::Shift<O>>;
type Or<O: IForbiddenValues> = Or<O, Self>;
type SelectFrom<Mask: IBitMask> =
<<Mask::HasFreeByteAt<Offset> as IBitBase>::AsForbiddenValue as ISingleForbiddenValue>::And<
<Rest::SelectFrom<Mask> as ISingleForbiddenValue>::Push<Offset, T>,
>;
type SelectOne = Array<Offset, T, Rest::SelectOne>;
unsafe fn is_invalid(ptr: *const u8) -> bool {
ptr.add(Offset::USIZE).read() == T::U8 && Rest::is_invalid(ptr)
}
}
impl<A: IForbiddenValues, B: IForbiddenValues> IForbiddenValues for Or<A, B> {
type Shift<O: Unsigned> = Or<A::Shift<O>, B::Shift<O>>;
type Or<T: IForbiddenValues> = Or<T, Self>;
type SelectFrom<Mask: IBitMask> =
<A::SelectFrom<Mask> as ISingleForbiddenValue>::Or<B::SelectFrom<Mask>>;
type SelectOne = A::SelectOne;
unsafe fn is_invalid(ptr: *const u8) -> bool {
A::is_invalid(ptr) || B::is_invalid(ptr)
}
}
/// An inclusive range of forbidden values for a single byte.
pub struct ForbiddenRange<Min: Unsigned, Max: Unsigned<Greater<Min> = B1>, Offset: Unsigned>(
core::marker::PhantomData<(Min, Max, Offset)>,
);
impl<Min: Unsigned, Max: Unsigned<Greater<Min> = B1>, Offset: Unsigned> IForbiddenValues
for ForbiddenRange<Min, Max, Offset>
{
type Shift<O: Unsigned> = ForbiddenRange<Min, Max, Offset::Add<O>>;
type Or<T: IForbiddenValues> = Or<Self, T>;
type SelectFrom<Mask: IBitMask> =
<Mask::HasFreeByteAt<Offset> as IBitBase>::_SfvTernary<Self::SelectOne, End>;
type SelectOne = Array<Offset, Min, End>;
unsafe fn is_invalid(ptr: *const u8) -> bool {
let v = ptr.add(Offset::USIZE).read();
Min::U8 <= v && v <= Max::U8
}
}
/// The union of 2 sets.
pub struct Or<A, B>(core::marker::PhantomData<(A, B)>);
Expand Down
4 changes: 4 additions & 0 deletions stabby-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,10 @@ pub mod report;
pub mod slice;
/// ABI-stable strs.
pub mod str;
/// ABI-stable tuples.
pub mod tuples {
include!(concat!(env!("OUT_DIR"), "/tuples.rs"));
}

pub use istable::{Array, End, IStable};

Expand Down
Loading

0 comments on commit 0adae35

Please sign in to comment.