Skip to content

Commit 220dd42

Browse files
committed
Resolve UB in abs_diff with signed values
Subtracting a large positive signed value from a large negative signed value can't be done in a signed value, as it will overflow.
1 parent 40d1ad5 commit 220dd42

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed

subspace/num/__private/intrinsics.h

+8
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,14 @@ sus_always_inline constexpr OverflowOut<T> sub_with_overflow_unsigned(
674674
};
675675
}
676676

677+
/// SAFETY: Requires that `x > y` so the result will be positive.
678+
template <class T>
679+
requires(std::is_integral_v<T> && std::is_signed_v<T>)
680+
sus_always_inline constexpr std::make_unsigned_t<T>
681+
sub_with_unsigned_positive_result(T x, T y) noexcept {
682+
return unchecked_sub(into_unsigned(x), into_unsigned(y));
683+
}
684+
677685
template <class T>
678686
requires(std::is_integral_v<T> && !std::is_signed_v<T> &&
679687
::sus::mem::size_of<T>() <= 4)

subspace/num/__private/signed_integer_macros.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -666,12 +666,13 @@ class Tuple;
666666
* panics by returning an unsigned integer. \
667667
*/ \
668668
constexpr UnsignedT abs_diff(const T& r) const& noexcept { \
669-
if (primitive_value >= r.primitive_value) \
670-
return __private::into_unsigned( \
671-
__private::unchecked_sub(primitive_value, r.primitive_value)); \
672-
else \
673-
return __private::into_unsigned( \
674-
__private::unchecked_sub(r.primitive_value, primitive_value)); \
669+
if (primitive_value >= r.primitive_value) { \
670+
return __private::sub_with_unsigned_positive_result(primitive_value, \
671+
r.primitive_value); \
672+
} else { \
673+
return __private::sub_with_unsigned_positive_result(r.primitive_value, \
674+
primitive_value); \
675+
} \
675676
} \
676677
static_assert(true)
677678

0 commit comments

Comments
 (0)