Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1182)

Unified Diff: base/numerics/safe_conversions.h

Issue 2528243002: Fix silent truncations when extracting values from CheckedNumeric (Closed)
Patch Set: compile cleanup and fix Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | components/webcrypto/algorithms/aes_cbc.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/numerics/safe_conversions.h
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 4b9129dfffe94329c954bc1842069e7e5bd1e919..04e4d446230000dd817d90249d9951d8e155423d 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -6,9 +6,11 @@
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
#include <stddef.h>
+#include <stdlib.h>
#include <cassert>
#include <limits>
+#include <ostream>
#include <type_traits>
#include "base/numerics/safe_conversions_impl.h"
@@ -91,7 +93,6 @@ constexpr Dst saturated_cast_impl(const Src value,
? std::numeric_limits<Dst>::max()
: NaNHandler::template HandleFailure<Dst>()));
}
-} // namespace internal
// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate rather than overflow or
@@ -107,6 +108,37 @@ constexpr Dst saturated_cast(Src value) {
value, internal::DstRangeRelationToSrcRange<Dst>(value));
}
+// The following macro attempts to determine if the passed expression is a
+// compile-time constant. GCC and clang have a compiler intrinsic for this,
+// but MSVS needs to use a hack where compile-time expression evaluation will
+// generate a zero constant that will trigger a different function overload
+// than a compile-time variable would.
+#if defined(__GNUC__) || defined(__clang__)
+#define IS_COMPILE_TIME_CONSTANT(V) __builtin_constant_p(V)
+#else
+struct ConstTest {
+ struct PlaceHolder {
+ PlaceHolder(int64_t x) {}
+ };
+ static int8_t Test(void*) { return int8_t(); }
+ static int64_t Test(PlaceHolder) { return int64_t(); }
+};
+
+#define IS_COMPILE_TIME_CONSTANT(V) \
+ (std::is_same<int8_t, decltype(internal::ConstTest::Test((V) - (V)))>::value)
+#endif
+
+// The test goes here because we undef the macro when we're done with it.
+inline void TestCompileTimeConstantSupport() {
+ const int kCompileTimeConstant = 10;
+ int compile_time_variable = rand();
+ static_assert(IS_COMPILE_TIME_CONSTANT(std::numeric_limits<int>::is_signed),
+ "");
+ static_assert(IS_COMPILE_TIME_CONSTANT(kCompileTimeConstant), "");
+ static_assert(!IS_COMPILE_TIME_CONSTANT(compile_time_variable), "");
+ (void)compile_time_variable;
+}
+
// strict_cast<> is analogous to static_cast<> for numeric types, except that
// it will cause a compile failure if the destination type is not large enough
// to contain any value in the source type. It performs no runtime checking.
@@ -116,18 +148,20 @@ constexpr Dst strict_cast(Src value) {
"Argument must be numeric.");
static_assert(std::numeric_limits<Dst>::is_specialized,
"Result must be numeric.");
- static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
- internal::NUMERIC_RANGE_CONTAINED),
- "The numeric conversion is out of range for this type. You "
- "should probably use one of the following conversion "
- "mechanisms on the value you want to pass:\n"
- "- base::checked_cast\n"
- "- base::saturated_cast\n"
- "- base::CheckedNumeric");
+
+ // We try to make compile-time constants just work regardless of type.
+ static_assert(
+ IS_COMPILE_TIME_CONSTANT(value)
+ ? IsValueInRangeForNumericType<Dst>(value)
+ : internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ internal::NUMERIC_RANGE_CONTAINED,
+ "The source type is out of range for the destination type");
return static_cast<Dst>(value);
}
+#undef IS_COMPILE_TIME_CONSTANT
+
// StrictNumeric implements compile time range checking between numeric types by
// wrapping assignment operations in a strict_cast. This class is intended to be
// used for function arguments and return types, to ensure the destination type
@@ -157,15 +191,57 @@ class StrictNumeric {
: value_(strict_cast<T>(value)) {}
// The numeric cast operator basically handles all the magic.
- template <typename Dst>
+ template <typename Dst,
+ typename std::enable_if<
+ ArithmeticOrUnderlyingEnum<Dst>::value>::type* = nullptr>
constexpr operator Dst() const {
- return strict_cast<Dst>(value_);
+ return strict_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
}
private:
const T value_;
};
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+
+// We allow simple pointer arithmetic.
Tom Sepez 2016/11/29 20:06:26 For extra credit: Can we make this blow up if R is
jschuh 2016/11/29 21:04:47 It would be very easy to just call the correspondi
jschuh 2016/11/30 23:38:52 Okay, I decided that wrapping here is always unsaf
+template <typename L, typename R>
+constexpr L* operator+(L* lhs, const StrictNumeric<R>& rhs) {
+ return lhs + static_cast<R>(rhs);
+}
+
+template <typename L, typename R>
+constexpr L* operator-(L* lhs, const StrictNumeric<R>& rhs) {
+ return lhs - static_cast<R>(rhs);
+}
+
+#define STRICT_COMPARISON_OP(NAME, OP) \
+ template <typename L, typename R, \
+ typename std::enable_if< \
+ internal::IsStrictOp<L, R>::value>::type* = nullptr> \
+ constexpr bool operator OP(const L lhs, const R rhs) { \
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>(lhs, rhs); \
+ }
+
+STRICT_COMPARISON_OP(IsLess, <);
+STRICT_COMPARISON_OP(IsLessOrEqual, <=);
+STRICT_COMPARISON_OP(IsGreater, >);
+STRICT_COMPARISON_OP(IsGreaterOrEqual, >=);
+STRICT_COMPARISON_OP(IsEqual, ==);
+STRICT_COMPARISON_OP(IsNotEqual, !=);
+
+#undef STRICT_COMPARISON_OP
+};
+
+using internal::strict_cast;
+using internal::saturated_cast;
+using internal::StrictNumeric;
+
// Explicitly make a shorter size_t typedef for convenience.
typedef StrictNumeric<size_t> SizeT;
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | components/webcrypto/algorithms/aes_cbc.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698