除了安全方法外,如何迫使Rust占有已分配内存的所有权? [英] How can one force Rust to take ownership of memory allocated other than by its safe methods?

查看:116
本文介绍了除了安全方法外,如何迫使Rust占有已分配内存的所有权?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在他2018年2月的笔记中,标题为" Rust中的内存安全: C的案例研究",威尔·克里顿(Will Crichton)写道:

Rust提供了获取原始指针所有权的能力,我们使用 ,它告诉Rust将内存指针视为堆分配的数组.转移所有权后,假设内存有效且具有正确的大小/类型,Rust将应用其通常的内存安全性和包含性检查.

上面提到的他的代码的相关部分是:

 let mut new_data = unsafe {
    let ptr = Heap::default()
        .alloc(Layout::array::<isize>(new_capacity).unwrap())
        .unwrap() as *mut isize;
    Box::from_raw(slice::from_raw_parts_mut(ptr, new_capacity))
};
 

但是, Box::from_raw 状态(添加了强调):

由于未指定Box分配和释放内存的方式, 传递给该函数的唯一有效指针 是通过 Box::into_raw 函数.

为免生疑问,(实验性) Heap 上面用于执行内存分配的API(自Rust 1.27.0中删除),在其解决方案

传递给新鲜分配的内存的Box::from_raw原始指针是否有效(尽管不受支持)

否,它无效.

特别是,当出现的Box被销毁时,Rust是否会重新分配该内存?

是的,这就是它无效的原因.

内存分配器提供成对分配和释放例程.使用一个分配器分配一块内存时,必须使用该分配器释放它.

如果不这样做,那么当进行分配的分配器执行其需要做的任何簿记操作时,它将不知道那部分内存.实际进行分配的分配器永远不会将该内存标记为不可用.

这些问题也没有得到解决.我已经向GLib提交了补丁程序,以更正发生分配/取消分配不匹配并导致分配不当的地方真实的问题.

必须拥有分配的内存的所有权

就原始指针而言,所有权在很大程度上是一种心态,就像在C或C ++中一样.在这里拥有自己的东西意味着您有责任适当地清理它.

mallocfree是成对的分配/取消分配方法.您可以创建自己的类型并为其实现Drop:

use libc::{free, malloc};
use std::{ffi::c_void, mem};

struct MallocBox(*mut i32);

impl MallocBox {
    fn new(v: i32) -> Self {
        unsafe {
            let p = malloc(mem::size_of::<i32>()) as *mut i32;
            *p = v;
            Self(p)
        }
    }
}

impl Drop for MallocBox {
    fn drop(&mut self) {
        unsafe { free(self.0 as *mut c_void) }
    }
}

fn main() {
    MallocBox::new(42);
}

真正的实现还可以实现Deref以及可能的许多其他特征,以便使用这种类型的人体工程学.

必须创建MallocBoxJeMallocBox以及MyCustomAllocBox会很烦人,这就是为什么工作正在进行,将Box<T>转换为Box<T, A: Alloc + Default = Global>.

一个人怎么能生锈

没有强制" Rust做任何事情的概念,更不用说像这样的低级细节了.例如,不能保证分配指针的C代码不会尝试释放指针本身.在FFI世界中,所有权是一种合作协议.


另请参阅:

In his February 2018 note titled "Memory Safety in Rust: A Case Study with C", Will Crichton wrote:

Rust provides the ability to take ownership of raw pointers, which we do using slice::from_raw_parts_mut and Box::from_raw which tells Rust to treat the memory pointer as a heap-allocated array. After transferring ownership, assuming the memory is valid and of the right size/type, Rust applies its usual memory safety and containment checks.

The relevant part of his code to which the above refers is:

let mut new_data = unsafe {
    let ptr = Heap::default()
        .alloc(Layout::array::<isize>(new_capacity).unwrap())
        .unwrap() as *mut isize;
    Box::from_raw(slice::from_raw_parts_mut(ptr, new_capacity))
};

However, the documentation for Box::from_raw states (emphasis added):

Since the way Box allocates and releases memory is unspecified, the only valid pointer to pass to this function is the one taken from another Box via the Box::into_raw function.

For the avoidance of doubt, the (experimental) Heap API used above to perform memory allocation (since removed in Rust 1.27.0) directly called __rust_alloc in its alloc method—and therefore ptr was not obtained from Box::into_raw.

Is it valid, albeit unsupported, to pass to Box::from_raw raw pointers to freshly allocated memory in order to have Rust take ownership of that memory and enforce its usual safety and containment checks? In particular, will Rust deallocate that memory when the arising Box is destroyed?

If not, how can one force Rust to take such ownership of memory allocated other than by its safe methods?

解决方案

Is it valid, albeit unsupported, to pass to Box::from_raw raw pointers to freshly allocated memory

No, it is not valid.

In particular, will Rust deallocate that memory when the arising Box is destroyed?

Yes, and this is the reason why it's invalid.

Memory allocators provide paired allocation and deallocation routines. When you allocate a piece of memory with one allocator, you must free it with that allocator.

If you don't, when the allocator doing the deallocation goes to perform whatever bookkeeping it needs to do, it won't know about that piece of memory. The allocator that actually did the allocation will never mark that memory as unavailable.

These concerns aren't made up, either. I've submitted patches to GLib to correct places where mismatched allocations / deallocations occurred and caused real problems in the wild.

Rust to take such ownership of memory allocated

At the level of raw pointers, ownership is largely a state of mind, just like it is in C or C++. To own something here means that you are responsible for cleaning it up appropriately.

malloc and free are paired allocation/deallocation methods. You could create your own type and implement Drop for it:

use libc::{free, malloc};
use std::{ffi::c_void, mem};

struct MallocBox(*mut i32);

impl MallocBox {
    fn new(v: i32) -> Self {
        unsafe {
            let p = malloc(mem::size_of::<i32>()) as *mut i32;
            *p = v;
            Self(p)
        }
    }
}

impl Drop for MallocBox {
    fn drop(&mut self) {
        unsafe { free(self.0 as *mut c_void) }
    }
}

fn main() {
    MallocBox::new(42);
}

A real implementation would also implement Deref and probably many other traits so that this type is ergonomic to use.

It would be annoying to have to create a MallocBox and JeMallocBox and a MyCustomAllocBox, which is why RFC 1398 proposes a shared trait for allocators. Related work is progressing to convert Box<T> into Box<T, A: Alloc + Default = Global>.

how can one force Rust

There's no concept of "forcing" Rust to do anything, much less when it comes to low level details like this. For example, there's no guarantee that the C code that allocated the pointer doesn't try to free the pointer itself. In an FFI world, ownership is a cooperative agreement.


See also:

这篇关于除了安全方法外,如何迫使Rust占有已分配内存的所有权?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆