rv6 icon indicating copy to clipboard operation
rv6 copied to clipboard

Is stacked borrow too restrictive to be used with `Pin`?

Open Medowhill opened this issue 3 years ago • 2 comments

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를 만들 방법이 있는지 의문이 듭니다.

Medowhill avatar May 14 '21 07:05 Medowhill

Related? https://github.com/rust-lang/unsafe-code-guidelines/issues/148

jh05013 avatar May 14 '21 10:05 jh05013

https://github.com/rust-lang/unsafe-code-guidelines/issues/148#issuecomment-1003594785

  • 결국 저번달에 miri가 업데이트 된 이후로는 miri가 더 이상 !Unpin인 type에 대해서는 mutuable reference의 uniqueness를 가정하지 않는 것으로 보입니다. 확인해본 결과, 더 이상 ListRcCell 등이 miri에서 에러를 일으키지 않습니다. 이렇게 되면 StrongPinMut을 다시 없애야할 수 있을 것 같습니다.

  • nightly-2019-05-01nightly-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와 비슷한걸 추가하는 등 여러가지를 논의한 것 같은데, 결국에는 이렇게 해결한 것으로 보입니다. 추후 변경될 수도 있을 것 같습니다.

travis1829 avatar Feb 14 '22 14:02 travis1829