Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MIRI errors for all tests on chapter 6 #213

Closed
nico-abram opened this issue Jan 22, 2022 · 3 comments
Closed

MIRI errors for all tests on chapter 6 #213

nico-abram opened this issue Jan 22, 2022 · 3 comments

Comments

@nico-abram
Copy link

nico-abram commented Jan 22, 2022

Running cargo +nightly miri test gives errors for all the tests:

Error on basics with MIRIFLAGS="-Zmiri-symbolic-alignment-check -Zmiri-check-number-validity -Zmiri-tag-raw-pointers":

error: Undefined Behavior: trying to reborrow for SharedReadWrite at alloc70342, but parent tag <185055> does not have an appropriate item in the borrow stack
   --> D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ptr\mod.rs:188:1
    |
188 | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to reborrow for SharedReadWrite at alloc70342, but parent tag <185055> does not have an appropriate item in the borrow stack
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

    = note: inside `std::ptr::drop_in_place::<std::option::Option<std::boxed::Box<Node<i32>>>> - shim(Some(std::option::Option<std::boxed::Box<Node<i32>>>))` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ptr\mod.rs:188:1
note: inside `List::<i32>::push` at src\main.rs:35:17
   --> src\main.rs:35:17
    |
35  |                 (*self.tail).next = Some(new_tail);
    |                 ^^^^^^^^^^^^^^^^^
note: inside `test::basics` at src\main.rs:142:9
   --> src\main.rs:142:9
    |
142 |         list.push(2);
    |         ^^^^^^^^^^^^
note: inside closure at src\main.rs:134:5
   --> src\main.rs:134:5
    |
133 |       #[test]
    |       ------- in this procedural macro expansion
134 | /     pub fn basics() {
135 | |         let mut list = List::new();
136 | |
137 | |         // Check empty list behaves right
...   |
168 | |         assert_eq!(list.pop(), None);
169 | |     }
    | |_____^
 

That error is the same with those MIRIFLAGS for all tests.

Error on basics with no MIRIFLAGS:

error: Undefined Behavior: trying to reborrow for Unique at alloc68683, but parent tag <176765> does not have an appropriate item in the borrow stack
   --> D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:874:18
    |
874 |             Some(x) => Some(f(x)),
    |                  ^ trying to reborrow for Unique at alloc68683, but parent tag <176765> does not have an appropriate item in the borrow stack
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

    = note: inside `std::option::Option::<std::boxed::Box<Node<i32>>>::map::<i32, [closure@src\main.rs:45:30: 54:10]>` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:874:18
note: inside `List::<i32>::pop` at src\main.rs:45:9
   --> src\main.rs:45:9
    |
45  | /         self.head.take().map(|head| {
46  | |             let head = *head;
47  | |             self.head = head.next;
48  | |
...   |
53  | |             head.elem
54  | |         })
    | |__________^
note: inside `test::basics` at src\main.rs:146:20
   --> src\main.rs:146:20
    |
146 |         assert_eq!(list.pop(), Some(1));
    |                    ^^^^^^^^^^
note: inside closure at src\main.rs:134:5
   --> src\main.rs:134:5
    |
133 |       #[test]
    |       ------- in this procedural macro expansion
134 | /     pub fn basics() {
135 | |         let mut list = List::new();
136 | |
137 | |         // Check empty list behaves right
...   |
168 | |         assert_eq!(list.pop(), None);
169 | |     }
    | |_____^

Error on into_iter with no MIRIFLAGS:

error: Undefined Behavior: trying to reborrow for Unique at alloc68677, but parent tag <176833> does not have an appropriate item in the borrow stack
   --> D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:874:18
    |
874 |             Some(x) => Some(f(x)),
    |                  ^ trying to reborrow for Unique at alloc68677, but parent tag <176833> does not have an appropriate item in the borrow stack
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

    = note: inside `std::option::Option::<std::boxed::Box<Node<i32>>>::map::<i32, [closure@src\main.rs:45:30: 54:10]>` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:874:18
note: inside `List::<i32>::pop` at src\main.rs:45:9
   --> src\main.rs:45:9
    |
45  | /         self.head.take().map(|head| {
46  | |             let head = *head;
47  | |             self.head = head.next;
48  | |
...   |
53  | |             head.elem
54  | |         })
    | |__________^
note: inside `<IntoIter<i32> as std::iter::Iterator>::next` at src\main.rs:104:9
   --> src\main.rs:104:9
    |
104 |         self.0.pop()
    |         ^^^^^^^^^^^^
note: inside `test::into_iter` at src\main.rs:178:20
   --> src\main.rs:178:20
    |
178 |         assert_eq!(iter.next(), Some(1));
    |                    ^^^^^^^^^^^
note: inside closure at src\main.rs:171:5
   --> src\main.rs:171:5
    |
170 |       #[test]
    |       ------- in this procedural macro expansion
171 | /     pub fn into_iter() {
172 | |         let mut list = List::new();
173 | |         list.push(1);
174 | |         list.push(2);
...   |
181 | |         assert_eq!(iter.next(), None);
182 | |     }
    | |_____^

Error on iter with no MIRIFLAGS:

error: Undefined Behavior: trying to reborrow for SharedReadOnly at alloc69065, but parent tag <177908> does not have an appropriate item in the borrow stack
    --> D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\boxed.rs:1727:9
     |
1727 |         &**self
     |         ^^^^^^^ trying to reborrow for SharedReadOnly at alloc69065, but parent tag <177908> does not have an appropriate item in the borrow stack
     |
     = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
     = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

     = note: inside `<std::boxed::Box<Node<i32>> as std::ops::Deref>::deref` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\boxed.rs:1727:9
     = note: inside `std::option::Option::<std::boxed::Box<Node<i32>>>::as_deref` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:1751:29
note: inside `List::<i32>::iter` at src\main.rs:71:19
    --> src\main.rs:71:19
     |
71   |             next: self.head.as_deref(),
     |                   ^^^^^^^^^^^^^^^^^^^^
note: inside `test::iter` at src\main.rs:190:24
    --> src\main.rs:190:24
     |
190  |         let mut iter = list.iter();
     |                        ^^^^^^^^^^^
note: inside closure at src\main.rs:184:5
    --> src\main.rs:184:5
     |
183  |       #[test]
     |       ------- in this procedural macro expansion
184  | /     pub fn iter() {
185  | |         let mut list = List::new();
186  | |         list.push(1);
187  | |         list.push(2);
...    |
194  | |         assert_eq!(iter.next(), None);
195  | |     }
     | |_____^

Error on iter_mut with no MIRIFLAGS:

error: Undefined Behavior: trying to reborrow for Unique at alloc68655, but parent tag <176792> does not have an appropriate item in the borrow stack
    --> D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\boxed.rs:1734:9
     |
1734 |         &mut **self
     |         ^^^^^^^^^^^ trying to reborrow for Unique at alloc68655, but parent tag <176792> does not have an appropriate item in the borrow stack
     |
     = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
     = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information

     = note: inside `<std::boxed::Box<Node<i32>> as std::ops::DerefMut>::deref_mut` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\boxed.rs:1734:9
     = note: inside `std::option::Option::<std::boxed::Box<Node<i32>>>::as_deref_mut` at D:\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:1779:29
note: inside `List::<i32>::iter_mut` at src\main.rs:77:19
    --> src\main.rs:77:19
     |
77   |             next: self.head.as_deref_mut(),
     |                   ^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `test::iter_mut` at src\main.rs:203:24
    --> src\main.rs:203:24
     |
203  |         let mut iter = list.iter_mut();
     |                        ^^^^^^^^^^^^^^^
note: inside closure at src\main.rs:197:5
    --> src\main.rs:197:5
     |
196  |       #[test]
     |       ------- in this procedural macro expansion
197  | /     pub fn iter_mut() {
198  | |         let mut list = List::new();
199  | |         list.push(1);
200  | |         list.push(2);
...    |
207  | |         assert_eq!(iter.next(), None);
208  | |     }
     | |_____^

Possibly related: rust-lang/unsafe-code-guidelines#133

@nico-abram
Copy link
Author

nico-abram commented Jan 22, 2022

Changing it to use Option<NonNull> instead of a Box seems to fix them (With and without MIRIFLAGS):

#![allow(unused)]

use std::ptr::{self, NonNull};

pub struct List<T> {
    head: Link<T>,
    tail: Option<NonNull<Node<T>>>,
}

type Link<T> = Option<NonNull<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<T> List<T> {
    pub fn new() -> Self {
        List {
            head: None,
            tail: None,
        }
    }

    pub fn push(&mut self, elem: T) {
        let mut new_tail = Box::new(Node {
            elem: elem,
            next: None,
        });

        let raw_tail: *mut _ = Box::into_raw(new_tail);
        let raw_tail = unsafe { NonNull::new_unchecked(raw_tail) };
        let raw_tail = Some(raw_tail);

        if let Some(tail) = self.tail {
            unsafe {
                (*tail.as_ptr()).next = raw_tail;
            }
        } else {
            self.head = raw_tail;
        }

        self.tail = raw_tail;
    }

    pub fn pop(&mut self) -> Option<T> {
        self.head.take().map(|head| {
            let head = *unsafe { Box::from_raw(head.as_ptr()) };
            self.head = head.next;

            if self.head.is_none() {
                self.tail = None;
            }

            head.elem
        })
    }

    pub fn peek(&self) -> Option<&T> {
        self.head
            .as_ref()
            .map(|node| unsafe { &(*node.as_ptr()).elem })
    }

    pub fn peek_mut(&mut self) -> Option<&mut T> {
        self.head
            .as_mut()
            .map(|node| unsafe { &mut (*node.as_ptr()).elem })
    }

    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }

    pub fn iter(&self) -> Iter<'_, T> {
        Iter {
            next: self.head.map(|ptr| unsafe { &*ptr.as_ptr() }),
        }
    }

    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
        IterMut {
            next: self.head.map(|ptr| unsafe { &mut *ptr.as_ptr() }),
        }
    }
}

pub struct IntoIter<T>(List<T>);

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut cur_link = self.head.take();
        while let Some(node_ptr) = cur_link {
            unsafe {
                cur_link = (*node_ptr.as_ptr()).next;
                Box::from_raw(node_ptr.as_ptr());
            }
        }
    }
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.pop()
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = unsafe { node.next.map(|x| &*x.as_ptr()) };
            &node.elem
        })
    }
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.take().map(|node| {
            self.next = unsafe { node.next.map(|x| &mut *x.as_ptr()) };
            &mut node.elem
        })
    }
}

mod test {
    use super::List;

    #[test]
    pub fn basics() {
        let mut list = List::new();

        // Check empty list behaves right
        assert_eq!(list.pop(), None);

        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);

        // Check normal removal
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push(4);
        list.push(5);

        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), None);

        // Check the exhaustion case fixed the pointer right
        list.push(6);
        list.push(7);

        // Check normal removal
        assert_eq!(list.pop(), Some(6));
        assert_eq!(list.pop(), Some(7));
        assert_eq!(list.pop(), None);
    }

    #[test]
    pub fn into_iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&3));
        assert_eq!(iter.next(), None);
    }

    #[test]
    pub fn iter_mut() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter_mut();
        assert_eq!(iter.next(), Some(&mut 1));
        assert_eq!(iter.next(), Some(&mut 2));
        assert_eq!(iter.next(), Some(&mut 3));
        assert_eq!(iter.next(), None);
    }
}

fn main() {
}

My understanding is the Box is marked noalias which can't alias with the tail pointer.

@Gankra
Copy link
Collaborator

Gankra commented Jan 24, 2022

Yeah, this is an annoying problem. This chapter was written in a time when we were far more wobbly around the pointer semantics of Rust, and this code was considered "fine". Alas, it is not.

@Gankra
Copy link
Collaborator

Gankra commented Jan 28, 2022

fixed in #215 😎

@Gankra Gankra closed this as completed Jan 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants