Skip to content

Commit f47e589

Browse files
committed
check that atomics are sufficiently aligned, and add test
1 parent 702f63e commit f47e589

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

src/shims/intrinsics.rs

+46-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_apfloat::Float;
22
use rustc::mir;
33
use rustc::mir::interpret::{InterpResult, PointerArithmetic};
4-
use rustc::ty::layout::{self, LayoutOf, Size};
4+
use rustc::ty::layout::{self, LayoutOf, Size, Align};
55
use rustc::ty;
66

77
use crate::{
@@ -48,30 +48,44 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4848
}
4949
}
5050

51+
"volatile_load" => {
52+
let ptr = this.deref_operand(args[0])?;
53+
this.copy_op(ptr.into(), dest)?;
54+
}
55+
56+
"volatile_store" => {
57+
let ptr = this.deref_operand(args[0])?;
58+
this.copy_op(args[1], ptr.into())?;
59+
}
60+
5161
"atomic_load" |
5262
"atomic_load_relaxed" |
5363
"atomic_load_acq" => {
5464
let ptr = this.deref_operand(args[0])?;
5565
let val = this.read_scalar(ptr.into())?; // make sure it fits into a scalar; otherwise it cannot be atomic
56-
this.write_scalar(val, dest)?;
57-
}
5866

59-
"volatile_load" => {
60-
let ptr = this.deref_operand(args[0])?;
61-
this.copy_op(ptr.into(), dest)?;
67+
// Check alignment requirements. Atomics must always be aligned to their size,
68+
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
69+
// be 8-aligned).
70+
let align = Align::from_bytes(ptr.layout.size.bytes()).unwrap();
71+
this.memory().check_ptr_access(ptr.ptr, ptr.layout.size, align)?;
72+
73+
this.write_scalar(val, dest)?;
6274
}
6375

6476
"atomic_store" |
6577
"atomic_store_relaxed" |
6678
"atomic_store_rel" => {
6779
let ptr = this.deref_operand(args[0])?;
6880
let val = this.read_scalar(args[1])?; // make sure it fits into a scalar; otherwise it cannot be atomic
69-
this.write_scalar(val, ptr.into())?;
70-
}
7181

72-
"volatile_store" => {
73-
let ptr = this.deref_operand(args[0])?;
74-
this.copy_op(args[1], ptr.into())?;
82+
// Check alignment requirements. Atomics must always be aligned to their size,
83+
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
84+
// be 8-aligned).
85+
let align = Align::from_bytes(ptr.layout.size.bytes()).unwrap();
86+
this.memory().check_ptr_access(ptr.ptr, ptr.layout.size, align)?;
87+
88+
this.write_scalar(val, ptr.into())?;
7589
}
7690

7791
"atomic_fence_acq" => {
@@ -82,6 +96,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8296
let ptr = this.deref_operand(args[0])?;
8397
let new = this.read_scalar(args[1])?;
8498
let old = this.read_scalar(ptr.into())?;
99+
100+
// Check alignment requirements. Atomics must always be aligned to their size,
101+
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
102+
// be 8-aligned).
103+
let align = Align::from_bytes(ptr.layout.size.bytes()).unwrap();
104+
this.memory().check_ptr_access(ptr.ptr, ptr.layout.size, align)?;
105+
85106
this.write_scalar(old, dest)?; // old value is returned
86107
this.write_scalar(new, ptr.into())?;
87108
}
@@ -91,6 +112,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
91112
let expect_old = this.read_immediate(args[1])?; // read as immediate for the sake of `binary_op()`
92113
let new = this.read_scalar(args[2])?;
93114
let old = this.read_immediate(ptr.into())?; // read as immediate for the sake of `binary_op()`
115+
116+
// Check alignment requirements. Atomics must always be aligned to their size,
117+
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
118+
// be 8-aligned).
119+
let align = Align::from_bytes(ptr.layout.size.bytes()).unwrap();
120+
this.memory().check_ptr_access(ptr.ptr, ptr.layout.size, align)?;
121+
94122
// binary_op will bail if either of them is not a scalar
95123
let (eq, _) = this.binary_op(mir::BinOp::Eq, old, expect_old)?;
96124
let res = Immediate::ScalarPair(old.to_scalar_or_undef(), eq.into());
@@ -137,6 +165,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
137165
}
138166
let rhs = this.read_immediate(args[1])?;
139167
let old = this.read_immediate(ptr.into())?;
168+
169+
// Check alignment requirements. Atomics must always be aligned to their size,
170+
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
171+
// be 8-aligned).
172+
let align = Align::from_bytes(ptr.layout.size.bytes()).unwrap();
173+
this.memory().check_ptr_access(ptr.ptr, ptr.layout.size, align)?;
174+
140175
this.write_immediate(*old, dest)?; // old value is returned
141176
let (op, neg) = match intrinsic_name.split('_').nth(1).unwrap() {
142177
"or" => (mir::BinOp::BitOr, false),
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(core_intrinsics)]
2+
3+
fn main() {
4+
// Do a 4-aligned u64 atomic access. That should be UB on all platforms,
5+
// even if u64 only has alignment 4.
6+
let z = [0u32; 2];
7+
let zptr = &z as *const _ as *const u64;
8+
unsafe {
9+
::std::intrinsics::atomic_load(zptr);
10+
//~^ ERROR tried to access memory with alignment 4, but alignment 8 is required
11+
}
12+
}

0 commit comments

Comments
 (0)