From 02b22323f129446c9e2255d0eeab6c7ab17aac52 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 3 Nov 2018 13:03:05 +0100 Subject: [PATCH] Make sure the initialization of constrained int range newtypes is unsafe --- src/libcore/lib.rs | 1 + src/libcore/nonzero.rs | 10 +++- src/libcore/num/mod.rs | 4 +- src/libcore/ptr.rs | 14 +++--- src/librustc_mir/transform/check_unsafety.rs | 16 ++++++- .../min_const_unsafe_fn_libstd_stability.rs | 47 +++++++++++++++++++ ...in_const_unsafe_fn_libstd_stability.stderr | 26 ++++++++++ .../min_const_unsafe_fn_libstd_stability2.rs | 43 +++++++++++++++++ ...n_const_unsafe_fn_libstd_stability2.stderr | 20 ++++++++ 9 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs create mode 100644 src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr create mode 100644 src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs create mode 100644 src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 726e891df0c..d070160609d 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -93,6 +93,7 @@ #![feature(never_type)] #![feature(nll)] #![feature(exhaustive_patterns)] +#![cfg_attr(not(stage0), feature(min_const_unsafe_fn))] #![feature(no_core)] #![feature(on_unimplemented)] #![feature(optin_builtin_traits)] diff --git a/src/libcore/nonzero.rs b/src/libcore/nonzero.rs index 436cd1fc057..22d93a5301e 100644 --- a/src/libcore/nonzero.rs +++ b/src/libcore/nonzero.rs @@ -15,10 +15,18 @@ /// A wrapper type for raw pointers and integers that will never be /// NULL or 0 that might allow certain optimizations. #[rustc_layout_scalar_valid_range_start(1)] -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub(crate) struct NonZero(pub(crate) T); +// Do not call `T::clone` as theoretically it could turn the field into `0` +// invalidating `NonZero`'s invariant. +impl Clone for NonZero { + fn clone(&self) -> Self { + unsafe { NonZero(self.0) } + } +} + impl, U> CoerceUnsized> for NonZero {} impl, U> DispatchFromDyn> for NonZero {} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 805be431328..7f5d596b220 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -70,7 +70,7 @@ impl $Ty { #[stable(feature = "nonzero", since = "1.28.0")] #[inline] pub const unsafe fn new_unchecked(n: $Int) -> Self { - $Ty(NonZero(n)) + $Ty(unsafe { NonZero(n) }) } /// Create a non-zero if the given value is not zero. @@ -78,7 +78,7 @@ impl $Ty { #[inline] pub fn new(n: $Int) -> Option { if n != 0 { - Some($Ty(NonZero(n))) + Some($Ty(unsafe { NonZero(n) })) } else { None } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index d3a74ed2a68..a07c7260f71 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2759,7 +2759,7 @@ impl Unique { /// Creates a new `Unique` if `ptr` is non-null. pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { - Some(Unique { pointer: NonZero(ptr as _), _marker: PhantomData }) + Some(Unique { pointer: unsafe { NonZero(ptr as _) }, _marker: PhantomData }) } else { None } @@ -2815,14 +2815,14 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a mut T> for Unique { fn from(reference: &'a mut T) -> Self { - Unique { pointer: NonZero(reference as _), _marker: PhantomData } + Unique { pointer: unsafe { NonZero(reference as _) }, _marker: PhantomData } } } #[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a T> for Unique { fn from(reference: &'a T) -> Self { - Unique { pointer: NonZero(reference as _), _marker: PhantomData } + Unique { pointer: unsafe { NonZero(reference as _) }, _marker: PhantomData } } } @@ -2895,7 +2895,7 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[inline] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - NonNull { pointer: NonZero(ptr as _) } + NonNull { pointer: unsafe { NonZero(ptr as _) } } } /// Creates a new `NonNull` if `ptr` is non-null. @@ -2903,7 +2903,7 @@ impl NonNull { #[inline] pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { - Some(NonNull { pointer: NonZero(ptr as _) }) + Some(NonNull { pointer: unsafe { NonZero(ptr as _) } }) } else { None } @@ -3025,7 +3025,7 @@ fn from(unique: Unique) -> Self { impl<'a, T: ?Sized> From<&'a mut T> for NonNull { #[inline] fn from(reference: &'a mut T) -> Self { - NonNull { pointer: NonZero(reference as _) } + NonNull { pointer: unsafe { NonZero(reference as _) } } } } @@ -3033,6 +3033,6 @@ fn from(reference: &'a mut T) -> Self { impl<'a, T: ?Sized> From<&'a T> for NonNull { #[inline] fn from(reference: &'a T) -> Self { - NonNull { pointer: NonZero(reference as _) } + NonNull { pointer: unsafe { NonZero(reference as _) } } } } diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 0547e4476cb..2c80e573749 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -24,6 +24,8 @@ use syntax::ast; use syntax::symbol::Symbol; +use std::ops::Bound; + use util; pub struct UnsafetyChecker<'a, 'tcx: 'a> { @@ -136,8 +138,18 @@ fn visit_rvalue(&mut self, if let &Rvalue::Aggregate(box ref aggregate, _) = rvalue { match aggregate { &AggregateKind::Array(..) | - &AggregateKind::Tuple | - &AggregateKind::Adt(..) => {} + &AggregateKind::Tuple => {} + &AggregateKind::Adt(ref def, ..) => { + match self.tcx.layout_scalar_valid_range(def.did) { + (Bound::Unbounded, Bound::Unbounded) => {}, + _ => self.require_unsafe( + "initializing type with `rustc_layout_scalar_valid_range` attr", + "initializing `NonZero` with a `0` violates layout constraints \ + and is undefined behavior", + UnsafetyViolationKind::MinConstFn, + ), + } + } &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => { let UnsafetyCheckResult { diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs new file mode 100644 index 00000000000..f559c23ff0f --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs @@ -0,0 +1,47 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "humans", + reason = "who ever let humans program computers, + we're apparently really bad at it", + issue = "0")] + +#![feature(rustc_const_unstable, const_fn, foo, foo2)] +#![feature(min_const_unsafe_fn)] +#![feature(staged_api)] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature="foo")] +const unsafe fn foo() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar() -> u32 { unsafe { foo() } } //~ ERROR can only call other `min_const_fn` + +#[unstable(feature = "rust1", issue="0")] +const unsafe fn foo2() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2() -> u32 { unsafe { foo2() } } //~ ERROR can only call other `min_const_fn` + +#[stable(feature = "rust1", since = "1.0.0")] +// conformity is required, even with `const_fn` feature gate +const unsafe fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` op + +// check whether this function cannot be called even with the feature gate active +#[unstable(feature = "foo2", issue="0")] +const unsafe fn foo2_gated() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } //~ ERROR can only call other + +fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr new file mode 100644 index 00000000000..37be2889173 --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -0,0 +1,26 @@ +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:26:41 + | +LL | const unsafe fn bar() -> u32 { unsafe { foo() } } //~ ERROR can only call other `min_const_fn` + | ^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:33:42 + | +LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } //~ ERROR can only call other `min_const_fn` + | ^^^^^^ + +error: only int, `bool` and `char` operations are stable in const fn + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:37:33 + | +LL | const unsafe fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` op + | ^^^^^^^^^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:45:48 + | +LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } //~ ERROR can only call other + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs new file mode 100644 index 00000000000..131bc97c85a --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs @@ -0,0 +1,43 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "humans", + reason = "who ever let humans program computers, + we're apparently really bad at it", + issue = "0")] + +#![feature(rustc_const_unstable, const_fn, foo, foo2)] +#![feature(min_const_unsafe_fn)] +#![feature(staged_api)] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature="foo")] +const fn foo() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn` + +#[unstable(feature = "rust1", issue="0")] +const fn foo2() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn` + +// check whether this function cannot be called even with the feature gate active +#[unstable(feature = "foo2", issue="0")] +const fn foo2_gated() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2_gated() -> u32 { foo2_gated() } //~ ERROR can only call other `min_const_fn` + +fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr new file mode 100644 index 00000000000..0b58dc1294b --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -0,0 +1,20 @@ +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:26:32 + | +LL | const unsafe fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn` + | ^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:33:33 + | +LL | const unsafe fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn` + | ^^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:41:39 + | +LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } //~ ERROR can only call other `min_const_fn` + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + -- 2.44.0