Skip to content

Commit c12c8a7

Browse files
committed
clean up internals of pointer checks; make get_size_and_align also check for fn allocations
1 parent dcc8371 commit c12c8a7

File tree

5 files changed

+69
-104
lines changed

5 files changed

+69
-104
lines changed

src/librustc/mir/interpret/allocation.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,13 @@ use super::{
66

77
use crate::ty::layout::{Size, Align};
88
use syntax::ast::Mutability;
9-
use std::{iter, fmt::{self, Display}};
9+
use std::iter;
1010
use crate::mir;
1111
use std::ops::{Range, Deref, DerefMut};
1212
use rustc_data_structures::sorted_map::SortedMap;
13-
use rustc_macros::HashStable;
1413
use rustc_target::abi::HasDataLayout;
1514
use std::borrow::Cow;
1615

17-
/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
18-
/// or also inbounds of a *live* allocation.
19-
#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
20-
pub enum InboundsCheck {
21-
Live,
22-
MaybeDead,
23-
}
24-
2516
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
2617
pub struct Allocation<Tag=(),Extra=()> {
2718
/// The actual bytes of the allocation.

src/librustc/mir/interpret/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ pub use self::error::{
1717

1818
pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue};
1919

20-
pub use self::allocation::{
21-
InboundsCheck, Allocation, AllocationExtra,
22-
Relocations, UndefMask,
23-
};
20+
pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask};
2421

2522
pub use self::pointer::{Pointer, PointerArithmetic, CheckInAllocMsg};
2623

src/librustc/mir/interpret/pointer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fmt;
1+
use std::fmt::{self, Display};
22

33
use crate::mir;
44
use crate::ty::layout::{self, HasDataLayout, Size};

src/librustc_mir/interpret/memory.rs

Lines changed: 65 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use syntax::ast::Mutability;
1919
use super::{
2020
Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
2121
InterpResult, Scalar, InterpError, GlobalAlloc, PointerArithmetic,
22-
Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg, InboundsCheck,
22+
Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg,
2323
InterpError::ValidationFailure,
2424
};
2525

@@ -44,6 +44,17 @@ impl<T: MayLeak> MayLeak for MemoryKind<T> {
4444
}
4545
}
4646

47+
/// Used by `get_size_and_align` to indicate whether the allocation needs to be live.
48+
#[derive(Debug, Copy, Clone)]
49+
pub enum AllocCheck {
50+
/// Allocation must be live and not a function pointer.
51+
Dereferencable,
52+
/// Allocations needs to be live, but may be a function pointer.
53+
Live,
54+
/// Allocation may be dead.
55+
MaybeDead,
56+
}
57+
4758
// `Memory` has to depend on the `Machine` because some of its operations
4859
// (e.g., `get`) call a `Machine` hook.
4960
pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -248,66 +259,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
248259
Ok(())
249260
}
250261

251-
/// Checks that the pointer is aligned AND non-NULL. This supports ZSTs in two ways:
252-
/// You can pass a scalar, and a `Pointer` does not have to actually still be allocated.
253-
fn check_align(
254-
&self,
255-
ptr: Scalar<M::PointerTag>,
256-
required_align: Align
257-
) -> InterpResult<'tcx> {
258-
// Check non-NULL/Undef, extract offset
259-
let (offset, alloc_align) = match ptr.to_bits_or_ptr(self.pointer_size(), self) {
260-
Err(ptr) => {
261-
// check this is not NULL -- which we can ensure only if this is in-bounds
262-
// of some (potentially dead) allocation.
263-
let align = self.check_ptr_bounds(ptr, InboundsCheck::MaybeDead,
264-
CheckInAllocMsg::NullPointerTest)?;
265-
(ptr.offset.bytes(), align)
266-
}
267-
Ok(data) => {
268-
// check this is not NULL
269-
if data == 0 {
270-
return err!(InvalidNullPointerUsage);
271-
}
272-
// the "base address" is 0 and hence always aligned
273-
(data as u64, required_align)
274-
}
275-
};
276-
// Check alignment
277-
if alloc_align.bytes() < required_align.bytes() {
278-
return err!(AlignmentCheckFailed {
279-
has: alloc_align,
280-
required: required_align,
281-
});
282-
}
283-
if offset % required_align.bytes() == 0 {
284-
Ok(())
285-
} else {
286-
let has = offset % required_align.bytes();
287-
err!(AlignmentCheckFailed {
288-
has: Align::from_bytes(has).unwrap(),
289-
required: required_align,
290-
})
291-
}
292-
}
293-
294-
/// Checks if the pointer is "in-bounds" of *some* (live or dead) allocation. Notice that
295-
/// a pointer pointing at the end of an allocation (i.e., at the first *inaccessible* location)
296-
/// *is* considered in-bounds! This follows C's/LLVM's rules.
297-
/// `liveness` can be used to rule out dead allocations. Testing in-bounds with a dead
298-
/// allocation is useful e.g. to exclude the possibility of this pointer being NULL.
299-
/// If you want to check bounds before doing a memory access, call `check_ptr_access`.
300-
fn check_ptr_bounds(
301-
&self,
302-
ptr: Pointer<M::PointerTag>,
303-
liveness: InboundsCheck,
304-
msg: CheckInAllocMsg,
305-
) -> InterpResult<'tcx, Align> {
306-
let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
307-
ptr.check_in_alloc(allocation_size, msg)?;
308-
Ok(align)
309-
}
310-
311262
/// Check if the given scalar is allowed to do a memory access of given `size`
312263
/// and `align`. On success, returns `None` for zero-sized accesses (where
313264
/// nothing else is left to do) and a `Pointer` to use for the actual access otherwise.
@@ -350,18 +301,35 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
350301
None
351302
}
352303
Err(ptr) => {
353-
// Test bounds.
354-
self.check_ptr_bounds(
355-
ptr.offset(size, self)?,
356-
InboundsCheck::Live,
357-
CheckInAllocMsg::MemoryAccessTest,
358-
)?;
359-
// Test align and non-NULL.
360-
self.check_align(ptr.into(), align)?;
361-
// FIXME: Alignment check is too strict, depending on the base address that
362-
// got picked we might be aligned even if this check fails.
363-
// We instead have to fall back to converting to an integer and checking
364-
// the "real" alignment.
304+
let (allocation_size, alloc_align) =
305+
self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferencable)?;
306+
// Test bounds. This also ensures non-NULL.
307+
// It is sufficient to check this for the end pointer. The addition
308+
// checks for overflow.
309+
let end_ptr = ptr.offset(size, self)?;
310+
end_ptr.check_in_alloc(allocation_size, CheckInAllocMsg::MemoryAccessTest)?;
311+
// Test align. Check this last; if both bounds and alignment are violated
312+
// we want the error to be about the bounds.
313+
if alloc_align.bytes() < align.bytes() {
314+
// The allocation itself is not aligned enough.
315+
// FIXME: Alignment check is too strict, depending on the base address that
316+
// got picked we might be aligned even if this check fails.
317+
// We instead have to fall back to converting to an integer and checking
318+
// the "real" alignment.
319+
return err!(AlignmentCheckFailed {
320+
has: alloc_align,
321+
required: align,
322+
});
323+
}
324+
let offset = ptr.offset.bytes();
325+
if offset % align.bytes() != 0 {
326+
// The offset os not aligned enough.
327+
let has = offset % align.bytes();
328+
return err!(AlignmentCheckFailed {
329+
has: Align::from_bytes(has).unwrap(),
330+
required: align,
331+
})
332+
}
365333

366334
// We can still be zero-sized in this branch, in which case we have to
367335
// return `None`.
@@ -375,8 +343,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
375343
&self,
376344
ptr: Pointer<M::PointerTag>,
377345
) -> bool {
378-
self.check_ptr_bounds(ptr, InboundsCheck::MaybeDead, CheckInAllocMsg::NullPointerTest)
379-
.is_err()
346+
let (size, _align) = self.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)
347+
.expect("alloc info with MaybeDead cannot fail");
348+
ptr.check_in_alloc(size, CheckInAllocMsg::NullPointerTest).is_err()
380349
}
381350
}
382351

@@ -515,13 +484,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
515484
}
516485
}
517486

518-
/// Obtain the size and alignment of an allocation, even if that allocation has been deallocated
487+
/// Obtain the size and alignment of an allocation, even if that allocation has
488+
/// been deallocated.
519489
///
520-
/// If `liveness` is `InboundsCheck::MaybeDead`, this function always returns `Ok`
490+
/// If `liveness` is `AllocCheck::MaybeDead`, this function always returns `Ok`.
521491
pub fn get_size_and_align(
522492
&self,
523493
id: AllocId,
524-
liveness: InboundsCheck,
494+
liveness: AllocCheck,
525495
) -> InterpResult<'static, (Size, Align)> {
526496
if let Ok(alloc) = self.get(id) {
527497
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
@@ -531,7 +501,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
531501
let alloc = self.tcx.alloc_map.lock().get(id);
532502
// Could also be a fn ptr or extern static
533503
match alloc {
534-
Some(GlobalAlloc::Function(..)) => Ok((Size::ZERO, Align::from_bytes(1).unwrap())),
504+
Some(GlobalAlloc::Function(..)) => {
505+
if let AllocCheck::Dereferencable = liveness {
506+
// The caller requested no function pointers.
507+
err!(DerefFunctionPointer)
508+
} else {
509+
Ok((Size::ZERO, Align::from_bytes(1).unwrap()))
510+
}
511+
}
535512
// `self.get` would also work, but can cause cycles if a static refers to itself
536513
Some(GlobalAlloc::Static(did)) => {
537514
// The only way `get` couldn't have worked here is if this is an extern static
@@ -545,15 +522,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
545522
if let Ok(alloc) = self.get(id) {
546523
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
547524
}
548-
match liveness {
549-
InboundsCheck::MaybeDead => {
550-
// Must be a deallocated pointer
551-
self.dead_alloc_map.get(&id).cloned().ok_or_else(||
552-
ValidationFailure("allocation missing in dead_alloc_map".to_string())
553-
.into()
554-
)
555-
},
556-
InboundsCheck::Live => err!(DanglingPointerDeref),
525+
if let AllocCheck::MaybeDead = liveness {
526+
// Deallocated pointers are allowed, we should be able to find
527+
// them in the map.
528+
self.dead_alloc_map.get(&id).copied().ok_or_else(||
529+
ValidationFailure("allocation missing in dead_alloc_map".to_string())
530+
.into()
531+
)
532+
} else {
533+
err!(DanglingPointerDeref)
557534
}
558535
},
559536
}

src/librustc_mir/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub use self::eval_context::{
2424

2525
pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
2626

27-
pub use self::memory::{Memory, MemoryKind};
27+
pub use self::memory::{Memory, MemoryKind, AllocCheck};
2828

2929
pub use self::machine::{Machine, AllocMap, MayLeak};
3030

0 commit comments

Comments
 (0)