Skip to content

Commit c162fd3

Browse files
committed
Auto merge of rust-lang#2636 - RalfJung:scalar-field-retag, r=oli-obk
Stacked Borrows: make scalar field retagging the default I think it is time to finally close this soundness gap. Any objections? :) Unfortunately the latest released versions of hashbrown and scopeguard can fail under full field retagging. The fixes have landed in the git repos but have not been released yet. I don't know if scalar field retagging as enabled by this PR is sufficient to cause problems with these crates, but it seems likely that this would be the case -- e.g. if both `value` and `dropfn` are scalars, the entire scopeguard struct will be a `ScalarPair` and thus get field retagging. However, given that we actually generate LLVM `noalias` for these cases, it seems prudent to inform users of this risk. They can easily set `-Zmiri-field-retag=none` to opt-out of this change. Cc rust-lang/miri#2528
2 parents 79a48ce + 1470e99 commit c162fd3

14 files changed

+76
-42
lines changed

src/tools/miri/README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,15 @@ to Miri failing to detect cases of undefined behavior in a program.
374374
application instead of raising an error within the context of Miri (and halting
375375
execution). Note that code might not expect these operations to ever panic, so
376376
this flag can lead to strange (mis)behavior.
377-
* `-Zmiri-retag-fields` changes Stacked Borrows retagging to recurse into fields.
377+
* `-Zmiri-retag-fields` changes Stacked Borrows retagging to recurse into *all* fields.
378378
This means that references in fields of structs/enums/tuples/arrays/... are retagged,
379379
and in particular, they are protected when passed as function arguments.
380+
(The default is to recurse only in cases where rustc would actually emit a `noalias` attribute.)
380381
* `-Zmiri-retag-fields=<all|none|scalar>` controls when Stacked Borrows retagging recurses into
381382
fields. `all` means it always recurses (like `-Zmiri-retag-fields`), `none` means it never
382-
recurses (the default), `scalar` means it only recurses for types where we would also emit
383+
recurses, `scalar` (the default) means it only recurses for types where we would also emit
383384
`noalias` annotations in the generated LLVM IR (types passed as indivudal scalars or pairs of
384-
scalars).
385+
scalars). Setting this to `none` is **unsound**.
385386
* `-Zmiri-tag-gc=<blocks>` configures how often the pointer tag garbage collector runs. The default
386387
is to search for and remove unreachable tags once every `10000` basic blocks. Setting this to
387388
`0` disables the garbage collector, which causes some programs to have explosive memory usage

src/tools/miri/src/eval.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl Default for MiriConfig {
163163
mute_stdout_stderr: false,
164164
preemption_rate: 0.01, // 1%
165165
report_progress: None,
166-
retag_fields: RetagFields::No,
166+
retag_fields: RetagFields::OnlyScalar,
167167
external_so_file: None,
168168
gc_interval: 10_000,
169169
num_cpus: 1,

src/tools/miri/tests/fail/stacked_borrows/newtype_pair_retagging.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//@compile-flags: -Zmiri-retag-fields=scalar
21
//@error-pattern: which is protected
32
struct Newtype<'a>(&'a mut i32, i32);
43

src/tools/miri/tests/fail/stacked_borrows/newtype_retagging.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//@compile-flags: -Zmiri-retag-fields=scalar
21
//@error-pattern: which is protected
32
struct Newtype<'a>(&'a mut i32);
43

Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Make sure that we cannot return a `&mut` that got already invalidated, not even in an `Option`.
2-
// Due to shallow reborrowing, the error only surfaces when we look into the `Option`.
32
fn foo(x: &mut (i32, i32)) -> Option<&mut i32> {
43
let xraw = x as *mut (i32, i32);
54
let ret = unsafe { &mut (*xraw).1 }; // let-bind to avoid 2phase
65
let ret = Some(ret);
76
let _val = unsafe { *xraw }; // invalidate xref
8-
ret
7+
ret //~ ERROR: /retag .* tag does not exist in the borrow stack/
98
}
109

1110
fn main() {
1211
match foo(&mut (1, 2)) {
13-
Some(_x) => {} //~ ERROR: /retag .* tag does not exist in the borrow stack/
12+
Some(_x) => {}
1413
None => {}
1514
}
1615
}

src/tools/miri/tests/fail/stacked_borrows/return_invalid_mut_option.stderr

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
error: Undefined Behavior: trying to retag from <TAG> for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
22
--> $DIR/return_invalid_mut_option.rs:LL:CC
33
|
4-
LL | Some(_x) => {}
5-
| ^^
6-
| |
7-
| trying to retag from <TAG> for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8-
| this error occurs as part of retag at ALLOC[0x4..0x8]
4+
LL | ret
5+
| ^^^
6+
| |
7+
| trying to retag from <TAG> for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8+
| this error occurs as part of retag at ALLOC[0x4..0x8]
99
|
1010
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
1111
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
1212
help: <TAG> was created by a Unique retag at offsets [0x4..0x8]
1313
--> $DIR/return_invalid_mut_option.rs:LL:CC
1414
|
1515
LL | let ret = Some(ret);
16-
| ^^^
16+
| ^^^^^^^^^
1717
help: <TAG> was later invalidated at offsets [0x0..0x8] by a read access
1818
--> $DIR/return_invalid_mut_option.rs:LL:CC
1919
|
2020
LL | let _val = unsafe { *xraw }; // invalidate xref
2121
| ^^^^^
2222
= note: BACKTRACE:
23-
= note: inside `main` at $DIR/return_invalid_mut_option.rs:LL:CC
23+
= note: inside `foo` at $DIR/return_invalid_mut_option.rs:LL:CC
24+
note: inside `main` at $DIR/return_invalid_mut_option.rs:LL:CC
25+
--> $DIR/return_invalid_mut_option.rs:LL:CC
26+
|
27+
LL | match foo(&mut (1, 2)) {
28+
| ^^^^^^^^^^^^^^^^
2429

2530
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2631

Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Make sure that we cannot return a `&mut` that got already invalidated, not even in a tuple.
2-
// Due to shallow reborrowing, the error only surfaces when we look into the tuple.
32
fn foo(x: &mut (i32, i32)) -> (&mut i32,) {
43
let xraw = x as *mut (i32, i32);
54
let ret = (unsafe { &mut (*xraw).1 },);
65
let _val = unsafe { *xraw }; // invalidate xref
7-
ret
6+
ret //~ ERROR: /retag .* tag does not exist in the borrow stack/
87
}
98

109
fn main() {
11-
foo(&mut (1, 2)).0; //~ ERROR: /retag .* tag does not exist in the borrow stack/
10+
foo(&mut (1, 2)).0;
1211
}

src/tools/miri/tests/fail/stacked_borrows/return_invalid_mut_tuple.stderr

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: Undefined Behavior: trying to retag from <TAG> for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
22
--> $DIR/return_invalid_mut_tuple.rs:LL:CC
33
|
4-
LL | foo(&mut (1, 2)).0;
5-
| ^^^^^^^^^^^^^^^^^^
4+
LL | ret
5+
| ^^^
66
| |
77
| trying to retag from <TAG> for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
88
| this error occurs as part of retag at ALLOC[0x4..0x8]
@@ -13,14 +13,19 @@ help: <TAG> was created by a Unique retag at offsets [0x4..0x8]
1313
--> $DIR/return_invalid_mut_tuple.rs:LL:CC
1414
|
1515
LL | let ret = (unsafe { &mut (*xraw).1 },);
16-
| ^^^^^^^^^^^^^^
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1717
help: <TAG> was later invalidated at offsets [0x0..0x8] by a read access
1818
--> $DIR/return_invalid_mut_tuple.rs:LL:CC
1919
|
2020
LL | let _val = unsafe { *xraw }; // invalidate xref
2121
| ^^^^^
2222
= note: BACKTRACE:
23-
= note: inside `main` at $DIR/return_invalid_mut_tuple.rs:LL:CC
23+
= note: inside `foo` at $DIR/return_invalid_mut_tuple.rs:LL:CC
24+
note: inside `main` at $DIR/return_invalid_mut_tuple.rs:LL:CC
25+
--> $DIR/return_invalid_mut_tuple.rs:LL:CC
26+
|
27+
LL | foo(&mut (1, 2)).0;
28+
| ^^^^^^^^^^^^^^^^
2429

2530
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2631

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// Make sure that we cannot return a `&` that got already invalidated, not even in an `Option`.
2-
// Due to shallow reborrowing, the error only surfaces when we look into the `Option`.
32
fn foo(x: &mut (i32, i32)) -> Option<&i32> {
43
let xraw = x as *mut (i32, i32);
54
let ret = Some(unsafe { &(*xraw).1 });
65
unsafe { *xraw = (42, 23) }; // unfreeze
7-
ret
6+
ret //~ ERROR: /retag .* tag does not exist in the borrow stack/
87
}
98

109
fn main() {
1110
match foo(&mut (1, 2)) {
12-
Some(_x) => {} //~ ERROR: /retag .* tag does not exist in the borrow stack/
11+
Some(_x) => {}
1312
None => {}
1413
}
1514
}

src/tools/miri/tests/fail/stacked_borrows/return_invalid_shr_option.stderr

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
error: Undefined Behavior: trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
22
--> $DIR/return_invalid_shr_option.rs:LL:CC
33
|
4-
LL | Some(_x) => {}
5-
| ^^
6-
| |
7-
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8-
| this error occurs as part of retag at ALLOC[0x4..0x8]
4+
LL | ret
5+
| ^^^
6+
| |
7+
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
8+
| this error occurs as part of retag at ALLOC[0x4..0x8]
99
|
1010
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
1111
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
1212
help: <TAG> was created by a SharedReadOnly retag at offsets [0x4..0x8]
1313
--> $DIR/return_invalid_shr_option.rs:LL:CC
1414
|
1515
LL | let ret = Some(unsafe { &(*xraw).1 });
16-
| ^^^^^^^^^^
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
1717
help: <TAG> was later invalidated at offsets [0x0..0x8] by a write access
1818
--> $DIR/return_invalid_shr_option.rs:LL:CC
1919
|
2020
LL | unsafe { *xraw = (42, 23) }; // unfreeze
2121
| ^^^^^^^^^^^^^^^^
2222
= note: BACKTRACE:
23-
= note: inside `main` at $DIR/return_invalid_shr_option.rs:LL:CC
23+
= note: inside `foo` at $DIR/return_invalid_shr_option.rs:LL:CC
24+
note: inside `main` at $DIR/return_invalid_shr_option.rs:LL:CC
25+
--> $DIR/return_invalid_shr_option.rs:LL:CC
26+
|
27+
LL | match foo(&mut (1, 2)) {
28+
| ^^^^^^^^^^^^^^^^
2429

2530
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2631

Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Make sure that we cannot return a `&` that got already invalidated, not even in a tuple.
2-
// Due to shallow reborrowing, the error only surfaces when we look into the tuple.
32
fn foo(x: &mut (i32, i32)) -> (&i32,) {
43
let xraw = x as *mut (i32, i32);
54
let ret = (unsafe { &(*xraw).1 },);
65
unsafe { *xraw = (42, 23) }; // unfreeze
7-
ret
6+
ret //~ ERROR: /retag .* tag does not exist in the borrow stack/
87
}
98

109
fn main() {
11-
foo(&mut (1, 2)).0; //~ ERROR: /retag .* tag does not exist in the borrow stack/
10+
foo(&mut (1, 2)).0;
1211
}

src/tools/miri/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: Undefined Behavior: trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
22
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
33
|
4-
LL | foo(&mut (1, 2)).0;
5-
| ^^^^^^^^^^^^^^^^^^
4+
LL | ret
5+
| ^^^
66
| |
77
| trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location
88
| this error occurs as part of retag at ALLOC[0x4..0x8]
@@ -13,14 +13,19 @@ help: <TAG> was created by a SharedReadOnly retag at offsets [0x4..0x8]
1313
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
1414
|
1515
LL | let ret = (unsafe { &(*xraw).1 },);
16-
| ^^^^^^^^^^
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^
1717
help: <TAG> was later invalidated at offsets [0x0..0x8] by a write access
1818
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
1919
|
2020
LL | unsafe { *xraw = (42, 23) }; // unfreeze
2121
| ^^^^^^^^^^^^^^^^
2222
= note: BACKTRACE:
23-
= note: inside `main` at $DIR/return_invalid_shr_tuple.rs:LL:CC
23+
= note: inside `foo` at $DIR/return_invalid_shr_tuple.rs:LL:CC
24+
note: inside `main` at $DIR/return_invalid_shr_tuple.rs:LL:CC
25+
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
26+
|
27+
LL | foo(&mut (1, 2)).0;
28+
| ^^^^^^^^^^^^^^^^
2429

2530
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2631

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@compile-flags: -Zmiri-retag-fields=none
2+
3+
struct Newtype<'a>(&'a mut i32);
4+
5+
fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) {
6+
dealloc();
7+
}
8+
9+
// Make sure that we do *not* retag the fields of `Newtype`.
10+
fn main() {
11+
let ptr = Box::into_raw(Box::new(0i32));
12+
#[rustfmt::skip] // I like my newlines
13+
unsafe {
14+
dealloc_while_running(
15+
Newtype(&mut *ptr),
16+
|| drop(Box::from_raw(ptr)),
17+
)
18+
};
19+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
0..1: [ SharedReadWrite<TAG> ]
22
0..1: [ SharedReadWrite<TAG> ]
33
0..1: [ SharedReadWrite<TAG> ]
4-
0..1: [ SharedReadWrite<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> ]
5-
0..1: [ SharedReadWrite<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> SharedReadOnly<TAG> ]
4+
0..1: [ SharedReadWrite<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> Unique<TAG> ]
5+
0..1: [ SharedReadWrite<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> Disabled<TAG> SharedReadOnly<TAG> ]
66
0..1: [ unknown-bottom(..<TAG>) ]

0 commit comments

Comments
 (0)