요약: 최신 Rust nightly(2023-04-30 이상)에서는 컴파일러가 struct의 필드 순서를 적극적으로 바꾸는 것으로 보이는데, 이 때문에 List와 SkipList의 traverse 코드가 undefined behavior에 빠집니다. 문제가 되는 SkipList와 List의 Node에 #[repr(C)]를 적용했습니다.
우리의 List 실험 코드에서는 traverse를 하기 전에 루트 노드를 가리키는 &AtomicPtr을 &Node 타입으로 형변환해 Cursor를 생성하는 Hack을 자주 이용했습니다. 지금까지는 이렇게 형변환한 뒤 Node::next 멤버에 접근하는 것이 안전했는데, Node의 첫 번째 멤버가 next이므로 alignment 상 &AtomicPtr를 직접 사용하는 것과 형변환한 뒤 next를 사용하는 것이 사실상 같았기 때문입니다.
그러나 04-30 이후 nightly에서는 컴파일러가 구조체의 변수 순서를 적극적으로 바꿉니다. 예를 들어, 아래 코드는 stable channel과 nightly-2023-04-29 이하에서는 모두 동작하지만, 04-30과 05-01에서만 실패합니다.
use std::sync::atomic::{AtomicPtr, Ordering};
// Let's imagine a `Node` type for a linked list.
struct Node {
// Note that `next` pointer is the first
// member in the memory alignment of `Node`.
next: AtomicPtr<Node>,
_key: i32,
_value: String,
}
fn main() {
// A root node with an arbitrary `next` pointer.
let root = Node {
next: AtomicPtr::new(123 as *mut _),
_key: 123,
_value: "123".to_string(),
};
// Cast a reference to the `next` member
// into a pointer to `Node`.
let link = &root.next;
let node = link as *const _ as *mut Node;
// An address of `next` member should be same with
// an address of the entire node.
assert!(link as *const _ as usize == &root as *const _ as usize);
// Accessing `next` member from `node` pointer must be safe,
// because the offset of `Node::next` in the memory alignment is 0.
assert!(unsafe { &*node }
.next
.compare_exchange(
123 as *mut _,
456 as *mut _,
Ordering::SeqCst,
Ordering::SeqCst
)
.is_ok());
}
요약: 최신 Rust nightly(2023-04-30 이상)에서는 컴파일러가 struct의 필드 순서를 적극적으로 바꾸는 것으로 보이는데, 이 때문에 List와 SkipList의 traverse 코드가 undefined behavior에 빠집니다. 문제가 되는 SkipList와 List의
Node
에#[repr(C)]
를 적용했습니다.우리의 List 실험 코드에서는 traverse를 하기 전에 루트 노드를 가리키는
&AtomicPtr
을&Node
타입으로 형변환해Cursor
를 생성하는 Hack을 자주 이용했습니다. 지금까지는 이렇게 형변환한 뒤Node::next
멤버에 접근하는 것이 안전했는데,Node
의 첫 번째 멤버가next
이므로 alignment 상&AtomicPtr
를 직접 사용하는 것과 형변환한 뒤next
를 사용하는 것이 사실상 같았기 때문입니다.그러나 04-30 이후 nightly에서는 컴파일러가 구조체의 변수 순서를 적극적으로 바꿉니다. 예를 들어, 아래 코드는 stable channel과 nightly-2023-04-29 이하에서는 모두 동작하지만, 04-30과 05-01에서만 실패합니다.