Skip to content

Commit 34b809f

Browse files
committed
Fix soundness hole around access::Map
Backport to the 0.4 version, releasing as 0.4.8. The assumption that the address of access's guarded reference stays the same is not true. Costs: * The Map is now slower and adds an allocation. * It can stop being Copy (but non-trivial guards weren't anyway) and it can stop being Sync/Send if the closure is not. * The taken closure needs to be Clone. Fixes #45. Technically, it is a breaking change, but the plan is not to raise major version, because: * Even rust std gives exception to break compatibility for soundness hole fixes. * It is not that likely people's code would break. * Even if it breaks, they are much more likely to go to the fixed version then if the version got bumped and that's what they should be doing ASAP due to the potential UB.
1 parent 77b5be7 commit 34b809f

File tree

4 files changed

+26
-38
lines changed

4 files changed

+26
-38
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 0.4.8
2+
3+
* Backport of fix to soundness issue in #45 (access::Map from Constant can lead
4+
to dangling references).
5+
16
# 0.4.7
27

38
* Rename the `unstable-weak` to `weak` feature. The support is now available on

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "arc-swap"
3-
version = "0.4.7"
3+
version = "0.4.8"
44
authors = ["Michal 'vorner' Vaner <[email protected]>"]
55
description = "Atomically swappable Arc"
66
documentation = "https://docs.rs/arc-swap"

src/access.rs

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![deny(unsafe_code)]
2+
13
//! Abstracting over accessing parts of stored value.
24
//!
35
//! Sometimes, there's a big globalish data structure (like a configuration for the whole program).
@@ -216,39 +218,20 @@ where
216218
/// This is the guard type for [`Map`]. It is accessible and nameable, but is not expected to be
217219
/// generally used directly.
218220
#[derive(Copy, Clone, Debug)]
219-
pub struct MapGuard<G, T> {
220-
_guard: G,
221-
value: *const T,
222-
}
223-
224-
// Why these are safe:
225-
// * The *const T is actually used just as a &const T with 'self lifetime (which can't be done in
226-
// Rust). So if the reference is Send/Sync, so is the raw pointer.
227-
unsafe impl<G, T> Send for MapGuard<G, T>
228-
where
229-
G: Send,
230-
for<'a> &'a T: Send,
231-
{
221+
pub struct MapGuard<G, F, T, R> {
222+
guard: G,
223+
projection: F,
224+
_t: PhantomData<fn(&T) -> &R>,
232225
}
233226

234-
unsafe impl<G, T> Sync for MapGuard<G, T>
227+
impl<G, F, T, R> Deref for MapGuard<G, F, T, R>
235228
where
236-
G: Sync,
237-
for<'a> &'a T: Sync,
229+
G: Deref<Target = T>,
230+
F: Fn(&T) -> &R,
238231
{
239-
}
240-
241-
impl<G, T> Deref for MapGuard<G, T> {
242-
type Target = T;
243-
fn deref(&self) -> &T {
244-
// Why this is safe:
245-
// * The pointer is originally converted from a reference. It's not null, it's aligned,
246-
// it's the right type, etc.
247-
// * The pointee couldn't have gone away ‒ the guard keeps the original reference alive, so
248-
// must the new one still be alive too. Moving the guard is fine, we assume the RefCnt is
249-
// Pin (because it's Arc or Rc or something like that ‒ when that one moves, the data it
250-
// points to stay at the same place).
251-
unsafe { &*self.value }
232+
type Target = R;
233+
fn deref(&self) -> &R {
234+
(self.projection)(&self.guard)
252235
}
253236
}
254237

@@ -277,7 +260,7 @@ impl<A, T, F> Map<A, T, F> {
277260
/// *cheap* (like only taking reference).
278261
pub fn new<R>(access: A, projection: F) -> Self
279262
where
280-
F: Fn(&T) -> &R,
263+
F: Fn(&T) -> &R + Clone,
281264
{
282265
Map {
283266
access,
@@ -287,18 +270,18 @@ impl<A, T, F> Map<A, T, F> {
287270
}
288271
}
289272

290-
impl<A, T, F, R> Access<R> for Map<A, T, F>
273+
impl<A, F, T, R> Access<R> for Map<A, T, F>
291274
where
292275
A: Access<T>,
293-
F: Fn(&T) -> &R,
276+
F: Fn(&T) -> &R + Clone,
294277
{
295-
type Guard = MapGuard<A::Guard, R>;
278+
type Guard = MapGuard<A::Guard, F, T, R>;
296279
fn load(&self) -> Self::Guard {
297280
let guard = self.access.load();
298-
let value: *const _ = (self.projection)(&guard);
299281
MapGuard {
300-
_guard: guard,
301-
value,
282+
guard,
283+
projection: self.projection.clone(),
284+
_t: PhantomData,
302285
}
303286
}
304287
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ impl<T: RefCnt, S: LockStorage> ArcSwapAny<T, S> {
12171217
/// ```
12181218
pub fn map<I, R, F>(&self, f: F) -> Map<&Self, I, F>
12191219
where
1220-
F: Fn(&I) -> &R,
1220+
F: Fn(&I) -> &R + Clone,
12211221
Self: Access<I>,
12221222
{
12231223
Map::new(self, f)

0 commit comments

Comments
 (0)