Skip to content

Commit 59b2945

Browse files
committed
Auto merge of #3385 - Zoxc:read-types, r=RalfJung
Report retags as distinct from real memory accesses for data races This changes the error reporting for data races such that reference invariants are no longer reported as real read and writes. Before: ``` Data race detected between (1) non-atomic write on thread `unnamed-6` and (2) non-atomic read on thread `unnamed-5` at alloc1034971+0x10c. (2) just happened here ``` After: ``` Data race detected between (1) non-atomic write on thread `unnamed-8` and (2) shared reference invariant on thread `unnamed-6` at alloc1018329+0x190. (2) just happened here ``` Non-atomic read accesses from the *other* thread don't have this information tracked so those are called `some potential non-atomic read access` here.
2 parents e19cc5e + 2d610f7 commit 59b2945

14 files changed

+202
-89
lines changed

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::borrow_tracker::{
1818
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
1919
GlobalStateInner, ProtectorKind,
2020
};
21+
use crate::concurrency::data_race::{NaReadType, NaWriteType};
2122
use crate::*;
2223

2324
use diagnostics::{RetagCause, RetagInfo};
@@ -751,7 +752,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
751752
assert_eq!(access, AccessKind::Write);
752753
// Make sure the data race model also knows about this.
753754
if let Some(data_race) = alloc_extra.data_race.as_mut() {
754-
data_race.write(alloc_id, range, machine)?;
755+
data_race.write(
756+
alloc_id,
757+
range,
758+
NaWriteType::Retag,
759+
Some(place.layout.ty),
760+
machine,
761+
)?;
755762
}
756763
}
757764
}
@@ -794,7 +801,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
794801
assert_eq!(access, AccessKind::Read);
795802
// Make sure the data race model also knows about this.
796803
if let Some(data_race) = alloc_extra.data_race.as_ref() {
797-
data_race.read(alloc_id, range, &this.machine)?;
804+
data_race.read(
805+
alloc_id,
806+
range,
807+
NaReadType::Retag,
808+
Some(place.layout.ty),
809+
&this.machine,
810+
)?;
798811
}
799812
}
800813
Ok(())

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ use rustc_middle::{
99
use rustc_span::def_id::DefId;
1010
use rustc_target::abi::{Abi, Size};
1111

12-
use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
1312
use crate::*;
13+
use crate::{
14+
borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind},
15+
concurrency::data_race::NaReadType,
16+
};
1417

1518
pub mod diagnostics;
1619
mod perms;
@@ -312,7 +315,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
312315
// Also inform the data race model (but only if any bytes are actually affected).
313316
if range.size.bytes() > 0 {
314317
if let Some(data_race) = alloc_extra.data_race.as_ref() {
315-
data_race.read(alloc_id, range, &this.machine)?;
318+
data_race.read(
319+
alloc_id,
320+
range,
321+
NaReadType::Retag,
322+
Some(place.layout.ty),
323+
&this.machine,
324+
)?;
316325
}
317326
}
318327

src/tools/miri/src/concurrency/data_race.rs

+79-62
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use std::{
4949
use rustc_ast::Mutability;
5050
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5151
use rustc_index::{Idx, IndexVec};
52-
use rustc_middle::mir;
52+
use rustc_middle::{mir, ty::Ty};
5353
use rustc_span::Span;
5454
use rustc_target::abi::{Align, HasDataLayout, Size};
5555

@@ -200,18 +200,38 @@ enum AtomicAccessType {
200200
Rmw,
201201
}
202202

203-
/// Type of write operation: allocating memory
204-
/// non-atomic writes and deallocating memory
205-
/// are all treated as writes for the purpose
206-
/// of the data-race detector.
203+
/// Type of a non-atomic read operation.
207204
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
208-
enum NaWriteType {
205+
pub enum NaReadType {
206+
/// Standard unsynchronized write.
207+
Read,
208+
209+
// An implicit read generated by a retag.
210+
Retag,
211+
}
212+
213+
impl NaReadType {
214+
fn description(self) -> &'static str {
215+
match self {
216+
NaReadType::Read => "non-atomic read",
217+
NaReadType::Retag => "retag read",
218+
}
219+
}
220+
}
221+
222+
/// Type of a non-atomic write operation: allocating memory, non-atomic writes, and
223+
/// deallocating memory are all treated as writes for the purpose of the data-race detector.
224+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
225+
pub enum NaWriteType {
209226
/// Allocate memory.
210227
Allocate,
211228

212229
/// Standard unsynchronized write.
213230
Write,
214231

232+
// An implicit write generated by a retag.
233+
Retag,
234+
215235
/// Deallocate memory.
216236
/// Note that when memory is deallocated first, later non-atomic accesses
217237
/// will be reported as use-after-free, not as data races.
@@ -224,44 +244,64 @@ impl NaWriteType {
224244
match self {
225245
NaWriteType::Allocate => "creating a new allocation",
226246
NaWriteType::Write => "non-atomic write",
247+
NaWriteType::Retag => "retag write",
227248
NaWriteType::Deallocate => "deallocation",
228249
}
229250
}
230251
}
231252

232253
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
233254
enum AccessType {
234-
NaRead,
255+
NaRead(NaReadType),
235256
NaWrite(NaWriteType),
236257
AtomicLoad,
237258
AtomicStore,
238259
AtomicRmw,
239260
}
240261

241262
impl AccessType {
242-
fn description(self) -> &'static str {
243-
match self {
244-
AccessType::NaRead => "non-atomic read",
263+
fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String {
264+
let mut msg = String::new();
265+
266+
if let Some(size) = size {
267+
msg.push_str(&format!("{}-byte {}", size.bytes(), msg))
268+
}
269+
270+
msg.push_str(match self {
271+
AccessType::NaRead(w) => w.description(),
245272
AccessType::NaWrite(w) => w.description(),
246273
AccessType::AtomicLoad => "atomic load",
247274
AccessType::AtomicStore => "atomic store",
248275
AccessType::AtomicRmw => "atomic read-modify-write",
276+
});
277+
278+
if let Some(ty) = ty {
279+
msg.push_str(&format!(" of type `{}`", ty));
249280
}
281+
282+
msg
250283
}
251284

252285
fn is_atomic(self) -> bool {
253286
match self {
254287
AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true,
255-
AccessType::NaRead | AccessType::NaWrite(_) => false,
288+
AccessType::NaRead(_) | AccessType::NaWrite(_) => false,
256289
}
257290
}
258291

259292
fn is_read(self) -> bool {
260293
match self {
261-
AccessType::AtomicLoad | AccessType::NaRead => true,
294+
AccessType::AtomicLoad | AccessType::NaRead(_) => true,
262295
AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false,
263296
}
264297
}
298+
299+
fn is_retag(self) -> bool {
300+
matches!(
301+
self,
302+
AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag)
303+
)
304+
}
265305
}
266306

267307
/// Memory Cell vector clock metadata
@@ -502,12 +542,14 @@ impl MemoryCellClocks {
502542
&mut self,
503543
thread_clocks: &mut ThreadClockSet,
504544
index: VectorIdx,
545+
read_type: NaReadType,
505546
current_span: Span,
506547
) -> Result<(), DataRace> {
507548
trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks);
508549
if !current_span.is_dummy() {
509550
thread_clocks.clock[index].span = current_span;
510551
}
552+
thread_clocks.clock[index].set_read_type(read_type);
511553
if self.write_was_before(&thread_clocks.clock) {
512554
let race_free = if let Some(atomic) = self.atomic() {
513555
// We must be ordered-after all atomic accesses, reads and writes.
@@ -875,7 +917,8 @@ impl VClockAlloc {
875917
/// This finds the two racing threads and the type
876918
/// of data-race that occurred. This will also
877919
/// return info about the memory location the data-race
878-
/// occurred in.
920+
/// occurred in. The `ty` parameter is used for diagnostics, letting
921+
/// the user know which type was involved in the access.
879922
#[cold]
880923
#[inline(never)]
881924
fn report_data_race<'tcx>(
@@ -885,6 +928,7 @@ impl VClockAlloc {
885928
access: AccessType,
886929
access_size: Size,
887930
ptr_dbg: Pointer<AllocId>,
931+
ty: Option<Ty<'_>>,
888932
) -> InterpResult<'tcx> {
889933
let (current_index, current_clocks) = global.current_thread_state(thread_mgr);
890934
let mut other_size = None; // if `Some`, this was a size-mismatch race
@@ -908,7 +952,7 @@ impl VClockAlloc {
908952
write_clock = mem_clocks.write();
909953
(AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock)
910954
} else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, &current_clocks.clock) {
911-
(AccessType::NaRead, idx, &mem_clocks.read)
955+
(AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read)
912956
// Finally, mixed-size races.
913957
} else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size {
914958
// This is only a race if we are not synchronized with all atomic accesses, so find
@@ -950,37 +994,33 @@ impl VClockAlloc {
950994
Err(err_machine_stop!(TerminationInfo::DataRace {
951995
involves_non_atomic,
952996
extra,
997+
retag_explain: access.is_retag() || other_access.is_retag(),
953998
ptr: ptr_dbg,
954999
op1: RacingOp {
955-
action: if let Some(other_size) = other_size {
956-
format!("{}-byte {}", other_size.bytes(), other_access.description())
957-
} else {
958-
other_access.description().to_owned()
959-
},
1000+
action: other_access.description(None, other_size),
9601001
thread_info: other_thread_info,
9611002
span: other_clock.as_slice()[other_thread.index()].span_data(),
9621003
},
9631004
op2: RacingOp {
964-
action: if other_size.is_some() {
965-
format!("{}-byte {}", access_size.bytes(), access.description())
966-
} else {
967-
access.description().to_owned()
968-
},
1005+
action: access.description(ty, other_size.map(|_| access_size)),
9691006
thread_info: current_thread_info,
9701007
span: current_clocks.clock.as_slice()[current_index.index()].span_data(),
9711008
},
9721009
}))?
9731010
}
9741011

975-
/// Detect data-races for an unsynchronized read operation, will not perform
1012+
/// Detect data-races for an unsynchronized read operation. It will not perform
9761013
/// data-race detection if `race_detecting()` is false, either due to no threads
9771014
/// being created or if it is temporarily disabled during a racy read or write
9781015
/// operation for which data-race detection is handled separately, for example
979-
/// atomic read operations.
1016+
/// atomic read operations. The `ty` parameter is used for diagnostics, letting
1017+
/// the user know which type was read.
9801018
pub fn read<'tcx>(
9811019
&self,
9821020
alloc_id: AllocId,
9831021
access_range: AllocRange,
1022+
read_type: NaReadType,
1023+
ty: Option<Ty<'_>>,
9841024
machine: &MiriMachine<'_, '_>,
9851025
) -> InterpResult<'tcx> {
9861026
let current_span = machine.current_span();
@@ -992,17 +1032,18 @@ impl VClockAlloc {
9921032
alloc_ranges.iter_mut(access_range.start, access_range.size)
9931033
{
9941034
if let Err(DataRace) =
995-
mem_clocks.read_race_detect(&mut thread_clocks, index, current_span)
1035+
mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span)
9961036
{
9971037
drop(thread_clocks);
9981038
// Report data-race.
9991039
return Self::report_data_race(
10001040
global,
10011041
&machine.threads,
10021042
mem_clocks,
1003-
AccessType::NaRead,
1043+
AccessType::NaRead(read_type),
10041044
access_range.size,
10051045
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1046+
ty,
10061047
);
10071048
}
10081049
}
@@ -1012,12 +1053,17 @@ impl VClockAlloc {
10121053
}
10131054
}
10141055

1015-
// Shared code for detecting data-races on unique access to a section of memory
1016-
fn unique_access<'tcx>(
1056+
/// Detect data-races for an unsynchronized write operation. It will not perform
1057+
/// data-race detection if `race_detecting()` is false, either due to no threads
1058+
/// being created or if it is temporarily disabled during a racy read or write
1059+
/// operation. The `ty` parameter is used for diagnostics, letting
1060+
/// the user know which type was written.
1061+
pub fn write<'tcx>(
10171062
&mut self,
10181063
alloc_id: AllocId,
10191064
access_range: AllocRange,
10201065
write_type: NaWriteType,
1066+
ty: Option<Ty<'_>>,
10211067
machine: &mut MiriMachine<'_, '_>,
10221068
) -> InterpResult<'tcx> {
10231069
let current_span = machine.current_span();
@@ -1042,6 +1088,7 @@ impl VClockAlloc {
10421088
AccessType::NaWrite(write_type),
10431089
access_range.size,
10441090
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1091+
ty,
10451092
);
10461093
}
10471094
}
@@ -1050,37 +1097,6 @@ impl VClockAlloc {
10501097
Ok(())
10511098
}
10521099
}
1053-
1054-
/// Detect data-races for an unsynchronized write operation, will not perform
1055-
/// data-race threads if `race_detecting()` is false, either due to no threads
1056-
/// being created or if it is temporarily disabled during a racy read or write
1057-
/// operation
1058-
pub fn write<'tcx>(
1059-
&mut self,
1060-
alloc_id: AllocId,
1061-
range: AllocRange,
1062-
machine: &mut MiriMachine<'_, '_>,
1063-
) -> InterpResult<'tcx> {
1064-
self.unique_access(alloc_id, range, NaWriteType::Write, machine)
1065-
}
1066-
1067-
/// Detect data-races for an unsynchronized deallocate operation, will not perform
1068-
/// data-race threads if `race_detecting()` is false, either due to no threads
1069-
/// being created or if it is temporarily disabled during a racy read or write
1070-
/// operation
1071-
pub fn deallocate<'tcx>(
1072-
&mut self,
1073-
alloc_id: AllocId,
1074-
size: Size,
1075-
machine: &mut MiriMachine<'_, '_>,
1076-
) -> InterpResult<'tcx> {
1077-
self.unique_access(
1078-
alloc_id,
1079-
alloc_range(Size::ZERO, size),
1080-
NaWriteType::Deallocate,
1081-
machine,
1082-
)
1083-
}
10841100
}
10851101

10861102
impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
@@ -1279,7 +1295,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
12791295
let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap();
12801296
trace!(
12811297
"Atomic op({}) with ordering {:?} on {:?} (size={})",
1282-
access.description(),
1298+
access.description(None, None),
12831299
&atomic,
12841300
place.ptr(),
12851301
size.bytes()
@@ -1307,6 +1323,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
13071323
alloc_id,
13081324
Size::from_bytes(mem_clocks_range.start),
13091325
),
1326+
None,
13101327
)
13111328
.map(|_| true);
13121329
}

0 commit comments

Comments
 (0)