Function security_abstract

Source
pub fn security_abstract()
Expand description

§安全抽象

什么叫安全抽象? 最简单的示例:

fn unbound_lifetime_foo<'a>(input: *const u32) -> &'a u32 {
    unsafe {
        return &*input
    }
}

fn normal_foo<'a>(input: &'a u32) -> &'a u32 {
    &input

}

fn main() {
    let x;
    { // -----------------------------------------------------------------------------------  `y` lifetime start
        // unbound lifetime broken lifetime
        let y = 7;
        x = unbound_lifetime_foo(&y);

        // normal lifetime will error: error[E0597]: `y` does not live long enough
        // let y = 8;
        // x = normal_foo(&y);
    } // ----------------------------------------------------------------------------------- `y` lifetime end
    println!("hello: {}", x);
}

示例2:

pub fn insert(&mut self, index: usize, element: T) {
    let len = self.len();
    // 通过该断言保证了数组不能越界
    assert!(index <= len);
    // 通过判断长度是否达到容量极限来决定是否进行扩容
    if len == self.buf.cap() {
        self.reserve(1);
    }
    unsafe {
        {
            let p = self.as_mut_ptr().offset(index as isize);
            ptr::copy(p, p.offset(1), len - index);
            ptr::write(p, element);
        }
        self.set_len(len + 1);
    }
}

§Drop check

示例1:正常的drop check

#![allow(unused)]
#![allow(unused)]
#![feature(alloc_layout_extra)]
#![feature(dropck_eyepatch)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

#[derive(Copy, Clone, Debug)]

enum State {
    InValid,
    Valid,
}

#[derive(Debug)]
struct Hello<T: fmt::Debug>(&'static str, T, State);
impl<T: fmt::Debug> Hello<T> {
    fn new(name: &'static str, t: T) -> Self {
        Hello(name, t, State::Valid)
    }
}
impl<T: fmt::Debug> Drop for Hello<T> {
    fn drop(&mut self) {
        println!("drop Hello({}, {:?}, {:?})", self.0, self.1, self.2);
        self.2 = State::InValid;
    }
}
struct WrapBox<T> {
    v: Box<T>,
}
impl<T> WrapBox<T> {
    fn new(t: T) -> Self {
        WrapBox { v: Box::new(t) }
    }
}
fn f1() {
    let x;
    let y;
    x = Hello::new("x", 13);
    y = WrapBox::new(Hello::new("y", &x));
}

struct MyBox<T> {
    v: *const T,
}
impl<T> MyBox<T> {
    fn new(t: T) -> Self {
        unsafe {
            let p = System.alloc(Layout::array::<T>(1).unwrap());
            let p = p as *mut T;
            ptr::write(p, t);
            MyBox {
                v: p,
            }
        }
    }
}


impl< T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            let p = self.v as *mut _;
            System.dealloc(p, Layout::array::<T>(mem::align_of::<T>()).unwrap());
        }
    }
}


fn f2() {
    {
        let (x1, y1);
        x1 = Hello::new("x1", 13);
        y1 = MyBox::new(Hello::new("y1", &x1));
    }
    {
        let (y2,x2 ); // 此处交换,会报错,注意编译错误
        x2 = Hello::new("x2", 13);
        y2 = MyBox::new(Hello::new("y2", &x2));
    }
}

fn main() {
    f1();
    f2();
}

使用 改进:

#![allow(unused)]
#![allow(unused)]
#![feature(alloc_layout_extra)]
#![feature(dropck_eyepatch)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

#[derive(Copy, Clone, Debug)]

enum State {
    InValid,
    Valid,
}

#[derive(Debug)]
struct Hello<T: fmt::Debug>(&'static str, T, State);
impl<T: fmt::Debug> Hello<T> {
    fn new(name: &'static str, t: T) -> Self {
        Hello(name, t, State::Valid)
    }
}
impl<T: fmt::Debug> Drop for Hello<T> {
    fn drop(&mut self) {
        println!("drop Hello({}, {:?}, {:?})", self.0, self.1, self.2);
        self.2 = State::InValid;
    }
}
struct WrapBox<T> {
    v: Box<T>,
}
impl<T> WrapBox<T> {
    fn new(t: T) -> Self {
        WrapBox { v: Box::new(t) }
    }
}
fn f1() {
    let x;
    let y;
    x = Hello::new("x", 13);
    y = WrapBox::new(Hello::new("y", &x));
}

struct MyBox<T> {
    v: *const T,
}
impl<T> MyBox<T> {
    fn new(t: T) -> Self {
        unsafe {
            let p = System.alloc(Layout::array::<T>(1).unwrap());
            let p = p as *mut T;
            ptr::write(p, t);
            MyBox {
                v: p,
            }
        }
    }
}


unsafe impl<#[may_dangle] T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            let p = self.v as *mut _;
            System.dealloc(p, Layout::array::<T>(mem::align_of::<T>()).unwrap());
        }
    }
}

fn f2() {
    {
        let (x1, y1);
        x1 = Hello::new("x1", 13);
        y1 = MyBox::new(Hello::new("y1", &x1));
    }
    {
        let (y2,x2 ); // 此处改变
        x2 = Hello::new("x2", 13);
        y2 = MyBox::new(Hello::new("y2", &x2));
    }
}

fn main() {
    f1();
    f2();
}

使用 PhantomData 防止出现 UB:

#![allow(unused)]
#![allow(unused)]
#![feature(alloc_layout_extra)]
#![feature(dropck_eyepatch)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

#[derive(Copy, Clone, Debug)]

enum State {
    InValid,
    Valid,
}

#[derive(Debug)]
struct Hello<T: fmt::Debug>(&'static str, T, State);
impl<T: fmt::Debug> Hello<T> {
    fn new(name: &'static str, t: T) -> Self {
        Hello(name, t, State::Valid)
    }
}
impl<T: fmt::Debug> Drop for Hello<T> {
    fn drop(&mut self) {
        println!("drop Hello({}, {:?}, {:?})", self.0, self.1, self.2);
        self.2 = State::InValid;
    }
}
struct WrapBox<T> {
    v: Box<T>,
}
impl<T> WrapBox<T> {
    fn new(t: T) -> Self {
        WrapBox { v: Box::new(t) }
    }
}
fn f1() {
    let x;
    let y;
    x = Hello::new("x", 13);
    y = WrapBox::new(Hello::new("y", &x));
}

struct MyBox<T> {
    v: *const T,
    // _pd: PhantomData<T>,
}
impl<T> MyBox<T> {
    fn new(t: T) -> Self {
        unsafe {
            let p = System.alloc(Layout::array::<T>(1).unwrap());
            let p = p as *mut T;
            ptr::write(p, t);
            MyBox {
                v: p,
                // _pd: Default::default()
            }
        }
    }
}


unsafe impl<#[may_dangle] T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            ptr::read(self.v); // 此处新增,出现UB (use after free,UAF)
            let p = self.v as *mut _;
            System.dealloc(p, Layout::array::<T>(mem::align_of::<T>()).unwrap());
        }
    }
}

fn f2() {
    {
        let (x1, y1);
        x1 = Hello::new("x1", 13);
        y1 = MyBox::new(Hello::new("y1", &x1));
    }
    {
        let (y2,x2 ); // 此处改变
        x2 = Hello::new("x2", 13);
        y2 = MyBox::new(Hello::new("y2", &x2));
    }
}

fn main() {
    f1();
    f2();
}

示例:型变

在一门程序设计语言的类型系统中,一个类型规则或者类型构造器是:

  • 协变(covariant),如果它保持了子类型序关系≦。该序关系是:子类型≦基类型。
  • 逆变(contravariant),如果它逆转了子类型序关系。
  • 不变(invariant),如果上述两种均不适用。

Rust 里唯一的类型父子关系是生命周期:'a: 'b 。比如,'static: 'a ,并且默认都是协变,唯一逆变的地方在 fn(T)

'static: 'a 对应: 子类型: 父类型

  • 协变: 能用 ’a 的地方,也可以用 ’static。
  • 逆变: 能用 ’static 的地方,可以可以用 ’a。

规则:

PhantomData规则:

  • PhantomData,在T上是协变。
  • PhantomData<&’a T>,在’a 和T上是协变。
  • PhantomData<&’a mut T>,在’a上是协变,在T上是不变。
  • PhantomData<*const T>,在T上是协变。
  • PhantomData<*mut T>,在T上不变。
  • PhantomData<fn(T)>,在T上是逆变,如果以后语法修改的话,会成为不变。
  • PhantomData<fn() -> T>,在T上是协变。
  • PhantomData<fn(T) -> T>,在T上是不变。
  • PhantomData<Cell<&’a ()>>,在’a上是不变。
// 协变类型
struct MyCell<T> {
    value: T,
}
impl<T: Copy> MyCell<T> {
    fn new(x: T) -> MyCell<T> {
        MyCell { value: x }
    }
    fn get(&self) -> T {
        self.value
}
fn set(&self, value: T) {
    use std::ptr;
    unsafe {
        ptr::write(&self.value as *const _ as *mut _, value);
    }
}
}

fn step1<'a>(r_c1: &MyCell<&'a i32>) {
    let val: i32 = 13;
    step2(&val, r_c1); // step2函数执行完再回到step1
    println!("step1 value: {}", r_c1.value);
} // step1调用完,栈帧将被清理,val将不复存在,&val将成为悬垂指针

fn step2<'b>(r_val: &'b i32, r_c2: &MyCell<&'b i32>) {
    r_c2.set(r_val);
}
static X: i32 = 10;
fn main() {
let cell = MyCell::new(&X);
step1(&cell);
println!("  end value: {}", cell.value); //此处 cell.value的值将无法预期,UB风险
}

Basic usage: 修改MyCell 类型为不变

解决上面示例UB的问题,编译将报错,因为安全检查生效了,成功阻止了UB风险

use std::marker::PhantomData;
struct MyCell<T> {
    value: T,
    mark: PhantomData<fn(T)> , //通过PhantomData<fn(T)>将MyCell<T>改为逆变类型
}
impl<T: Copy> MyCell<T> {
    fn new(x: T) -> MyCell<T> {
        MyCell { value: x , mark: PhantomData}
    }
fn get(&self) -> T {
    self.value
}
fn set(&self, value: T) {
    use std::ptr;
    unsafe {
        ptr::write(&self.value as *const _ as *mut _, value);
    }
}
}
fn step1<'a>(r_c1: &MyCell<&'a i32>) {
    let val: i32 = 13;
    step2(&val, r_c1); // error[E0597]: `val` does not live long enough
    println!("step1 value: {}", r_c1.value);
} // step1调用完,栈帧将被清理,val将不复存在,&val将成为悬垂指针

fn step2<'b>(r_val: &'b i32, r_c2: &MyCell<&'b i32>) {
    r_c2.set(r_val);
}
static X: i32 = 10;
fn main() {
    let cell = MyCell::new(&X);
    step1(&cell);
    println!("  end value: {}", cell.value);
}

示例:逆变

#![allow(unused)]
trait A {
    fn foo(&self, s: &'static str);
}
struct B;
impl A for B {
    fn foo(&self, s: &str){
        println!("{:?}", s);
    }
}
impl B{
fn foo2(&self, s: &'static str){
    println!("{:?}", s);
}
}
fn main() {
    B.foo("hello");
    let s = "hello".to_string();
    B.foo2(&s)
}

示例2:

fn foo(input: &str)  {
    println!("{:?}", input);
}
fn bar(f: fn(&'static str), v: &'static str) {
    (f)(v);
}
fn main(){
    let v : &'static str = "hello";
    bar(foo, v);
}