rv6
rv6 copied to clipboard
Is stacked borrow too restrictive to be used with `Pin`?
rv6에 대한 이슈는 아니지만, 여기에 쓰는 편이 의견을 나누기 좋을 것 같아 여기에 씁니다.
Rust standard library Pin
문서의 self-referential struct 예시에는 다음과 같은 코드가 있습니다.
struct Unmovable {
data: String,
slice: NonNull<String>,
_pin: PhantomPinned,
}
impl Unmovable {
fn new(data: String) -> Pin<Box<Self>> {
let res = Unmovable {
data,
slice: NonNull::dangling(),
_pin: PhantomPinned,
};
let mut boxed = Box::pin(res);
let slice = NonNull::from(&boxed.data);
unsafe {
let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed);
Pin::get_unchecked_mut(mut_ref).slice = slice;
}
boxed
}
}
Stacked borrow에 따르면 let slice = NonNull::from(&boxed.data);
에서 만들어진 raw pointer는 바로 이후에 &mut boxed
을 하면 무효화되기 때문에 slice
를 사용하는 것은 UB입니다. 예를 들어, 다음과 같은 코드를 그냥 실행하면 hello
가 출력되지만, Miri로 실행(-Zmiri-track-raw-pointers
플래그 필요)하면 unmoved.read()
를 할 때 UB가 탐지됩니다.
impl Unmovable {
fn read(&self) -> &String {
unsafe { &*self.slice.as_ptr() }
}
}
let unmoved = Unmovable::new("hello".to_string());
println!("{}", unmoved.read());
Standard library 문서에 나올 정도로 전형적이면서도 단순한 self-referential struct의 예시라고 생각되는데, 여기에조차 stacked borrow 관점에서 보았을 때 UB가 있다면, Rust에서 stacked borrow를 어기지 않으면서 self-referential struct를 만들 방법이 있는지 의문이 듭니다.
Related? https://github.com/rust-lang/unsafe-code-guidelines/issues/148
https://github.com/rust-lang/unsafe-code-guidelines/issues/148#issuecomment-1003594785
- 결국 저번달에 miri가 업데이트 된 이후로는 miri가 더 이상
!Unpin
인 type에 대해서는 mutuable reference의 uniqueness를 가정하지 않는 것으로 보입니다. 확인해본 결과, 더 이상List
와RcCell
등이 miri에서 에러를 일으키지 않습니다. 이렇게 되면StrongPinMut
을 다시 없애야할 수 있을 것 같습니다.
-
nightly-2019-05-01
과nightly-2022-02-14
를 가지고 실험을 해보았는데, 다음 3가지 경우 모두 이전 버전은 miri가 실패하는 반면, 최신 버전은MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo miri run
와 그냥cargo miri run
모두 성공했습니다.- self referential struct (https://github.com/rust-lang/unsafe-code-guidelines/issues/148#issue-459346973 와 https://github.com/kaist-cp/rv6/issues/527#issue-891685435)
-
List
(https://github.com/kaist-cp/rv6/pull/495#pullrequestreview-647263552) -
RcCell
(https://github.com/travis1829/rv6/tree/299736491e80b842d5c781f6201b94464680960f)
let mut cell = RcCell::new(10); let mut cell = unsafe { Pin::new_unchecked(&mut cell) }; let borrow_mut = cell.borrow_mut(); println!("{}", cell.as_mut().get_pin_mut().is_some()); println!("{}", *borrow_mut); drop(borrow_mut); println!("{}", cell.as_mut().get_pin_mut().is_some());
- 처음에는 rust-lang 측에서도
StrongPinMut
와 비슷한걸 추가하는 등 여러가지를 논의한 것 같은데, 결국에는 이렇게 해결한 것으로 보입니다. 추후 변경될 수도 있을 것 같습니다.