Goo*_*opy -1 casting alignment rust
在 Rust 中,将 a 转换*const u8为 a*const T是可以的,但取消引用强制转换指针是不安全的,因为指向的内存可能不满足T大小、对齐和有效字节模式的要求。我试图提出一个违反对齐要求的示例,但满足其他两个要求。
因此,我生成一个 7 的随机切片u8,并尝试将不同的长度为 4 的子切片解释为 f32 值。任何字节模式都是有效的 f32 ,而 4 u8 是无效的size_of::<f32>()。因此,唯一变化的是子切片指针的对齐方式,该指针从基本切片偏移:
slice: [ 0 | 1 | 2 | 3 | 4 | 5 | 6 ]
sub-slices: [ 0 1 2 3 ]
[ 1 2 3 4 ]
[ 2 3 4 5 ]
[ 3 4 5 6 ]
Run Code Online (Sandbox Code Playgroud)
这是我运行的代码
use std::mem::transmute;
use std::ptr::read;
use std::convert::TryInto;
//use rand::Rng;
fn to_f32(v: &[u8]) -> f32 {
let ptr = v.as_ptr() as *const f32;
unsafe {
// [1] dereference
*ptr
// [2] alternatively
//ptr.read()
}
}
fn main() {
println!("align_of::<f32>() = {}", std::mem::align_of::<f32>());
//let mut rng = rand::thread_rng();
// with a pointer on the stack
let v: [u8; 7] = [ 0x4A, 0x3A, 0x2a, 0x10, 0x0F, 0xD2, 0x37];
// with a pointer on the heap
//let v = Box::new(rng.gen::<[u8;7]>());
for i in 0..4 {
let ptr = &v[i..(i+4)];
let f = to_f32(ptr);
// max alignment of ptr
let alignment = 1 << (ptr.as_ptr() as usize).trailing_zeros();
// other ways to convert, as a control check
let repr = ptr.try_into().expect("");
let f2 = unsafe { transmute::<[u8; 4], f32>(repr) };
let f3 = f32::from_le_bytes(repr);
println!("{:x?} [{alignment}]: {repr:02x?} : {f} =? {f2} = {f3}", ptr.as_ptr());
assert_eq!(f, f2);
assert_eq!(f, f3);
}
}
Run Code Online (Sandbox Code Playgroud)
代码输出:
align_of::<f32>() = 4
0x7fffa431a5d1 [1]: [4a, 3a, 2a, 10] : 0.000000000000000000000000000033571493 =? 0.000000000000000000000000000033571493 = 0.000000000000000000000000000033571493
0x7fffa431a5d2 [2]: [3a, 2a, 10, 0f] : 0.000000000000000000000000000007107881 =? 0.000000000000000000000000000007107881 = 0.000000000000000000000000000007107881
0x7fffa431a5d3 [1]: [2a, 10, 0f, d2] : -153612880000 =? -153612880000 = -153612880000
0x7fffa431a5d4 [4]: [10, 0f, d2, 37] : 0.000025040965 =? 0.000025040965 = 0.000025040965
Run Code Online (Sandbox Code Playgroud)
问题是为什么这段代码永远不会断言,即使它 [1] 不安全地取消引用未对齐的指针或 [2] 调用显式需要有效对齐的 ptr::read() ?
取消引用未对齐的指针是未定义的行为。未定义 行为是未定义的,任何事情都可能发生,其中包括预期的结果。这并不意味着代码是正确的。具体来说,x86 允许未对齐读取,因此这可能是它不会失败的原因。
Miri 确实报告了您的代码中的错误:
error: Undefined Behavior: accessing memory with alignment 1, but alignment 4 is required
--> src/main.rs:10:9
|
10 | *ptr
| ^^^^ accessing memory with alignment 1, but alignment 4 is required
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `to_f32` at src/main.rs:10:9: 10:13
note: inside `main`
--> src/main.rs:28:17
|
28 | let f = to_f32(ptr);
| ^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)