Skip to content

Commit daf05aa

Browse files
authored
Fix a soundness hole allowing multiple mutable borrows (#124)
* fix a soundness hole allowing multiple mutable borrows * bump min version to 1.24.1 * remove static mut from macros * leave a debug_assert in the supposedly unreachable block * remove redundant unsafe block * add a FIXME to replace Option with MaybeInitialized
1 parent ff1eff5 commit daf05aa

File tree

4 files changed

+39
-36
lines changed

4 files changed

+39
-36
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: rust
22
matrix:
33
include:
4-
- rust: 1.21.0
4+
- rust: 1.24.1
55
- rust: stable
66
- os: osx
77
- rust: beta

src/heap_lazy.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,27 @@
88
extern crate std;
99

1010
use self::std::prelude::v1::*;
11+
use self::std::cell::Cell;
1112
use self::std::sync::Once;
1213
pub use self::std::sync::ONCE_INIT;
1314

14-
pub struct Lazy<T: Sync>(*const T, Once);
15+
pub struct Lazy<T: Sync>(Cell<*const T>, Once);
1516

1617
impl<T: Sync> Lazy<T> {
17-
pub const INIT: Self = Lazy(0 as *const T, ONCE_INIT);
18+
pub const INIT: Self = Lazy(Cell::new(0 as *const T), ONCE_INIT);
1819

1920
#[inline(always)]
20-
pub fn get<F>(&'static mut self, f: F) -> &T
21+
pub fn get<F>(&'static self, f: F) -> &T
2122
where
2223
F: FnOnce() -> T,
2324
{
24-
unsafe {
25-
let r = &mut self.0;
26-
self.1.call_once(|| {
27-
*r = Box::into_raw(Box::new(f()));
28-
});
29-
30-
&*self.0
31-
}
25+
self.1.call_once(|| {
26+
self.0.set(Box::into_raw(Box::new(f())));
27+
});
28+
29+
// `self.0` is guaranteed to have a value by this point
30+
// The `Once` will catch and propegate panics
31+
unsafe { &*self.0.get() }
3232
}
3333
}
3434

@@ -38,6 +38,6 @@ unsafe impl<T: Sync> Sync for Lazy<T> {}
3838
#[doc(hidden)]
3939
macro_rules! __lazy_static_create {
4040
($NAME:ident, $T:ty) => {
41-
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
41+
static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
4242
};
4343
}

src/inline_lazy.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,35 @@ extern crate core;
99
extern crate std;
1010

1111
use self::std::prelude::v1::*;
12+
use self::std::cell::Cell;
1213
use self::std::sync::Once;
1314
pub use self::std::sync::ONCE_INIT;
1415

15-
pub struct Lazy<T: Sync>(Option<T>, Once);
16+
// FIXME: Replace Option<T> with MaybeInitialized<T>
17+
pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);
1618

1719
impl<T: Sync> Lazy<T> {
18-
pub const INIT: Self = Lazy(None, ONCE_INIT);
20+
pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT);
1921

2022
#[inline(always)]
21-
pub fn get<F>(&'static mut self, f: F) -> &T
23+
pub fn get<F>(&'static self, f: F) -> &T
2224
where
2325
F: FnOnce() -> T,
2426
{
25-
{
26-
let r = &mut self.0;
27-
self.1.call_once(|| {
28-
*r = Some(f());
29-
});
30-
}
27+
self.1.call_once(|| {
28+
self.0.set(Some(f()));
29+
});
30+
31+
// `self.0` is guaranteed to be `Some` by this point
32+
// The `Once` will catch and propegate panics
3133
unsafe {
32-
match self.0 {
34+
match *self.0.as_ptr() {
3335
Some(ref x) => x,
34-
None => unreachable_unchecked(),
36+
None => {
37+
debug_assert!(false, "attempted to derefence an uninitialized lazy static. This is a bug");
38+
39+
unreachable_unchecked()
40+
},
3541
}
3642
}
3743
}
@@ -43,7 +49,7 @@ unsafe impl<T: Sync> Sync for Lazy<T> {}
4349
#[doc(hidden)]
4450
macro_rules! __lazy_static_create {
4551
($NAME:ident, $T:ty) => {
46-
static mut $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
52+
static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
4753
};
4854
}
4955

src/lib.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,16 @@ macro_rules! __lazy_static_internal {
137137
(@TAIL, $N:ident : $T:ty = $e:expr) => {
138138
impl $crate::__Deref for $N {
139139
type Target = $T;
140-
#[allow(unsafe_code)]
141140
fn deref(&self) -> &$T {
142-
unsafe {
143-
#[inline(always)]
144-
fn __static_ref_initialize() -> $T { $e }
145-
146-
#[inline(always)]
147-
unsafe fn __stability() -> &'static $T {
148-
__lazy_static_create!(LAZY, $T);
149-
LAZY.get(__static_ref_initialize)
150-
}
151-
__stability()
141+
#[inline(always)]
142+
fn __static_ref_initialize() -> $T { $e }
143+
144+
#[inline(always)]
145+
fn __stability() -> &'static $T {
146+
__lazy_static_create!(LAZY, $T);
147+
LAZY.get(__static_ref_initialize)
152148
}
149+
__stability()
153150
}
154151
}
155152
impl $crate::LazyStatic for $N {

0 commit comments

Comments
 (0)