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

Side by Side Diff: base/numerics/safe_math_impl.h

Issue 2528243002: Fix silent truncations when extracting values from CheckedNumeric (Closed)
Patch Set: compile fix Created 4 years 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 unified diff | Download patch
« no previous file with comments | « base/numerics/safe_math.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
11 #include <climits> 11 #include <climits>
12 #include <cmath> 12 #include <cmath>
13 #include <cstdlib> 13 #include <cstdlib>
14 #include <limits> 14 #include <limits>
15 #include <type_traits> 15 #include <type_traits>
16 16
17 #include "base/numerics/safe_conversions.h" 17 #include "base/numerics/safe_conversions.h"
18 18
19 namespace base { 19 namespace base {
20 namespace internal { 20 namespace internal {
21 21
22 // Everything from here up to the floating point operations is portable C++, 22 // Everything from here up to the floating point operations is portable C++,
23 // but it may not be fast. This code could be split based on 23 // but it may not be fast. This code could be split based on
24 // platform/architecture and replaced with potentially faster implementations. 24 // platform/architecture and replaced with potentially faster implementations.
25 25
26 // Integer promotion templates used by the portable checked integer arithmetic.
27 template <size_t Size, bool IsSigned>
28 struct IntegerForSizeAndSign;
29 template <>
30 struct IntegerForSizeAndSign<1, true> {
31 typedef int8_t type;
32 };
33 template <>
34 struct IntegerForSizeAndSign<1, false> {
35 typedef uint8_t type;
36 };
37 template <>
38 struct IntegerForSizeAndSign<2, true> {
39 typedef int16_t type;
40 };
41 template <>
42 struct IntegerForSizeAndSign<2, false> {
43 typedef uint16_t type;
44 };
45 template <>
46 struct IntegerForSizeAndSign<4, true> {
47 typedef int32_t type;
48 };
49 template <>
50 struct IntegerForSizeAndSign<4, false> {
51 typedef uint32_t type;
52 };
53 template <>
54 struct IntegerForSizeAndSign<8, true> {
55 typedef int64_t type;
56 };
57 template <>
58 struct IntegerForSizeAndSign<8, false> {
59 typedef uint64_t type;
60 static_assert(sizeof(uintmax_t) == 8,
61 "Max integer size not supported for this toolchain.");
62 };
63
64 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
65 // support 128-bit math, then the ArithmeticPromotion template below will need
66 // to be updated (or more likely replaced with a decltype expression).
67
68 template <typename Integer>
69 struct UnsignedIntegerForSize {
70 typedef typename std::enable_if<
71 std::numeric_limits<Integer>::is_integer,
72 typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
73 };
74
75 template <typename Integer>
76 struct SignedIntegerForSize {
77 typedef typename std::enable_if<
78 std::numeric_limits<Integer>::is_integer,
79 typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
80 };
81
82 template <typename Integer>
83 struct TwiceWiderInteger {
84 typedef typename std::enable_if<
85 std::numeric_limits<Integer>::is_integer,
86 typename IntegerForSizeAndSign<
87 sizeof(Integer) * 2,
88 std::numeric_limits<Integer>::is_signed>::type>::type type;
89 };
90
91 template <typename Integer>
92 struct PositionOfSignBit {
93 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
94 size_t>::type value =
95 CHAR_BIT * sizeof(Integer) - 1;
96 };
97
98 // This is used for UnsignedAbs, where we need to support floating-point 26 // This is used for UnsignedAbs, where we need to support floating-point
99 // template instantiations even though we don't actually support the operations. 27 // template instantiations even though we don't actually support the operations.
100 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, 28 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
101 // so the float versions will not compile. 29 // so the float versions will not compile.
102 template <typename Numeric, 30 template <typename Numeric,
103 bool IsInteger = std::numeric_limits<Numeric>::is_integer, 31 bool IsInteger = std::numeric_limits<Numeric>::is_integer,
104 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> 32 bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
105 struct UnsignedOrFloatForSize; 33 struct UnsignedOrFloatForSize;
106 34
107 template <typename Numeric> 35 template <typename Numeric>
(...skipping 14 matching lines...) Expand all
122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> 50 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
123 PositionOfSignBit<T>::value); 51 PositionOfSignBit<T>::value);
124 } 52 }
125 53
126 // This wrapper undoes the standard integer promotions. 54 // This wrapper undoes the standard integer promotions.
127 template <typename T> 55 template <typename T>
128 constexpr T BinaryComplement(T x) { 56 constexpr T BinaryComplement(T x) {
129 return static_cast<T>(~x); 57 return static_cast<T>(~x);
130 } 58 }
131 59
132 enum ArithmeticPromotionCategory {
133 LEFT_PROMOTION, // Use the type of the left-hand argument.
134 RIGHT_PROMOTION, // Use the type of the right-hand argument.
135 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent.
136 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type.
137 };
138
139 template <ArithmeticPromotionCategory Promotion,
140 typename Lhs,
141 typename Rhs = Lhs>
142 struct ArithmeticPromotion;
143
144 template <typename Lhs,
145 typename Rhs,
146 ArithmeticPromotionCategory Promotion =
147 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
148 ? LEFT_PROMOTION
149 : RIGHT_PROMOTION>
150 struct MaxExponentPromotion;
151
152 template <typename Lhs, typename Rhs>
153 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
154 using type = Lhs;
155 };
156
157 template <typename Lhs, typename Rhs>
158 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
159 using type = Rhs;
160 };
161
162 template <typename Lhs,
163 typename Rhs = Lhs,
164 bool is_intmax_type =
165 std::is_integral<
166 typename MaxExponentPromotion<Lhs, Rhs>::type>::value &&
167 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) ==
168 sizeof(intmax_t),
169 bool is_max_exponent =
170 StaticDstRangeRelationToSrcRange<
171 typename MaxExponentPromotion<Lhs, Rhs>::type,
172 Lhs>::value ==
173 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
174 typename MaxExponentPromotion<Lhs, Rhs>::type,
175 Rhs>::value == NUMERIC_RANGE_CONTAINED>
176 struct BigEnoughPromotion;
177
178 // The side with the max exponent is big enough.
179 template <typename Lhs, typename Rhs, bool is_intmax_type>
180 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
181 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
182 static const bool is_contained = true;
183 };
184
185 // We can use a twice wider type to fit.
186 template <typename Lhs, typename Rhs>
187 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
188 using type = typename IntegerForSizeAndSign<
189 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2,
190 std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type;
191 static const bool is_contained = true;
192 };
193
194 // No type is large enough.
195 template <typename Lhs, typename Rhs>
196 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
197 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
198 static const bool is_contained = false;
199 };
200
201 // These are the supported promotion types.
202
203 // Use the type supporting the largest exponent.
204 template <typename Lhs, typename Rhs>
205 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> {
206 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
207 static const bool is_contained = true;
208 };
209
210 // Attempt to find a big enough type.
211 template <typename Lhs, typename Rhs>
212 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> {
213 using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
214 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained;
215 };
216
217 // We can statically check if operations on the provided types can wrap, so we
218 // can skip the checked operations if they're not needed. So, for an integer we
219 // care if the destination type preserves the sign and is twice the width of
220 // the source.
221 template <typename T, typename Lhs, typename Rhs>
222 struct IsIntegerArithmeticSafe {
223 static const bool value = !std::numeric_limits<T>::is_iec559 &&
224 StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
225 NUMERIC_RANGE_CONTAINED &&
226 sizeof(T) >= (2 * sizeof(Lhs)) &&
227 StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
228 NUMERIC_RANGE_CONTAINED &&
229 sizeof(T) >= (2 * sizeof(Rhs));
230 };
231
232 // Probe for builtin math overflow support on Clang and version check on GCC. 60 // Probe for builtin math overflow support on Clang and version check on GCC.
233 #if defined(__has_builtin) 61 #if defined(__has_builtin)
234 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) 62 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow))
235 #elif defined(__GNUC__) 63 #elif defined(__GNUC__)
236 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) 64 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5)
237 #else 65 #else
238 #define USE_OVERFLOW_BUILTINS (0) 66 #define USE_OVERFLOW_BUILTINS (0)
239 #endif 67 #endif
240 68
241 // Here are the actual portable checked integer math implementations. 69 // Here are the actual portable checked integer math implementations.
(...skipping 623 matching lines...) Expand 10 before | Expand all | Expand 10 after
865 693
866 // Copy constructor. 694 // Copy constructor.
867 template <typename Src> 695 template <typename Src>
868 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) 696 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
869 : value_(static_cast<T>(rhs.value())) {} 697 : value_(static_cast<T>(rhs.value())) {}
870 698
871 constexpr bool is_valid() const { return std::isfinite(value_); } 699 constexpr bool is_valid() const { return std::isfinite(value_); }
872 constexpr T value() const { return value_; } 700 constexpr T value() const { return value_; }
873 }; 701 };
874 702
875 // The following are helper templates used in the CheckedNumeric class.
876 template <typename T>
877 class CheckedNumeric;
878
879 // Used to treat CheckedNumeric and arithmetic underlying types the same.
880 template <typename T>
881 struct UnderlyingType {
882 using type = T;
883 static const bool is_numeric = std::is_arithmetic<T>::value;
884 static const bool is_checked = false;
885 };
886
887 template <typename T>
888 struct UnderlyingType<CheckedNumeric<T>> {
889 using type = T;
890 static const bool is_numeric = true;
891 static const bool is_checked = true;
892 };
893
894 template <typename L, typename R>
895 struct IsCheckedOp {
896 static const bool value =
897 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
898 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
899 };
900
901 template <template <typename, typename, typename> class M, 703 template <template <typename, typename, typename> class M,
902 typename L, 704 typename L,
903 typename R> 705 typename R>
904 struct MathWrapper { 706 struct MathWrapper {
905 using math = M<typename UnderlyingType<L>::type, 707 using math = M<typename UnderlyingType<L>::type,
906 typename UnderlyingType<R>::type, 708 typename UnderlyingType<R>::type,
907 void>; 709 void>;
908 using type = typename math::result_type; 710 using type = typename math::result_type;
909 }; 711 };
910 712
911 } // namespace internal 713 } // namespace internal
912 } // namespace base 714 } // namespace base
913 715
914 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 716 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW
« no previous file with comments | « base/numerics/safe_math.h ('k') | base/numerics/safe_numerics_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698