如何通过 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。
这似乎工作正常。
是的,所以我认为这里有几个不同的问题需要解决。
首先你需要了解什么是Vec
。 Vec
不像 C-style 数组,而更像是 C++ std::vector
。 Vec
对象本身仅包含两项(通常称为“控制块”):指向 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)
.
我正在尝试使用全局分配器分配一个包含 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。
这似乎工作正常。
是的,所以我认为这里有几个不同的问题需要解决。
首先你需要了解什么是Vec
。 Vec
不像 C-style 数组,而更像是 C++ std::vector
。 Vec
对象本身仅包含两项(通常称为“控制块”):指向 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)
.