better document const-pattern dynamic soundness checks, and fix a soundness hole

This commit is contained in:
Ralf Jung 2020-04-28 23:48:22 +02:00
parent db98d32ea0
commit e8ffa2182b
3 changed files with 24 additions and 9 deletions

View File

@ -193,7 +193,7 @@ fn validate_and_turn_into_const<'tcx>(
mplace.into(), mplace.into(),
path, path,
&mut ref_tracking, &mut ref_tracking,
/*may_ref_to_static*/ is_static, /*may_ref_to_static*/ ecx.memory.extra.can_access_statics,
)?; )?;
} }
} }

View File

@ -99,7 +99,12 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct MemoryExtra { pub struct MemoryExtra {
/// Whether this machine may read from statics /// We need to make sure consts never point to anything mutable, even recursively. That is
/// relied on for pattern matching on consts with references.
/// To achieve this, two pieces have to work together:
/// * Interning makes everything outside of statics immutable.
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
/// This boolean here controls the second part.
pub(super) can_access_statics: bool, pub(super) can_access_statics: bool,
} }
@ -337,6 +342,8 @@ fn before_access_global(
} else if static_def_id.is_some() { } else if static_def_id.is_some() {
// Machine configuration does not allow us to read statics // Machine configuration does not allow us to read statics
// (e.g., `const` initializer). // (e.g., `const` initializer).
// See const_eval::machine::MemoryExtra::can_access_statics for why
// this check is so important.
Err(ConstEvalErrKind::ConstAccessesStatic.into()) Err(ConstEvalErrKind::ConstAccessesStatic.into())
} else { } else {
// Immutable global, this read is fine. // Immutable global, this read is fine.

View File

@ -404,19 +404,27 @@ fn check_safe_pointer(
// Skip validation entirely for some external statics // Skip validation entirely for some external statics
let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id); let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
if let Some(GlobalAlloc::Static(did)) = alloc_kind { if let Some(GlobalAlloc::Static(did)) = alloc_kind {
// `extern static` cannot be validated as they have no body. // See const_eval::machine::MemoryExtra::can_access_statics for why
// FIXME: Statics from other crates are also skipped. // this check is so important.
// They might be checked at a different type, but for now we // This check is reachable when the const just referenced the static,
// want to avoid recursing too deeply. This is not sound! // but never read it (so we never entered `before_access_global`).
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) { // We also need to do it here instead of going on to avoid running
return Ok(()); // into the `before_access_global` check during validation.
}
if !self.may_ref_to_static && self.ecx.tcx.is_static(did) { if !self.may_ref_to_static && self.ecx.tcx.is_static(did) {
throw_validation_failure!( throw_validation_failure!(
format_args!("a {} pointing to a static variable", kind), format_args!("a {} pointing to a static variable", kind),
self.path self.path
); );
} }
// `extern static` cannot be validated as they have no body.
// FIXME: Statics from other crates are also skipped.
// They might be checked at a different type, but for now we
// want to avoid recursing too deeply. We might miss const-invalid data,
// but things are still sound otherwise (in particular re: consts
// referring to statics).
if !did.is_local() || self.ecx.tcx.is_foreign_item(did) {
return Ok(());
}
} }
} }
// Proceed recursively even for ZST, no reason to skip them! // Proceed recursively even for ZST, no reason to skip them!