首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么要将PhantomData<T>添加到结构中,但是代码仍然编译,并且有一个事后使用的错误。

为什么要将PhantomData<T>添加到结构中,但是代码仍然编译,并且有一个事后使用的错误。
EN

Stack Overflow用户
提问于 2021-06-23 13:23:57
回答 1查看 113关注 0票数 0

我在读取诺米康后编写了以下代码(代码1)。我觉得不应该编出来。但实际上,它编译和使用后,免费的错误。PhantomData<T>应该告诉编译器World拥有T,因此它将调用T的析构函数。但是在下面的代码中,最后一个ptr不会超过world,并且会在world发生之前下降。

代码语言:javascript
复制
// 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,确切地说:

代码语言:javascript
复制
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)。

代码语言:javascript
复制
// 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

代码语言:javascript
复制
// 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进行编译

代码语言:javascript
复制
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;
}
EN

回答 1

Stack Overflow用户

发布于 2021-06-23 15:37:31

这个定义要求将World视为拥有T。指针应该指向World管理的东西:

代码语言:javascript
复制
struct World<T:Debug> {
    ptr: *mut T,
    _marker: PhantomData<T>,
}

此使用将创建一个World,其中指针指向不属于world的东西。相反,它似乎包含了一个引用:

代码语言:javascript
复制
    let mut v = 8usize;
    let mut world = World {
        ptr: &mut v as *mut usize,
        _marker: PhantomData
    };

这些是有冲突的。如果想要引用行为,请使用引用定义World

代码语言:javascript
复制
struct World<'a, T:Debug> {
    ptr: *mut T,
    _marker: PhantomData<&'a mut T>,
}

如果您想拥有行为,构造必须创建一个拥有的版本:

代码语言:javascript
复制
    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中:

代码语言:javascript
复制
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推断的。必须捕获所引用数据的生存期。我给一个类似的问题写了一个答案。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68100691

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档