Skip to content

Commit 36bb583

Browse files
committed
Bug 849666 - Make CheckedInt<T>::operator-() not depend on undefined behavior when negating minimum signed values, and add a test for this. Patch is something of a tag-team effort by bjacob and me. r=bjacob
1 parent 6464ed2 commit 36bb583

File tree

2 files changed

+31
-20
lines changed

2 files changed

+31
-20
lines changed

mfbt/CheckedInt.h

+26-20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
namespace mozilla {
4242

43+
template<typename T> class CheckedInt;
44+
4345
namespace detail {
4446

4547
/*
@@ -454,23 +456,32 @@ IsDivValid(T x, T y)
454456
!(IsSigned<T>::value && x == MinValue<T>::value && y == T(-1));
455457
}
456458

457-
// This is just to shut up msvc warnings about negating unsigned ints.
458-
template<typename T, bool IsTSigned = IsSigned<T>::value>
459-
struct OppositeIfSignedImpl
460-
{
461-
static T run(T x) { return -x; }
462-
};
459+
template<typename T, bool IsSigned = IsSigned<T>::value>
460+
struct NegateImpl;
461+
463462
template<typename T>
464-
struct OppositeIfSignedImpl<T, false>
463+
struct NegateImpl<T, false>
465464
{
466-
static T run(T x) { return x; }
465+
static CheckedInt<T> negate(const CheckedInt<T>& val)
466+
{
467+
// Handle negation separately for signed/unsigned, for simpler code and to
468+
// avoid an MSVC warning negating an unsigned value.
469+
return CheckedInt<T>(0, val.isValid() && val.mValue == 0);
470+
}
467471
};
472+
468473
template<typename T>
469-
inline T
470-
OppositeIfSigned(T x)
474+
struct NegateImpl<T, true>
471475
{
472-
return OppositeIfSignedImpl<T>::run(x);
473-
}
476+
static CheckedInt<T> negate(const CheckedInt<T>& val)
477+
{
478+
// Watch out for the min-value, which (with twos-complement) can't be
479+
// negated as -min-value is then (max-value + 1).
480+
if (!val.isValid() || val.mValue == MinValue<T>::value)
481+
return CheckedInt<T>(val.mValue, false);
482+
return CheckedInt<T>(-val.mValue, true);
483+
}
484+
};
474485

475486
} // namespace detail
476487

@@ -560,6 +571,8 @@ class CheckedInt
560571
"This type is not supported by CheckedInt");
561572
}
562573

574+
friend class detail::NegateImpl<T>;
575+
563576
public:
564577
/**
565578
* Constructs a checked integer with given @a value. The checked integer is
@@ -628,14 +641,7 @@ class CheckedInt
628641

629642
CheckedInt operator -() const
630643
{
631-
// Circumvent msvc warning about - applied to unsigned int.
632-
// if we're unsigned, the only valid case anyway is 0
633-
// in which case - is a no-op.
634-
T result = detail::OppositeIfSigned(mValue);
635-
/* Help the compiler perform RVO (return value optimization). */
636-
return CheckedInt(result,
637-
mIsValid && detail::IsSubValid(T(0),
638-
mValue));
644+
return detail::NegateImpl<T>::negate(*this);
639645
}
640646

641647
/**

mfbt/tests/TestCheckedInt.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ void test()
197197

198198
if (isTSigned) {
199199
VERIFY_IS_VALID(-max);
200+
VERIFY_IS_INVALID(-min);
201+
VERIFY(-max - min == one);
200202
VERIFY_IS_VALID(-max - one);
201203
VERIFY_IS_VALID(negOne);
202204
VERIFY_IS_VALID(-max + negOne);
@@ -206,6 +208,9 @@ void test()
206208
VERIFY_IS_VALID(negOne + negOne);
207209
VERIFY(negOne + negOne == negTwo);
208210
} else {
211+
VERIFY_IS_INVALID(-max);
212+
VERIFY_IS_VALID(-min);
213+
VERIFY(min == zero);
209214
VERIFY_IS_INVALID(negOne);
210215
}
211216

0 commit comments

Comments
 (0)