Skip to content

Commit b8bd42e

Browse files
authored
Merge pull request rust-lang#234 from RalfJung/reallocate
Make Reallocate & Deallocate less permissive
2 parents 9a04be9 + 4165051 commit b8bd42e

File tree

10 files changed

+193
-88
lines changed

10 files changed

+193
-88
lines changed

src/error.rs

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub enum EvalError<'tcx> {
5858
TypeNotPrimitive(Ty<'tcx>),
5959
ReallocatedStaticMemory,
6060
DeallocatedStaticMemory,
61+
ReallocateNonBasePtr,
62+
DeallocateNonBasePtr,
63+
IncorrectAllocationInformation,
6164
Layout(layout::LayoutError<'tcx>),
6265
HeapAllocZeroBytes,
6366
HeapAllocNonPowerOfTwoAlignment(u64),
@@ -72,98 +75,105 @@ pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
7275

7376
impl<'tcx> Error for EvalError<'tcx> {
7477
fn description(&self) -> &str {
78+
use EvalError::*;
7579
match *self {
76-
EvalError::FunctionPointerTyMismatch(..) =>
80+
FunctionPointerTyMismatch(..) =>
7781
"tried to call a function through a function pointer of a different type",
78-
EvalError::InvalidMemoryAccess =>
82+
InvalidMemoryAccess =>
7983
"tried to access memory through an invalid pointer",
80-
EvalError::DanglingPointerDeref =>
84+
DanglingPointerDeref =>
8185
"dangling pointer was dereferenced",
82-
EvalError::InvalidFunctionPointer =>
86+
InvalidFunctionPointer =>
8387
"tried to use an integer pointer or a dangling pointer as a function pointer",
84-
EvalError::InvalidBool =>
88+
InvalidBool =>
8589
"invalid boolean value read",
86-
EvalError::InvalidDiscriminant =>
90+
InvalidDiscriminant =>
8791
"invalid enum discriminant value read",
88-
EvalError::PointerOutOfBounds { .. } =>
92+
PointerOutOfBounds { .. } =>
8993
"pointer offset outside bounds of allocation",
90-
EvalError::InvalidNullPointerUsage =>
94+
InvalidNullPointerUsage =>
9195
"invalid use of NULL pointer",
92-
EvalError::ReadPointerAsBytes =>
96+
ReadPointerAsBytes =>
9397
"a raw memory access tried to access part of a pointer value as raw bytes",
94-
EvalError::ReadBytesAsPointer =>
98+
ReadBytesAsPointer =>
9599
"a memory access tried to interpret some bytes as a pointer",
96-
EvalError::InvalidPointerMath =>
100+
InvalidPointerMath =>
97101
"attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations",
98-
EvalError::ReadUndefBytes =>
102+
ReadUndefBytes =>
99103
"attempted to read undefined bytes",
100-
EvalError::DeadLocal =>
104+
DeadLocal =>
101105
"tried to access a dead local variable",
102-
EvalError::InvalidBoolOp(_) =>
106+
InvalidBoolOp(_) =>
103107
"invalid boolean operation",
104-
EvalError::Unimplemented(ref msg) => msg,
105-
EvalError::DerefFunctionPointer =>
108+
Unimplemented(ref msg) => msg,
109+
DerefFunctionPointer =>
106110
"tried to dereference a function pointer",
107-
EvalError::ExecuteMemory =>
111+
ExecuteMemory =>
108112
"tried to treat a memory pointer as a function pointer",
109-
EvalError::ArrayIndexOutOfBounds(..) =>
113+
ArrayIndexOutOfBounds(..) =>
110114
"array index out of bounds",
111-
EvalError::Math(..) =>
115+
Math(..) =>
112116
"mathematical operation failed",
113-
EvalError::Intrinsic(..) =>
117+
Intrinsic(..) =>
114118
"intrinsic failed",
115-
EvalError::OverflowingMath =>
119+
OverflowingMath =>
116120
"attempted to do overflowing math",
117-
EvalError::NoMirFor(..) =>
121+
NoMirFor(..) =>
118122
"mir not found",
119-
EvalError::InvalidChar(..) =>
123+
InvalidChar(..) =>
120124
"tried to interpret an invalid 32-bit value as a char",
121-
EvalError::OutOfMemory{..} =>
125+
OutOfMemory{..} =>
122126
"could not allocate more memory",
123-
EvalError::ExecutionTimeLimitReached =>
127+
ExecutionTimeLimitReached =>
124128
"reached the configured maximum execution time",
125-
EvalError::StackFrameLimitReached =>
129+
StackFrameLimitReached =>
126130
"reached the configured maximum number of stack frames",
127-
EvalError::OutOfTls =>
131+
OutOfTls =>
128132
"reached the maximum number of representable TLS keys",
129-
EvalError::TlsOutOfBounds =>
133+
TlsOutOfBounds =>
130134
"accessed an invalid (unallocated) TLS key",
131-
EvalError::AbiViolation(ref msg) => msg,
132-
EvalError::AlignmentCheckFailed{..} =>
135+
AbiViolation(ref msg) => msg,
136+
AlignmentCheckFailed{..} =>
133137
"tried to execute a misaligned read or write",
134-
EvalError::CalledClosureAsFunction =>
138+
CalledClosureAsFunction =>
135139
"tried to call a closure through a function pointer",
136-
EvalError::VtableForArgumentlessMethod =>
140+
VtableForArgumentlessMethod =>
137141
"tried to call a vtable function without arguments",
138-
EvalError::ModifiedConstantMemory =>
142+
ModifiedConstantMemory =>
139143
"tried to modify constant memory",
140-
EvalError::AssumptionNotHeld =>
144+
AssumptionNotHeld =>
141145
"`assume` argument was false",
142-
EvalError::InlineAsm =>
146+
InlineAsm =>
143147
"miri does not support inline assembly",
144-
EvalError::TypeNotPrimitive(_) =>
148+
TypeNotPrimitive(_) =>
145149
"expected primitive type, got nonprimitive",
146-
EvalError::ReallocatedStaticMemory =>
150+
ReallocatedStaticMemory =>
147151
"tried to reallocate static memory",
148-
EvalError::DeallocatedStaticMemory =>
152+
DeallocatedStaticMemory =>
149153
"tried to deallocate static memory",
150-
EvalError::Layout(_) =>
154+
ReallocateNonBasePtr =>
155+
"tried to reallocate with a pointer not to the beginning of an existing object",
156+
DeallocateNonBasePtr =>
157+
"tried to deallocate with a pointer not to the beginning of an existing object",
158+
IncorrectAllocationInformation =>
159+
"tried to deallocate or reallocate using incorrect alignment or size",
160+
Layout(_) =>
151161
"rustc layout computation failed",
152-
EvalError::UnterminatedCString(_) =>
162+
UnterminatedCString(_) =>
153163
"attempted to get length of a null terminated string, but no null found before end of allocation",
154-
EvalError::HeapAllocZeroBytes =>
164+
HeapAllocZeroBytes =>
155165
"tried to re-, de- or allocate zero bytes on the heap",
156-
EvalError::HeapAllocNonPowerOfTwoAlignment(_) =>
166+
HeapAllocNonPowerOfTwoAlignment(_) =>
157167
"tried to re-, de-, or allocate heap memory with alignment that is not a power of two",
158-
EvalError::Unreachable =>
168+
Unreachable =>
159169
"entered unreachable code",
160-
EvalError::Panic =>
170+
Panic =>
161171
"the evaluated program panicked",
162-
EvalError::NeedsRfc(_) =>
172+
NeedsRfc(_) =>
163173
"this feature needs an rfc before being allowed inside constants",
164-
EvalError::NotConst(_) =>
174+
NotConst(_) =>
165175
"this feature is not compatible with constant evaluation",
166-
EvalError::ReadFromReturnPointer =>
176+
ReadFromReturnPointer =>
167177
"tried to read from the return pointer",
168178
}
169179
}
@@ -173,36 +183,37 @@ impl<'tcx> Error for EvalError<'tcx> {
173183

174184
impl<'tcx> fmt::Display for EvalError<'tcx> {
175185
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186+
use EvalError::*;
176187
match *self {
177-
EvalError::PointerOutOfBounds { ptr, access, allocation_size } => {
188+
PointerOutOfBounds { ptr, access, allocation_size } => {
178189
write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}",
179190
if access { "memory access" } else { "pointer computed" },
180191
ptr.offset, ptr.alloc_id, allocation_size)
181192
},
182-
EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
183-
EvalError::FunctionPointerTyMismatch(sig, got) =>
193+
NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
194+
FunctionPointerTyMismatch(sig, got) =>
184195
write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig, got),
185-
EvalError::ArrayIndexOutOfBounds(span, len, index) =>
196+
ArrayIndexOutOfBounds(span, len, index) =>
186197
write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span),
187-
EvalError::Math(span, ref err) =>
198+
Math(span, ref err) =>
188199
write!(f, "{:?} at {:?}", err, span),
189-
EvalError::Intrinsic(ref err) =>
200+
Intrinsic(ref err) =>
190201
write!(f, "{}", err),
191-
EvalError::InvalidChar(c) =>
202+
InvalidChar(c) =>
192203
write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
193-
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
204+
OutOfMemory { allocation_size, memory_size, memory_usage } =>
194205
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
195206
allocation_size, memory_size - memory_usage, memory_size),
196-
EvalError::AlignmentCheckFailed { required, has } =>
207+
AlignmentCheckFailed { required, has } =>
197208
write!(f, "tried to access memory with alignment {}, but alignment {} is required",
198209
has, required),
199-
EvalError::TypeNotPrimitive(ty) =>
210+
TypeNotPrimitive(ty) =>
200211
write!(f, "expected primitive type, got {}", ty),
201-
EvalError::Layout(ref err) =>
212+
Layout(ref err) =>
202213
write!(f, "rustc layout computation failed: {:?}", err),
203-
EvalError::NeedsRfc(ref msg) =>
214+
NeedsRfc(ref msg) =>
204215
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
205-
EvalError::NotConst(ref msg) =>
216+
NotConst(ref msg) =>
206217
write!(f, "Cannot evaluate within constants: \"{}\"", msg),
207218
_ => write!(f, "{}", self.description()),
208219
}

src/eval_context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
412412
trace!("deallocating local");
413413
let ptr = ptr.to_ptr()?;
414414
self.memory.dump_alloc(ptr.alloc_id);
415-
match self.memory.deallocate(ptr) {
415+
match self.memory.deallocate(ptr, None) {
416416
// We could alternatively check whether the alloc_id is static before calling
417417
// deallocate, but this is much simpler and is probably the rare case.
418418
Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {},
@@ -1714,7 +1714,7 @@ pub fn eval_main<'a, 'tcx: 'a>(
17141714

17151715
while ecx.step()? {}
17161716
if let Some(cleanup_ptr) = cleanup_ptr {
1717-
ecx.memory.deallocate(cleanup_ptr)?;
1717+
ecx.memory.deallocate(cleanup_ptr, None)?;
17181718
}
17191719
return Ok(());
17201720
}

src/memory.rs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -223,58 +223,74 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
223223

224224
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
225225
// when reallocating/deallocating any others.
226-
pub fn reallocate(&mut self, ptr: Pointer, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> {
226+
pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> {
227227
assert!(align.is_power_of_two());
228228
// TODO(solson): Report error about non-__rust_allocate'd pointer.
229-
if ptr.offset != 0 {
230-
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
229+
if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() {
230+
return Err(EvalError::ReallocateNonBasePtr);
231231
}
232232
if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) {
233233
return Err(EvalError::ReallocatedStaticMemory);
234234
}
235235

236236
let size = self.get(ptr.alloc_id)?.bytes.len() as u64;
237+
let real_align = self.get(ptr.alloc_id)?.align;
238+
if size != old_size || real_align != align {
239+
return Err(EvalError::IncorrectAllocationInformation);
240+
}
237241

238242
if new_size > size {
239243
let amount = new_size - size;
240244
self.memory_usage += amount;
241245
let alloc = self.get_mut(ptr.alloc_id)?;
242-
// FIXME: check alignment here
243246
assert_eq!(amount as usize as u64, amount);
244247
alloc.bytes.extend(iter::repeat(0).take(amount as usize));
245248
alloc.undef_mask.grow(amount, false);
246249
} else if size > new_size {
247250
self.memory_usage -= size - new_size;
248251
self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?;
249252
let alloc = self.get_mut(ptr.alloc_id)?;
250-
// FIXME: check alignment here
251253
// `as usize` is fine here, since it is smaller than `size`, which came from a usize
252254
alloc.bytes.truncate(new_size as usize);
253255
alloc.bytes.shrink_to_fit();
254256
alloc.undef_mask.truncate(new_size);
255257
}
256258

257-
Ok(Pointer::new(ptr.alloc_id, 0))
259+
// Change allocation ID. We do this after the above to be able to re-use methods like `clear_relocations`.
260+
let id = {
261+
let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("We already used this pointer above");
262+
let id = self.next_id;
263+
self.next_id.0 += 1;
264+
self.alloc_map.insert(id, alloc);
265+
id
266+
};
267+
268+
Ok(Pointer::new(id, 0))
258269
}
259270

260271
// TODO(solson): See comment on `reallocate`.
261-
pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> {
262-
if ptr.offset != 0 {
272+
pub fn deallocate(&mut self, ptr: Pointer, size_and_align: Option<(u64, u64)>) -> EvalResult<'tcx> {
273+
if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() {
263274
// TODO(solson): Report error about non-__rust_allocate'd pointer.
264-
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
265-
}
266-
if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) {
267-
return Err(EvalError::DeallocatedStaticMemory);
275+
return Err(EvalError::DeallocateNonBasePtr);
268276
}
269277

270-
if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) {
271-
self.memory_usage -= alloc.bytes.len() as u64;
272-
} else {
273-
debug!("deallocated a pointer twice: {}", ptr.alloc_id);
274-
// TODO(solson): Report error about erroneous free. This is blocked on properly tracking
275-
// already-dropped state since this if-statement is entered even in safe code without
276-
// it.
278+
{
279+
// deallocate_local in eval_context.rs relies on nothing actually having changed when this error occurs.
280+
// So we do this test in advance.
281+
let alloc = self.get(ptr.alloc_id)?;
282+
if alloc.static_kind != StaticKind::NotStatic {
283+
return Err(EvalError::DeallocatedStaticMemory);
284+
}
285+
if let Some((size, align)) = size_and_align {
286+
if size != alloc.bytes.len() as u64 || align != alloc.align {
287+
return Err(EvalError::IncorrectAllocationInformation);
288+
}
289+
}
277290
}
291+
292+
let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("already verified");
293+
self.memory_usage -= alloc.bytes.len() as u64;
278294
debug!("deallocated : {}", ptr.alloc_id);
279295

280296
Ok(())

src/terminator/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
589589
"free" => {
590590
let ptr = args[0].read_ptr(&self.memory)?;
591591
if !ptr.is_null()? {
592-
self.memory.deallocate(ptr.to_ptr()?)?;
592+
self.memory.deallocate(ptr.to_ptr()?, None)?;
593593
}
594594
}
595595

@@ -638,7 +638,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
638638

639639
"__rust_deallocate" => {
640640
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
641-
// FIXME: insert sanity check for size and align?
642641
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
643642
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
644643
if old_size == 0 {
@@ -647,20 +646,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
647646
if !align.is_power_of_two() {
648647
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
649648
}
650-
self.memory.deallocate(ptr)?;
649+
self.memory.deallocate(ptr, Some((old_size, align)))?;
651650
},
652651

653652
"__rust_reallocate" => {
654653
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
654+
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
655655
let size = self.value_to_primval(args[2], usize)?.to_u64()?;
656656
let align = self.value_to_primval(args[3], usize)?.to_u64()?;
657-
if size == 0 {
657+
if old_size == 0 || size == 0 {
658658
return Err(EvalError::HeapAllocZeroBytes);
659659
}
660660
if !align.is_power_of_two() {
661661
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
662662
}
663-
let new_ptr = self.memory.reallocate(ptr, size, align)?;
663+
let new_ptr = self.memory.reallocate(ptr, old_size, size, align)?;
664664
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
665665
}
666666

@@ -768,7 +768,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
768768
}
769769
if let Some(old) = success {
770770
if let Some(var) = old {
771-
self.memory.deallocate(var)?;
771+
self.memory.deallocate(var, None)?;
772772
}
773773
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
774774
} else {
@@ -795,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
795795
self.memory.write_bytes(PrimVal::Ptr(value_copy), &value)?;
796796
self.memory.write_bytes(PrimVal::Ptr(value_copy.offset(value.len() as u64, self.memory.layout)?), &[0])?;
797797
if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) {
798-
self.memory.deallocate(var)?;
798+
self.memory.deallocate(var, None)?;
799799
}
800800
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
801801
} else {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(alloc, heap_api)]
2+
3+
extern crate alloc;
4+
5+
// error-pattern: tried to deallocate or reallocate using incorrect alignment or size
6+
7+
use alloc::heap::*;
8+
fn main() {
9+
unsafe {
10+
let x = allocate(1, 1);
11+
deallocate(x, 1, 2);
12+
}
13+
}

0 commit comments

Comments
 (0)