Skip to content

Commit f37d70e

Browse files
authored
correct tag clearing in TaggedPtr::as_ptr (#45)
1 parent d6efff6 commit f37d70e

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

oscars/src/alloc/arena2/alloc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl<T> TaggedPtr<T> {
115115
}
116116

117117
fn as_ptr(&self) -> *mut T {
118-
self.0.map_addr(|addr| addr ^ MASK)
118+
self.0.map_addr(|addr| addr & !MASK)
119119
}
120120
}
121121

oscars/src/alloc/arena2/tests.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,34 @@ fn arc_drop() {
8888

8989
assert_eq!(allocator.arenas_len(), 0);
9090
}
91+
92+
// === test for TaggedPtr::as_ptr === //
93+
94+
// `TaggedPtr::as_ptr` must use `addr & !MASK` to unconditionally clear the high
95+
// bit rather than XORing it out. The XOR approach worked for tagged items
96+
// but incorrectly flipped the bit on untagged items, corrupting the pointer.
97+
#[test]
98+
fn as_ptr_clears_not_flips_tag_bit() {
99+
let mut allocator = ArenaAllocator::default();
100+
101+
let mut ptr_a = allocator.try_alloc(1u64).unwrap().as_ptr();
102+
let mut ptr_b = allocator.try_alloc(2u64).unwrap().as_ptr();
103+
let _ptr_c = allocator.try_alloc(3u64).unwrap().as_ptr();
104+
assert_eq!(allocator.arenas_len(), 1);
105+
106+
// Mark B and C as dropped, leave A live.
107+
unsafe {
108+
ptr_b.as_mut().mark_dropped();
109+
}
110+
111+
let states = allocator.arena_drop_states();
112+
assert_eq!(
113+
states[0].as_slice(),
114+
&[false, true, false],
115+
"item_drop_states must correctly report live/dropped status for all nodes"
116+
);
117+
118+
unsafe {
119+
ptr_a.as_mut().mark_dropped();
120+
}
121+
}

0 commit comments

Comments
 (0)