Skip to content

Commit 65c1d42

Browse files
committed
Ensure that String pointers are aligned before converting into BoxedString.
1 parent 7e3a4fc commit 65c1d42

File tree

3 files changed

+18
-6
lines changed

3 files changed

+18
-6
lines changed

src/boxed.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ pub(crate) struct BoxedString {
2727
ptr: NonNull<u8>,
2828
}
2929

30+
/// Checks if a pointer is aligned to an even address (good)
31+
/// or an odd address (either actually an InlineString or very, very bad).
32+
///
33+
/// Returns `true` if aligned to an odd address, `false` if even. The sense of
34+
/// the boolean is "does this look like an InlineString? true/false"
35+
fn check_alignment(ptr: *const u8) -> bool {
36+
ptr.align_offset(2) > 0
37+
}
38+
3039
impl GenericString for BoxedString {
3140
fn set_size(&mut self, size: usize) {
3241
self.len = size;
@@ -44,9 +53,8 @@ impl GenericString for BoxedString {
4453
impl BoxedString {
4554
const MINIMAL_CAPACITY: usize = MAX_INLINE * 2;
4655

47-
pub(crate) fn check_alignment(this: &Self) -> usize {
48-
let ptr: *const u8 = this.ptr.as_ptr();
49-
ptr.align_offset(2)
56+
pub(crate) fn check_alignment(this: &Self) -> bool {
57+
check_alignment(this.ptr.as_ptr())
5058
}
5159

5260
fn layout_for(cap: usize) -> Layout {
@@ -163,6 +171,10 @@ impl From<String> for BoxedString {
163171
if s.is_empty() {
164172
Self::new(s.capacity())
165173
} else {
174+
// If the `String`'s buffer isn't word aligned, we can't reuse it.
175+
if check_alignment(s.as_ptr()) {
176+
return Self::from_str(s.capacity(), &s);
177+
}
166178
// TODO: Use String::into_raw_parts when stabilised, meanwhile let's get unsafe
167179
let len = s.len();
168180
let cap = s.capacity();

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ impl<Mode: SmartStringMode> SmartString<Mode> {
300300
let str_ptr: *const BoxedString =
301301
self.data.as_ptr().cast() as *const _ as *const BoxedString;
302302
#[allow(unsafe_code)]
303-
Discriminant::from_bit(BoxedString::check_alignment(unsafe { &*str_ptr }) == 1)
303+
Discriminant::from_bit(BoxedString::check_alignment(unsafe { &*str_ptr }))
304304
}
305305

306306
fn cast(&self) -> StringCast<'_> {

src/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,11 +535,11 @@ mod tests {
535535
let boxed_ptr: *const BoxedString = inline_ptr.cast();
536536
#[allow(unsafe_code)]
537537
let discriminant =
538-
Discriminant::from_bit(BoxedString::check_alignment(unsafe { &*boxed_ptr }) == 1);
538+
Discriminant::from_bit(BoxedString::check_alignment(unsafe { &*boxed_ptr }));
539539
assert_eq!(Discriminant::Inline, discriminant);
540540

541541
let boxed = BoxedString::from_str(32, "welp");
542-
let discriminant = Discriminant::from_bit(BoxedString::check_alignment(&boxed) == 1);
542+
let discriminant = Discriminant::from_bit(BoxedString::check_alignment(&boxed));
543543
assert_eq!(Discriminant::Boxed, discriminant);
544544

545545
let mut s = SmartString::<Compact>::new();

0 commit comments

Comments
 (0)