Skip to content

Commit abe6d86

Browse files
committed
Make as_primitive() casts panic if sign changes
1 parent 7fd3ee9 commit abe6d86

File tree

1 file changed

+18
-5
lines changed

1 file changed

+18
-5
lines changed

src/cast/as_primitive.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ where
9090
}
9191

9292
/// This should act like `as`, keeping the value the same, removing any extra
93-
/// bits
93+
/// bits.
94+
///
95+
/// If the sign would change by casting, panics.
9496
pub(crate) const fn as_primitive<T, V>(value: V) -> T
9597
where
9698
V: AsPrimitive<T>,
@@ -127,7 +129,7 @@ where
127129
};
128130

129131
let mut value = as_repr::as_repr(value);
130-
let is_negative = V::SIGNED && unsafe { is_negative(value) };
132+
let negative = unsafe { is_negative(value) };
131133

132134
// Convert to little endian
133135
if cfg!(target_endian = "big") {
@@ -136,7 +138,7 @@ where
136138

137139
let mut primitive: T = unsafe { mem::zeroed() };
138140

139-
if is_negative {
141+
if negative {
140142
primitive = unsafe { not(primitive) };
141143
}
142144

@@ -157,6 +159,11 @@ where
157159
primitive = unsafe { endian_swizzle(primitive) };
158160
}
159161

162+
// Check if sign changed
163+
if unsafe { is_negative(primitive) } ^ negative {
164+
panic!("cannot change sign during cast")
165+
}
166+
160167
primitive
161168
}
162169

@@ -251,17 +258,23 @@ where
251258
///
252259
/// # Safety
253260
///
254-
/// - `T` must be a signed primitive value
261+
/// - `T` must be a primitive value (since `RangeablePrimitive` and `Primitive`
262+
/// are safe traits that could theoretically break this invariant, but
263+
/// shouldn't, this function must be marked unsafe)
255264
const unsafe fn is_negative<T>(input: T) -> bool
256265
where
257-
T: Copy + Clone,
266+
T: Primitive,
258267
{
259268
const {
260269
if !matches!(size_of::<T>(), 1 | 2 | 4 | 8 | 16) {
261270
panic!("invalid size");
262271
}
263272
}
264273

274+
if const { !T::SIGNED } {
275+
return false;
276+
}
277+
265278
match size_of::<T>() {
266279
1 => {
267280
let input: *const T = &input;

0 commit comments

Comments
 (0)