如何通过 Vec<T> 分配修复 STATUS_ACCESS_VIOLATION?

How can I fix STATUS_ACCESS_VIOLATION by Vec<T> allocation?

我正在尝试使用全局分配器分配一个包含 Vec 的结构。 (为了简化这个问题,这里使用 Vec。) 貌似使用std::alloc::alloc函数分配Vec可能会出错

我应该如何在不安全的 Rust 中修复它?

#[test]
fn allocation() {
    let layout = std::alloc::Layout::new::<Vec<u32>>();
    let ptr = unsafe{ std::alloc::alloc(layout) } as *mut Vec<u32>;
    dbg!(ptr);
    let ptr_ref = unsafe{ &mut *ptr  as &mut Vec<u32> };
    ptr_ref.resize_with(16, || {
        dbg!("initializing");
        0
    });
    dbg!(&ptr_ref[0] as *const u32);
    unsafe{ std::alloc::dealloc( ptr as *mut u8, layout); }
}

当运行上述代码时,有时会以“0xc0000005,STATUS_ACCESS_VIOLATION”或“0xc0000374,STATUS_HEAP_CORRUPTION”结尾。 还有,当出现这些错误时,好像是没有执行初始化(闭包即returns初始值)。


编辑:

看来我的代码没有包含Vec的初始化。 JMAA 的回答给出了详细信息。所以我在转换为 &mut Vec<u32>.

之前添加了以下代码
unsafe {
    std::ptr::write(ptr, Vec::with_capacity(16));
}

std::ptr::write 函数的详细信息是 here。 这似乎工作正常。

是的,所以我认为这里有几个不同的问题需要解决。

首先你需要了解什么是VecVec 不像 C-style 数组,而更像是 C++ std::vectorVec 对象本身仅包含两项(通常称为“控制块”):指向 heap-allocated space 的宽指针,用于 Vec 的元素(包括分配的容量)和Vec的当前长度。 Vec 的实际元素存在于其他地方,在由 Vec 本身分配的内存中,并由它在控制块中持有的宽指针指向。

因此,在您的代码中,ptr 指向分配有足够 space 的堆内存区域以容纳 Vec 对象,即容纳控制块。您从未真正初始化一个 Vec 对象,只需分配足够的内存来容纳一个对象。

然后你做了你的第一件“坏事”,你将 ptr 转换为 &mut Vec。但是您从未在那里初始化 Vec 对象(即,尚未初始化为控制块分配的内存)。 Rust 中的引用不能指向未初始化的内存,这样做违反了安全性并且您的整个程序无效。特别是,您在“Vec”上调用了 resize_with 方法,但是那里 没有 Vec ,只有一些堆内存块理论上容纳 Vec 控制块的大小和对齐方式。

我想你认为你正在做的是指向 Vec 的指针不指向控制块,而是直接指向包含 Vec 元素的堆内存。这可以理解为什么您尝试 dealloc 的方式,使用指向 Vec 的第一个元素而不是 Vec 本身的指针。这也是错误的,因为那不是指向你 alloc 的内存。


那么你应该做什么?我不是专家,因为我以前从未觉得需要在 Rust 中手动 de/allocate,但我的理解是你可以简单地做 *ptr = Vec::new()(或任何你想要的 Vec 构造函数) 然后你可以转换回 &mut Vec 因为现在那里有一个初始化的 Vec

然后,当你完成后,你首先需要确保 Vec 被删除——这将导致它在你之前在内部释放为元素分配的 space做 dealloc(ptr.cast(), layout).