File: segfault.rs

package info (click to toggle)
rustc-web 1.85.0%2Bdfsg3-1~deb12u3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,759,988 kB
  • sloc: xml: 158,127; python: 35,830; javascript: 19,497; cpp: 19,002; sh: 17,245; ansic: 13,127; asm: 4,376; makefile: 1,056; lisp: 29; perl: 29; ruby: 19; sql: 11
file content (66 lines) | stat: -rw-r--r-- 1,951 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//@ known-bug: #107975
//@ compile-flags: -Copt-level=2
//@ run-pass

// https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601

use std::{
    cell::{Ref, RefCell},
    ptr,
};

fn main() {
    let a: usize = {
        let v = 0u8;
        ptr::from_ref(&v).addr()
    };
    let b: usize = {
        let v = 0u8;
        ptr::from_ref(&v).addr()
    };
    let i: usize = b - a;

    // A surprise tool that will help us later.
    let arr = [
        RefCell::new(Some(Box::new(1u8))),
        RefCell::new(None),
        RefCell::new(None),
        RefCell::new(None),
    ];

    // `i` is not 0
    assert_ne!(i, 0);

    // Let's borrow the `i`-th element.
    // If `i` is out of bounds, indexing will panic.
    let r: Ref<Option<Box<u8>>> = arr[i].borrow();

    // If we got here, it means `i` was in bounds.
    // Now, two options are possible:
    // EITHER `i` is not 0 (as we have asserted above),
    // so the unwrap will panic, because only the 0-th element is `Some`
    // OR the assert lied, `i` *is* 0, and the `unwrap` will not panic.
    let r: &Box<u8> = r.as_ref().unwrap();

    // If we got here, it means `i` *was* actually 0.
    // Let's ignore the fact that the assert has lied
    // and try to take a mutable reference to the 0-th element.
    // `borrow_mut` should panic, because we are sill holding on
    // to a shared `Ref` for the same `RefCell`.
    *arr[0].borrow_mut() = None;

    // But it doesn't panic!
    // We have successfully replaced `Some(Box)` with `None`,
    // while holding a shared reference to it.
    // No unsafe involved.

    // The `Box` has been deallocated by now, so this is a dangling reference!
    let r: &u8 = &*r;
    println!("{:p}", r);

    // The following might segfault. Or it might not.
    // Depends on the platform semantics
    // and whatever happened to the pointed-to memory after deallocation.
    // let u: u8 = *r;
    // println!("{u}");
}