Report retags as distinct from real memory accesses for data races

This commit is contained in:
John Kåre Alsaker 2024-03-17 09:59:43 +01:00
parent 6d9549fbe6
commit 2d610f7473
14 changed files with 202 additions and 89 deletions

View file

@ -18,6 +18,7 @@
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
GlobalStateInner, ProtectorKind, GlobalStateInner, ProtectorKind,
}; };
use crate::concurrency::data_race::{NaReadType, NaWriteType};
use crate::*; use crate::*;
use diagnostics::{RetagCause, RetagInfo}; use diagnostics::{RetagCause, RetagInfo};
@ -751,7 +752,13 @@ fn sb_reborrow(
assert_eq!(access, AccessKind::Write); assert_eq!(access, AccessKind::Write);
// Make sure the data race model also knows about this. // Make sure the data race model also knows about this.
if let Some(data_race) = alloc_extra.data_race.as_mut() { if let Some(data_race) = alloc_extra.data_race.as_mut() {
data_race.write(alloc_id, range, machine)?; data_race.write(
alloc_id,
range,
NaWriteType::Retag,
Some(place.layout.ty),
machine,
)?;
} }
} }
} }
@ -794,7 +801,13 @@ fn sb_reborrow(
assert_eq!(access, AccessKind::Read); assert_eq!(access, AccessKind::Read);
// Make sure the data race model also knows about this. // Make sure the data race model also knows about this.
if let Some(data_race) = alloc_extra.data_race.as_ref() { if let Some(data_race) = alloc_extra.data_race.as_ref() {
data_race.read(alloc_id, range, &this.machine)?; data_race.read(
alloc_id,
range,
NaReadType::Retag,
Some(place.layout.ty),
&this.machine,
)?;
} }
} }
Ok(()) Ok(())

View file

@ -9,8 +9,11 @@
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_target::abi::{Abi, Size}; use rustc_target::abi::{Abi, Size};
use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
use crate::*; use crate::*;
use crate::{
borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind},
concurrency::data_race::NaReadType,
};
pub mod diagnostics; pub mod diagnostics;
mod perms; mod perms;
@ -312,7 +315,13 @@ fn tb_reborrow(
// Also inform the data race model (but only if any bytes are actually affected). // Also inform the data race model (but only if any bytes are actually affected).
if range.size.bytes() > 0 { if range.size.bytes() > 0 {
if let Some(data_race) = alloc_extra.data_race.as_ref() { if let Some(data_race) = alloc_extra.data_race.as_ref() {
data_race.read(alloc_id, range, &this.machine)?; data_race.read(
alloc_id,
range,
NaReadType::Retag,
Some(place.layout.ty),
&this.machine,
)?;
} }
} }

View file

@ -49,7 +49,7 @@
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_index::{Idx, IndexVec}; use rustc_index::{Idx, IndexVec};
use rustc_middle::mir; use rustc_middle::{mir, ty::Ty};
use rustc_span::Span; use rustc_span::Span;
use rustc_target::abi::{Align, HasDataLayout, Size}; use rustc_target::abi::{Align, HasDataLayout, Size};
@ -200,18 +200,38 @@ enum AtomicAccessType {
Rmw, Rmw,
} }
/// Type of write operation: allocating memory /// Type of a non-atomic read operation.
/// non-atomic writes and deallocating memory
/// are all treated as writes for the purpose
/// of the data-race detector.
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum NaWriteType { pub enum NaReadType {
/// Standard unsynchronized write.
Read,
// An implicit read generated by a retag.
Retag,
}
impl NaReadType {
fn description(self) -> &'static str {
match self {
NaReadType::Read => "non-atomic read",
NaReadType::Retag => "retag read",
}
}
}
/// Type of a non-atomic write operation: allocating memory, non-atomic writes, and
/// deallocating memory are all treated as writes for the purpose of the data-race detector.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum NaWriteType {
/// Allocate memory. /// Allocate memory.
Allocate, Allocate,
/// Standard unsynchronized write. /// Standard unsynchronized write.
Write, Write,
// An implicit write generated by a retag.
Retag,
/// Deallocate memory. /// Deallocate memory.
/// Note that when memory is deallocated first, later non-atomic accesses /// Note that when memory is deallocated first, later non-atomic accesses
/// will be reported as use-after-free, not as data races. /// will be reported as use-after-free, not as data races.
@ -224,6 +244,7 @@ fn description(self) -> &'static str {
match self { match self {
NaWriteType::Allocate => "creating a new allocation", NaWriteType::Allocate => "creating a new allocation",
NaWriteType::Write => "non-atomic write", NaWriteType::Write => "non-atomic write",
NaWriteType::Retag => "retag write",
NaWriteType::Deallocate => "deallocation", NaWriteType::Deallocate => "deallocation",
} }
} }
@ -231,7 +252,7 @@ fn description(self) -> &'static str {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AccessType { enum AccessType {
NaRead, NaRead(NaReadType),
NaWrite(NaWriteType), NaWrite(NaWriteType),
AtomicLoad, AtomicLoad,
AtomicStore, AtomicStore,
@ -239,29 +260,48 @@ enum AccessType {
} }
impl AccessType { impl AccessType {
fn description(self) -> &'static str { fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String {
match self { let mut msg = String::new();
AccessType::NaRead => "non-atomic read",
if let Some(size) = size {
msg.push_str(&format!("{}-byte {}", size.bytes(), msg))
}
msg.push_str(match self {
AccessType::NaRead(w) => w.description(),
AccessType::NaWrite(w) => w.description(), AccessType::NaWrite(w) => w.description(),
AccessType::AtomicLoad => "atomic load", AccessType::AtomicLoad => "atomic load",
AccessType::AtomicStore => "atomic store", AccessType::AtomicStore => "atomic store",
AccessType::AtomicRmw => "atomic read-modify-write", AccessType::AtomicRmw => "atomic read-modify-write",
});
if let Some(ty) = ty {
msg.push_str(&format!(" of type `{}`", ty));
} }
msg
} }
fn is_atomic(self) -> bool { fn is_atomic(self) -> bool {
match self { match self {
AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true, AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true,
AccessType::NaRead | AccessType::NaWrite(_) => false, AccessType::NaRead(_) | AccessType::NaWrite(_) => false,
} }
} }
fn is_read(self) -> bool { fn is_read(self) -> bool {
match self { match self {
AccessType::AtomicLoad | AccessType::NaRead => true, AccessType::AtomicLoad | AccessType::NaRead(_) => true,
AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false, AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false,
} }
} }
fn is_retag(self) -> bool {
matches!(
self,
AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag)
)
}
} }
/// Memory Cell vector clock metadata /// Memory Cell vector clock metadata
@ -502,12 +542,14 @@ fn read_race_detect(
&mut self, &mut self,
thread_clocks: &mut ThreadClockSet, thread_clocks: &mut ThreadClockSet,
index: VectorIdx, index: VectorIdx,
read_type: NaReadType,
current_span: Span, current_span: Span,
) -> Result<(), DataRace> { ) -> Result<(), DataRace> {
trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks);
if !current_span.is_dummy() { if !current_span.is_dummy() {
thread_clocks.clock[index].span = current_span; thread_clocks.clock[index].span = current_span;
} }
thread_clocks.clock[index].set_read_type(read_type);
if self.write_was_before(&thread_clocks.clock) { if self.write_was_before(&thread_clocks.clock) {
let race_free = if let Some(atomic) = self.atomic() { let race_free = if let Some(atomic) = self.atomic() {
// We must be ordered-after all atomic accesses, reads and writes. // We must be ordered-after all atomic accesses, reads and writes.
@ -875,7 +917,8 @@ fn find_gt_index(l: &VClock, r: &VClock) -> Option<VectorIdx> {
/// This finds the two racing threads and the type /// This finds the two racing threads and the type
/// of data-race that occurred. This will also /// of data-race that occurred. This will also
/// return info about the memory location the data-race /// return info about the memory location the data-race
/// occurred in. /// occurred in. The `ty` parameter is used for diagnostics, letting
/// the user know which type was involved in the access.
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn report_data_race<'tcx>( fn report_data_race<'tcx>(
@ -885,6 +928,7 @@ fn report_data_race<'tcx>(
access: AccessType, access: AccessType,
access_size: Size, access_size: Size,
ptr_dbg: Pointer<AllocId>, ptr_dbg: Pointer<AllocId>,
ty: Option<Ty<'_>>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let (current_index, current_clocks) = global.current_thread_state(thread_mgr); let (current_index, current_clocks) = global.current_thread_state(thread_mgr);
let mut other_size = None; // if `Some`, this was a size-mismatch race let mut other_size = None; // if `Some`, this was a size-mismatch race
@ -908,7 +952,7 @@ fn report_data_race<'tcx>(
write_clock = mem_clocks.write(); write_clock = mem_clocks.write();
(AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock) (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock)
} else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, &current_clocks.clock) { } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, &current_clocks.clock) {
(AccessType::NaRead, idx, &mem_clocks.read) (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read)
// Finally, mixed-size races. // Finally, mixed-size races.
} else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size { } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size {
// This is only a race if we are not synchronized with all atomic accesses, so find // This is only a race if we are not synchronized with all atomic accesses, so find
@ -950,37 +994,33 @@ fn report_data_race<'tcx>(
Err(err_machine_stop!(TerminationInfo::DataRace { Err(err_machine_stop!(TerminationInfo::DataRace {
involves_non_atomic, involves_non_atomic,
extra, extra,
retag_explain: access.is_retag() || other_access.is_retag(),
ptr: ptr_dbg, ptr: ptr_dbg,
op1: RacingOp { op1: RacingOp {
action: if let Some(other_size) = other_size { action: other_access.description(None, other_size),
format!("{}-byte {}", other_size.bytes(), other_access.description())
} else {
other_access.description().to_owned()
},
thread_info: other_thread_info, thread_info: other_thread_info,
span: other_clock.as_slice()[other_thread.index()].span_data(), span: other_clock.as_slice()[other_thread.index()].span_data(),
}, },
op2: RacingOp { op2: RacingOp {
action: if other_size.is_some() { action: access.description(ty, other_size.map(|_| access_size)),
format!("{}-byte {}", access_size.bytes(), access.description())
} else {
access.description().to_owned()
},
thread_info: current_thread_info, thread_info: current_thread_info,
span: current_clocks.clock.as_slice()[current_index.index()].span_data(), span: current_clocks.clock.as_slice()[current_index.index()].span_data(),
}, },
}))? }))?
} }
/// Detect data-races for an unsynchronized read operation, will not perform /// Detect data-races for an unsynchronized read operation. It will not perform
/// data-race detection if `race_detecting()` is false, either due to no threads /// data-race detection if `race_detecting()` is false, either due to no threads
/// being created or if it is temporarily disabled during a racy read or write /// being created or if it is temporarily disabled during a racy read or write
/// operation for which data-race detection is handled separately, for example /// operation for which data-race detection is handled separately, for example
/// atomic read operations. /// atomic read operations. The `ty` parameter is used for diagnostics, letting
/// the user know which type was read.
pub fn read<'tcx>( pub fn read<'tcx>(
&self, &self,
alloc_id: AllocId, alloc_id: AllocId,
access_range: AllocRange, access_range: AllocRange,
read_type: NaReadType,
ty: Option<Ty<'_>>,
machine: &MiriMachine<'_, '_>, machine: &MiriMachine<'_, '_>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let current_span = machine.current_span(); let current_span = machine.current_span();
@ -992,7 +1032,7 @@ pub fn read<'tcx>(
alloc_ranges.iter_mut(access_range.start, access_range.size) alloc_ranges.iter_mut(access_range.start, access_range.size)
{ {
if let Err(DataRace) = if let Err(DataRace) =
mem_clocks.read_race_detect(&mut thread_clocks, index, current_span) mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span)
{ {
drop(thread_clocks); drop(thread_clocks);
// Report data-race. // Report data-race.
@ -1000,9 +1040,10 @@ pub fn read<'tcx>(
global, global,
&machine.threads, &machine.threads,
mem_clocks, mem_clocks,
AccessType::NaRead, AccessType::NaRead(read_type),
access_range.size, access_range.size,
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
ty,
); );
} }
} }
@ -1012,12 +1053,17 @@ pub fn read<'tcx>(
} }
} }
// Shared code for detecting data-races on unique access to a section of memory /// Detect data-races for an unsynchronized write operation. It will not perform
fn unique_access<'tcx>( /// data-race detection if `race_detecting()` is false, either due to no threads
/// being created or if it is temporarily disabled during a racy read or write
/// operation. The `ty` parameter is used for diagnostics, letting
/// the user know which type was written.
pub fn write<'tcx>(
&mut self, &mut self,
alloc_id: AllocId, alloc_id: AllocId,
access_range: AllocRange, access_range: AllocRange,
write_type: NaWriteType, write_type: NaWriteType,
ty: Option<Ty<'_>>,
machine: &mut MiriMachine<'_, '_>, machine: &mut MiriMachine<'_, '_>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let current_span = machine.current_span(); let current_span = machine.current_span();
@ -1042,6 +1088,7 @@ fn unique_access<'tcx>(
AccessType::NaWrite(write_type), AccessType::NaWrite(write_type),
access_range.size, access_range.size,
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
ty,
); );
} }
} }
@ -1050,37 +1097,6 @@ fn unique_access<'tcx>(
Ok(()) Ok(())
} }
} }
/// Detect data-races for an unsynchronized write operation, will not perform
/// data-race threads if `race_detecting()` is false, either due to no threads
/// being created or if it is temporarily disabled during a racy read or write
/// operation
pub fn write<'tcx>(
&mut self,
alloc_id: AllocId,
range: AllocRange,
machine: &mut MiriMachine<'_, '_>,
) -> InterpResult<'tcx> {
self.unique_access(alloc_id, range, NaWriteType::Write, machine)
}
/// Detect data-races for an unsynchronized deallocate operation, will not perform
/// data-race threads if `race_detecting()` is false, either due to no threads
/// being created or if it is temporarily disabled during a racy read or write
/// operation
pub fn deallocate<'tcx>(
&mut self,
alloc_id: AllocId,
size: Size,
machine: &mut MiriMachine<'_, '_>,
) -> InterpResult<'tcx> {
self.unique_access(
alloc_id,
alloc_range(Size::ZERO, size),
NaWriteType::Deallocate,
machine,
)
}
} }
impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
@ -1279,7 +1295,7 @@ fn validate_atomic_op<A: Debug + Copy>(
let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap();
trace!( trace!(
"Atomic op({}) with ordering {:?} on {:?} (size={})", "Atomic op({}) with ordering {:?} on {:?} (size={})",
access.description(), access.description(None, None),
&atomic, &atomic,
place.ptr(), place.ptr(),
size.bytes() size.bytes()
@ -1307,6 +1323,7 @@ fn validate_atomic_op<A: Debug + Copy>(
alloc_id, alloc_id,
Size::from_bytes(mem_clocks_range.start), Size::from_bytes(mem_clocks_range.start),
), ),
None,
) )
.map(|_| true); .map(|_| true);
} }

View file

@ -4,9 +4,11 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
fmt::Debug, fmt::Debug,
ops::{Index, IndexMut}, ops::{Index, IndexMut, Shr},
}; };
use super::data_race::NaReadType;
/// A vector clock index, this is associated with a thread id /// A vector clock index, this is associated with a thread id
/// but in some cases one vector index may be shared with /// but in some cases one vector index may be shared with
/// multiple thread ids if it's safe to do so. /// multiple thread ids if it's safe to do so.
@ -50,13 +52,51 @@ fn from(id: u32) -> Self {
/// so that diagnostics can report what code was responsible for an operation. /// so that diagnostics can report what code was responsible for an operation.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct VTimestamp { pub struct VTimestamp {
time: u32, /// The lowest bit indicates read type, the rest is the time.
/// `1` indicates a retag read, `0` a regular read.
time_and_read_type: u32,
pub span: Span, pub span: Span,
} }
impl VTimestamp { impl VTimestamp {
pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP }; pub const ZERO: VTimestamp = VTimestamp::new(0, NaReadType::Read, DUMMY_SP);
#[inline]
const fn encode_time_and_read_type(time: u32, read_type: NaReadType) -> u32 {
let read_type_bit = match read_type {
NaReadType::Read => 0,
NaReadType::Retag => 1,
};
// Put the `read_type` in the lowest bit and `time` in the rest
read_type_bit | time.checked_mul(2).expect("Vector clock overflow")
}
#[inline]
const fn new(time: u32, read_type: NaReadType, span: Span) -> Self {
Self { time_and_read_type: Self::encode_time_and_read_type(time, read_type), span }
}
#[inline]
fn time(&self) -> u32 {
self.time_and_read_type.shr(1)
}
#[inline]
fn set_time(&mut self, time: u32) {
self.time_and_read_type = Self::encode_time_and_read_type(time, self.read_type());
}
#[inline]
pub fn read_type(&self) -> NaReadType {
if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag }
}
#[inline]
pub fn set_read_type(&mut self, read_type: NaReadType) {
self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type);
}
#[inline]
pub fn span_data(&self) -> SpanData { pub fn span_data(&self) -> SpanData {
self.span.data() self.span.data()
} }
@ -64,7 +104,7 @@ pub fn span_data(&self) -> SpanData {
impl PartialEq for VTimestamp { impl PartialEq for VTimestamp {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.time == other.time self.time() == other.time()
} }
} }
@ -78,7 +118,7 @@ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
impl Ord for VTimestamp { impl Ord for VTimestamp {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.time.cmp(&other.time) self.time().cmp(&other.time())
} }
} }
@ -130,7 +170,7 @@ pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) {
let idx = idx.index(); let idx = idx.index();
let mut_slice = self.get_mut_with_min_len(idx + 1); let mut_slice = self.get_mut_with_min_len(idx + 1);
let idx_ref = &mut mut_slice[idx]; let idx_ref = &mut mut_slice[idx];
idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow"); idx_ref.set_time(idx_ref.time().checked_add(1).expect("Vector clock overflow"));
if !current_span.is_dummy() { if !current_span.is_dummy() {
idx_ref.span = current_span; idx_ref.span = current_span;
} }
@ -379,8 +419,8 @@ fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp {
/// test suite /// test suite
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{VClock, VTimestamp, VectorIdx}; use super::{VClock, VTimestamp, VectorIdx};
use crate::concurrency::data_race::NaReadType;
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -448,7 +488,13 @@ fn from_slice(mut slice: &[u32]) -> VClock {
while let Some(0) = slice.last() { while let Some(0) = slice.last() {
slice = &slice[..slice.len() - 1] slice = &slice[..slice.len() - 1]
} }
VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect()) VClock(
slice
.iter()
.copied()
.map(|time| VTimestamp::new(time, NaReadType::Read, DUMMY_SP))
.collect(),
)
} }
fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) { fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) {

View file

@ -46,6 +46,7 @@ pub enum TerminationInfo {
op1: RacingOp, op1: RacingOp,
op2: RacingOp, op2: RacingOp,
extra: Option<&'static str>, extra: Option<&'static str>,
retag_explain: bool,
}, },
} }
@ -263,12 +264,17 @@ pub fn report_error<'tcx, 'mir>(
vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))], vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))],
Int2PtrWithStrictProvenance => Int2PtrWithStrictProvenance =>
vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))], vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))],
DataRace { op1, extra, .. } => { DataRace { op1, extra, retag_explain, .. } => {
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))]; let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
if let Some(extra) = extra { if let Some(extra) = extra {
helps.push((None, format!("{extra}"))); helps.push((None, format!("{extra}")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model")));
} }
if *retag_explain {
helps.push((None, "retags occur on all (re)borrows and as well as when references are copied or moved".to_owned()));
helps.push((None, "retags permit optimizations that insert speculative reads or writes".to_owned()));
helps.push((None, "therefore from the perspective of data races, a retag has the same implications as a read or write".to_owned()));
}
helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"))); helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")));
helps helps

View file

@ -1,5 +1,6 @@
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(cell_update)] #![feature(cell_update)]
#![feature(const_option)]
#![feature(float_gamma)] #![feature(float_gamma)]
#![feature(generic_nonzero)] #![feature(generic_nonzero)]
#![feature(map_try_insert)] #![feature(map_try_insert)]

View file

@ -36,6 +36,9 @@
*, *,
}; };
use self::concurrency::data_race::NaReadType;
use self::concurrency::data_race::NaWriteType;
/// First real-time signal. /// First real-time signal.
/// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35
/// as typical values. /// as typical values.
@ -1241,7 +1244,7 @@ fn before_memory_read(
.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read)); .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read));
} }
if let Some(data_race) = &alloc_extra.data_race { if let Some(data_race) = &alloc_extra.data_race {
data_race.read(alloc_id, range, machine)?; data_race.read(alloc_id, range, NaReadType::Read, None, machine)?;
} }
if let Some(borrow_tracker) = &alloc_extra.borrow_tracker { if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?; borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
@ -1265,7 +1268,7 @@ fn before_memory_write(
.emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write)); .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write));
} }
if let Some(data_race) = &mut alloc_extra.data_race { if let Some(data_race) = &mut alloc_extra.data_race {
data_race.write(alloc_id, range, machine)?; data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?;
} }
if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?; borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
@ -1289,7 +1292,13 @@ fn before_memory_deallocation(
machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
} }
if let Some(data_race) = &mut alloc_extra.data_race { if let Some(data_race) = &mut alloc_extra.data_race {
data_race.deallocate(alloc_id, size, machine)?; data_race.write(
alloc_id,
alloc_range(Size::ZERO, size),
NaWriteType::Deallocate,
None,
machine,
)?;
} }
if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?; borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;

View file

@ -17,7 +17,7 @@ fn thread_1(p: SendPtr) {
fn thread_2(p: SendPtr) { fn thread_2(p: SendPtr) {
let p = p.0; let p = p.0;
unsafe { unsafe {
*p = 5; //~ ERROR: /Data race detected between \(1\) non-atomic (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ *p = 5; //~ ERROR: /Data race detected between \(1\) retag (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/
} }
} }

View file

@ -1,14 +1,17 @@
error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here error: Undefined Behavior: Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
--> $DIR/retag_data_race_write.rs:LL:CC --> $DIR/retag_data_race_write.rs:LL:CC
| |
LL | *p = 5; LL | *p = 5;
| ^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | ^^^^^^ Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
| |
help: and (1) occurred earlier here help: and (1) occurred earlier here
--> $DIR/retag_data_race_write.rs:LL:CC --> $DIR/retag_data_race_write.rs:LL:CC
| |
LL | let _r = &mut *p; LL | let _r = &mut *p;
| ^^^^^^^ | ^^^^^^^
= help: retags occur on all (re)borrows and as well as when references are copied or moved
= help: retags permit optimizations that insert speculative reads or writes
= help: therefore from the perspective of data races, a retag has the same implications as a read or write
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE (of the first span) on thread `unnamed-ID`: = note: BACKTRACE (of the first span) on thread `unnamed-ID`:

View file

@ -1,14 +1,17 @@
error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
--> $DIR/retag_data_race_write.rs:LL:CC --> $DIR/retag_data_race_write.rs:LL:CC
| |
LL | *p = 5; LL | *p = 5;
| ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
| |
help: and (1) occurred earlier here help: and (1) occurred earlier here
--> $DIR/retag_data_race_write.rs:LL:CC --> $DIR/retag_data_race_write.rs:LL:CC
| |
LL | let _r = &mut *p; LL | let _r = &mut *p;
| ^^^^^^^ | ^^^^^^^
= help: retags occur on all (re)borrows and as well as when references are copied or moved
= help: retags permit optimizations that insert speculative reads or writes
= help: therefore from the perspective of data races, a retag has the same implications as a read or write
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE (of the first span) on thread `unnamed-ID`: = note: BACKTRACE (of the first span) on thread `unnamed-ID`:

View file

@ -13,7 +13,7 @@ fn main() {
let ptr = ptr; let ptr = ptr;
// We do a protected mutable retag (but no write!) in this thread. // We do a protected mutable retag (but no write!) in this thread.
fn retag(_x: &mut i32) {} fn retag(_x: &mut i32) {}
retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-1` retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-1`
}); });
// We do a read in the main thread. // We do a read in the main thread.

View file

@ -1,14 +1,17 @@
error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here
--> $DIR/retag_data_race_protected_read.rs:LL:CC --> $DIR/retag_data_race_protected_read.rs:LL:CC
| |
LL | retag(unsafe { &mut *ptr.0 }); LL | retag(unsafe { &mut *ptr.0 });
| ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here
| |
help: and (1) occurred earlier here help: and (1) occurred earlier here
--> $DIR/retag_data_race_protected_read.rs:LL:CC --> $DIR/retag_data_race_protected_read.rs:LL:CC
| |
LL | unsafe { ptr.0.read() }; LL | unsafe { ptr.0.read() };
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
= help: retags occur on all (re)borrows and as well as when references are copied or moved
= help: retags permit optimizations that insert speculative reads or writes
= help: therefore from the perspective of data races, a retag has the same implications as a read or write
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE (of the first span) on thread `unnamed-ID`: = note: BACKTRACE (of the first span) on thread `unnamed-ID`:

View file

@ -15,7 +15,7 @@ fn thread_1(p: SendPtr) {
fn thread_2(p: SendPtr) { fn thread_2(p: SendPtr) {
let p = p.0; let p = p.0;
unsafe { unsafe {
*p = 5; //~ ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` *p = 5; //~ ERROR: Data race detected between (1) retag read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2`
} }
} }

View file

@ -1,14 +1,17 @@
error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
--> $DIR/retag_data_race_read.rs:LL:CC --> $DIR/retag_data_race_read.rs:LL:CC
| |
LL | *p = 5; LL | *p = 5;
| ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here
| |
help: and (1) occurred earlier here help: and (1) occurred earlier here
--> $DIR/retag_data_race_read.rs:LL:CC --> $DIR/retag_data_race_read.rs:LL:CC
| |
LL | let _r = &*p; LL | let _r = &*p;
| ^^^ | ^^^
= help: retags occur on all (re)borrows and as well as when references are copied or moved
= help: retags permit optimizations that insert speculative reads or writes
= help: therefore from the perspective of data races, a retag has the same implications as a read or write
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE (of the first span) on thread `unnamed-ID`: = note: BACKTRACE (of the first span) on thread `unnamed-ID`: