我在读取诺米康后编写了以下代码(代码1)。我觉得不应该编出来。但实际上,它编译和使用后,免费的错误。PhantomData<T>应该告诉编译器World拥有T,因此它将调用T的析构函数。但是在下面的代码中,最后一个ptr不会超过world,并且会在world发生之前下降。
// code 1: compile but has UAF
use std::ptr;
use std::marker::PhantomData;
use std::fmt::Debug;
impl<T:Debug> Drop for World<T> {
fn drop(&mut self) {
unsafe{
println!("I was only {:?} days from retirement!", ptr::read(self.ptr));
}
}
}
struct World<T:Debug> {
ptr: *mut T,
_marker: PhantomData<T>,
}
fn main() {
let mut v = 8usize;
let mut world = World {
ptr: &mut v as *mut usize,
_marker: PhantomData
};
let mut v = 99usize;
world.ptr = &mut v as *mut usize;
}导致我困惑的句子是
为了告诉dropck我们做了T类型的值,因此当我们下降时可能会掉一些T,我们必须添加一个额外的PhantomData,确切地说:
use std::marker;
struct Vec<T> {
data: *const T, // *const for variance!
len: usize,
cap: usize,
_marker: marker::PhantomData<T>,
}下面的代码(代码2)将编译并具有UAF。但是取消注释,PhantomData<T>将不会编译(参见代码3)。
// code 2: compile and has UAF
#![feature(dropck_eyepatch)]
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ptr;
struct MyBox<T> {
pub raw: *mut T,
//_marker: PhantomData<T>
}
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
Self {
raw: Box::into_raw(Box::new(x)),
// _marker: PhantomData
}
}
}
unsafe impl<#[may_dangle] T> Drop for MyBox<T> {
fn drop(&mut self) {
unsafe {
Box::from_raw(self.raw);
}
}
}
#[derive(Debug)]
struct Bar<T: Debug>(T);
impl<T: Debug> Drop for Bar<T> {
fn drop(&mut self) {
println!("{:?}", self.0);
}
}
fn main() {
let _b;
let x = "hell".to_owned();
_b = MyBox::new(Bar(&x));
}代码3
// code 3: does not compile becuase PhantomData<T>
#![feature(dropck_eyepatch)]
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ptr;
struct MyBox<T> {
pub raw: *mut T,
_marker: PhantomData<T>
}
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
Self {
raw: Box::into_raw(Box::new(x)),
_marker: PhantomData
}
}
}
unsafe impl<#[may_dangle] T> Drop for MyBox<T> {
fn drop(&mut self) {
unsafe {
Box::from_raw(self.raw);
}
}
}
#[derive(Debug)]
struct Bar<T: Debug>(T);
impl<T: Debug> Drop for Bar<T> {
fn drop(&mut self) {
println!("{:?}", self.0);
}
}
fn main() {
let _b;
let x = "hell".to_owned();
_b = MyBox::new(Bar(&x));
}代码4:为什么以下代码可以使用UAF进行编译
use std::ptr;
use std::marker::PhantomData;
use std::fmt::Debug;
impl<'a, T:Debug> Drop for World<'a, T> {
fn drop(&mut self) {
unsafe{
println!("I was only {:?} days from retirement!", ptr::read(self.ptr));
}
}
}
struct World<'a, T:Debug> {
ptr: *mut T,
_marker: PhantomData<&'a mut T>,
}
fn main() {
let mut v = 8usize;
let mut world = World {
ptr: &mut v as *mut usize,
_marker: PhantomData
};
let mut v = 99usize;
world.ptr = &mut v as *mut usize;
}发布于 2021-06-23 15:37:31
这个定义要求将World视为拥有T。指针应该指向World管理的东西:
struct World<T:Debug> {
ptr: *mut T,
_marker: PhantomData<T>,
}此使用将创建一个World,其中指针指向不属于world的东西。相反,它似乎包含了一个引用:
let mut v = 8usize;
let mut world = World {
ptr: &mut v as *mut usize,
_marker: PhantomData
};这些是有冲突的。如果想要引用行为,请使用引用定义World:
struct World<'a, T:Debug> {
ptr: *mut T,
_marker: PhantomData<&'a mut T>,
}如果您想拥有行为,构造必须创建一个拥有的版本:
let mut v = 8usize;
let mut world = World {
ptr: Box::into_raw(Box::new(v)) as *mut usize,
_marker: PhantomData
};PhantomData应该告诉编译器世界拥有T,所以它将调用T的析构函数。
PhantomData<T>告诉编译器将World视为拥有T,而World将调用T的析构函数。
在代码2/3中:
fn main() {
let _b;
let x = "hell".to_owned();
_b = MyBox::new(Bar(&x));
}没有PhantomData,借阅检查程序就无法知道MyBox<T>和T之间的关系,因此它会编译。对于PhantomData,您有一个拥有的结构,它无法编译,因为x是在_b之前删除的。我不确定这种混淆是来自drop排序,还是由于let和以后的初始化,还是来自may_dangle的行为,它只适用于检查T,仍然可以检查它的内容在下降。。
代码#4使用UAF进行编译,因为初始化器的生存期不是World推断的。必须捕获所引用数据的生存期。我给一个类似的问题写了一个答案。
https://stackoverflow.com/questions/68100691
复制相似问题