Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : // Slightly adapted for inclusion in V8.
6 : // Copyright 2014 the V8 project authors. All rights reserved.
7 :
8 : #ifndef V8_BASE_SAFE_MATH_H_
9 : #define V8_BASE_SAFE_MATH_H_
10 :
11 : #include "src/base/safe_math_impl.h"
12 :
13 : namespace v8 {
14 : namespace base {
15 : namespace internal {
16 :
17 : // CheckedNumeric implements all the logic and operators for detecting integer
18 : // boundary conditions such as overflow, underflow, and invalid conversions.
19 : // The CheckedNumeric type implicitly converts from floating point and integer
20 : // data types, and contains overloads for basic arithmetic operations (i.e.: +,
21 : // -, *, /, %).
22 : //
23 : // The following methods convert from CheckedNumeric to standard numeric values:
24 : // IsValid() - Returns true if the underlying numeric value is valid (i.e. has
25 : // has not wrapped and is not the result of an invalid conversion).
26 : // ValueOrDie() - Returns the underlying value. If the state is not valid this
27 : // call will crash on a CHECK.
28 : // ValueOrDefault() - Returns the current value, or the supplied default if the
29 : // state is not valid.
30 : // ValueFloating() - Returns the underlying floating point value (valid only
31 : // only for floating point CheckedNumeric types).
32 : //
33 : // Bitwise operations are explicitly not supported, because correct
34 : // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
35 : // operations are explicitly not supported because they could result in a crash
36 : // on a CHECK condition. You should use patterns like the following for these
37 : // operations:
38 : // Bitwise operation:
39 : // CheckedNumeric<int> checked_int = untrusted_input_value;
40 : // int x = checked_int.ValueOrDefault(0) | kFlagValues;
41 : // Comparison:
42 : // CheckedNumeric<size_t> checked_size;
43 : // CheckedNumeric<int> checked_size = untrusted_input_value;
44 : // checked_size = checked_size + HEADER LENGTH;
45 : // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
46 : // Do stuff...
47 : template <typename T>
48 : class CheckedNumeric {
49 : public:
50 : typedef T type;
51 :
52 : CheckedNumeric() = default;
53 :
54 : // Copy constructor.
55 : template <typename Src>
56 : CheckedNumeric(const CheckedNumeric<Src>& rhs)
57 : : state_(rhs.ValueUnsafe(), rhs.validity()) {}
58 :
59 : template <typename Src>
60 : CheckedNumeric(Src value, RangeConstraint validity)
61 : : state_(value, validity) {}
62 :
63 : // This is not an explicit constructor because we implicitly upgrade regular
64 : // numerics to CheckedNumerics to make them easier to use.
65 : template <typename Src>
66 43506357 : CheckedNumeric(Src value) // NOLINT
67 : : state_(value) {
68 : // Argument must be numeric.
69 : STATIC_ASSERT(std::numeric_limits<Src>::is_specialized);
70 43506357 : }
71 :
72 : // IsValid() is the public API to test if a CheckedNumeric is currently valid.
73 : bool IsValid() const { return validity() == RANGE_VALID; }
74 :
75 : // ValueOrDie() The primary accessor for the underlying value. If the current
76 : // state is not valid it will CHECK and crash.
77 43505230 : T ValueOrDie() const {
78 43505230 : CHECK(IsValid());
79 43505230 : return state_.value();
80 : }
81 :
82 : // ValueOrDefault(T default_value) A convenience method that returns the
83 : // current value if the state is valid, and the supplied default_value for
84 : // any other state.
85 : T ValueOrDefault(T default_value) const {
86 114412 : return IsValid() ? state_.value() : default_value;
87 : }
88 :
89 : // ValueFloating() - Since floating point values include their validity state,
90 : // we provide an easy method for extracting them directly, without a risk of
91 : // crashing on a CHECK.
92 : T ValueFloating() const {
93 : // Argument must be a floating-point value.
94 : STATIC_ASSERT(std::numeric_limits<T>::is_iec559);
95 : return CheckedNumeric<T>::cast(*this).ValueUnsafe();
96 : }
97 :
98 : // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
99 : // tests and to avoid a big matrix of friend operator overloads. But the
100 : // values it returns are likely to change in the future.
101 : // Returns: current validity state (i.e. valid, overflow, underflow, nan).
102 : // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
103 : // saturation/wrapping so we can expose this state consistently and implement
104 : // saturated arithmetic.
105 174025555 : RangeConstraint validity() const { return state_.validity(); }
106 :
107 : // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
108 : // for tests and to avoid a big matrix of friend operator overloads. But the
109 : // values it returns are likely to change in the future.
110 : // Returns: the raw numeric value, regardless of the current state.
111 : // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
112 : // saturation/wrapping so we can expose this state consistently and implement
113 : // saturated arithmetic.
114 87013193 : T ValueUnsafe() const { return state_.value(); }
115 :
116 : // Prototypes for the supported arithmetic operator overloads.
117 : template <typename Src> CheckedNumeric& operator+=(Src rhs);
118 : template <typename Src> CheckedNumeric& operator-=(Src rhs);
119 : template <typename Src> CheckedNumeric& operator*=(Src rhs);
120 : template <typename Src> CheckedNumeric& operator/=(Src rhs);
121 : template <typename Src> CheckedNumeric& operator%=(Src rhs);
122 :
123 : CheckedNumeric operator-() const {
124 : RangeConstraint validity;
125 : T value = CheckedNeg(state_.value(), &validity);
126 : // Negation is always valid for floating point.
127 : if (std::numeric_limits<T>::is_iec559)
128 : return CheckedNumeric<T>(value);
129 :
130 : validity = GetRangeConstraint(state_.validity() | validity);
131 : return CheckedNumeric<T>(value, validity);
132 : }
133 :
134 : CheckedNumeric Abs() const {
135 : RangeConstraint validity;
136 : T value = CheckedAbs(state_.value(), &validity);
137 : // Absolute value is always valid for floating point.
138 : if (std::numeric_limits<T>::is_iec559)
139 : return CheckedNumeric<T>(value);
140 :
141 : validity = GetRangeConstraint(state_.validity() | validity);
142 : return CheckedNumeric<T>(value, validity);
143 : }
144 :
145 : CheckedNumeric& operator++() {
146 : *this += 1;
147 : return *this;
148 : }
149 :
150 : CheckedNumeric operator++(int) {
151 : CheckedNumeric value = *this;
152 : *this += 1;
153 : return value;
154 : }
155 :
156 : CheckedNumeric& operator--() {
157 : *this -= 1;
158 : return *this;
159 : }
160 :
161 : CheckedNumeric operator--(int) {
162 : CheckedNumeric value = *this;
163 : *this -= 1;
164 : return value;
165 : }
166 :
167 : // These static methods behave like a convenience cast operator targeting
168 : // the desired CheckedNumeric type. As an optimization, a reference is
169 : // returned when Src is the same type as T.
170 : template <typename Src>
171 : static CheckedNumeric<T> cast(
172 : Src u,
173 : typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
174 : 0) {
175 : return u;
176 : }
177 :
178 : template <typename Src>
179 : static CheckedNumeric<T> cast(
180 : const CheckedNumeric<Src>& u,
181 : typename enable_if<!is_same<Src, T>::value, int>::type = 0) {
182 : return u;
183 : }
184 :
185 : static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; }
186 :
187 : private:
188 : CheckedNumericState<T> state_;
189 : };
190 :
191 : // This is the boilerplate for the standard arithmetic operator overloads. A
192 : // macro isn't the prettiest solution, but it beats rewriting these five times.
193 : // Some details worth noting are:
194 : // * We apply the standard arithmetic promotions.
195 : // * We skip range checks for floating points.
196 : // * We skip range checks for destination integers with sufficient range.
197 : // TODO(jschuh): extract these out into templates.
198 : #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
199 : /* Binary arithmetic operator for CheckedNumerics of the same type. */ \
200 : template <typename T> \
201 : CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \
202 : const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \
203 : typedef typename ArithmeticPromotion<T>::type Promotion; \
204 : /* Floating point always takes the fast path */ \
205 : if (std::numeric_limits<T>::is_iec559) \
206 : return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
207 : if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \
208 : return CheckedNumeric<Promotion>( \
209 : lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
210 : GetRangeConstraint(rhs.validity() | lhs.validity())); \
211 : RangeConstraint validity = RANGE_VALID; \
212 : T result = Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \
213 : static_cast<Promotion>(rhs.ValueUnsafe()), \
214 : &validity); \
215 : return CheckedNumeric<Promotion>( \
216 : result, \
217 : GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \
218 : } \
219 : /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
220 : template <typename T> \
221 : template <typename Src> \
222 : CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \
223 : *this = CheckedNumeric<T>::cast(*this) OP CheckedNumeric<Src>::cast(rhs); \
224 : return *this; \
225 : } \
226 : /* Binary arithmetic operator for CheckedNumeric of different type. */ \
227 : template <typename T, typename Src> \
228 : CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
229 : const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \
230 : typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
231 : if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
232 : return CheckedNumeric<Promotion>( \
233 : lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
234 : GetRangeConstraint(rhs.validity() | lhs.validity())); \
235 : return CheckedNumeric<Promotion>::cast(lhs) \
236 : OP CheckedNumeric<Promotion>::cast(rhs); \
237 : } \
238 : /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
239 : template <typename T, typename Src> \
240 : CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
241 : const CheckedNumeric<T>& lhs, Src rhs) { \
242 : typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
243 : if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
244 : return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \
245 : lhs.validity()); \
246 : return CheckedNumeric<Promotion>::cast(lhs) \
247 : OP CheckedNumeric<Promotion>::cast(rhs); \
248 : } \
249 : /* Binary arithmetic operator for right numeric and left CheckedNumeric. */ \
250 : template <typename T, typename Src> \
251 : CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
252 : Src lhs, const CheckedNumeric<T>& rhs) { \
253 : typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
254 : if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
255 : return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \
256 : rhs.validity()); \
257 : return CheckedNumeric<Promotion>::cast(lhs) \
258 : OP CheckedNumeric<Promotion>::cast(rhs); \
259 : }
260 :
261 130517949 : BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += )
262 : BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= )
263 261043337 : BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= )
264 : BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= )
265 : BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= )
266 :
267 : #undef BASE_NUMERIC_ARITHMETIC_OPERATORS
268 :
269 : } // namespace internal
270 :
271 : using internal::CheckedNumeric;
272 :
273 : } // namespace base
274 : } // namespace v8
275 :
276 : #endif // V8_BASE_SAFE_MATH_H_
|