1 // Copyright 2017 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_ 7 8 #include <stddef.h> 9 10 #include <limits> 11 #include <type_traits> 12 13 #include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math_impl.h" 14 15 namespace partition_alloc::internal::base { 16 namespace internal { 17 18 template <typename T> 19 class CheckedNumeric { 20 static_assert(std::is_arithmetic<T>::value, 21 "CheckedNumeric<T>: T must be a numeric type."); 22 23 public: 24 template <typename Src> 25 friend class CheckedNumeric; 26 27 using type = T; 28 29 constexpr CheckedNumeric() = default; 30 31 // Copy constructor. 32 template <typename Src> CheckedNumeric(const CheckedNumeric<Src> & rhs)33 constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs) 34 : state_(rhs.state_.value(), rhs.IsValid()) {} 35 36 // This is not an explicit constructor because we implicitly upgrade regular 37 // numerics to CheckedNumerics to make them easier to use. 38 template <typename Src> CheckedNumeric(Src value)39 constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) 40 : state_(value) { 41 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); 42 } 43 44 // This is not an explicit constructor because we want a seamless conversion 45 // from StrictNumeric types. 46 template <typename Src> CheckedNumeric(StrictNumeric<Src> value)47 constexpr CheckedNumeric( 48 StrictNumeric<Src> value) // NOLINT(runtime/explicit) 49 : state_(static_cast<Src>(value)) {} 50 51 // IsValid() - The public API to test if a CheckedNumeric is currently valid. 52 // A range checked destination type can be supplied using the Dst template 53 // parameter. 54 template <typename Dst = T> IsValid()55 constexpr bool IsValid() const { 56 return state_.is_valid() && 57 IsValueInRangeForNumericType<Dst>(state_.value()); 58 } 59 60 // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid 61 // and is within the range supported by the destination type. Returns true if 62 // successful and false otherwise. 63 template <typename Dst> 64 #if defined(__clang__) || defined(__GNUC__) 65 __attribute__((warn_unused_result)) 66 #elif defined(_MSC_VER) 67 _Check_return_ 68 #endif 69 constexpr bool AssignIfValid(Dst * result)70 AssignIfValid(Dst* result) const { 71 return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) 72 ? ((*result = static_cast<Dst>(state_.value())), true) 73 : false; 74 } 75 76 // ValueOrDie() - The primary accessor for the underlying value. If the 77 // current state is not valid it will CHECK and crash. 78 // A range checked destination type can be supplied using the Dst template 79 // parameter, which will trigger a CHECK if the value is not in bounds for 80 // the destination. 81 // The CHECK behavior can be overridden by supplying a handler as a 82 // template parameter, for test code, etc. However, the handler cannot access 83 // the underlying value, and it is not available through other means. 84 template <typename Dst = T, class CheckHandler = CheckOnFailure> ValueOrDie()85 constexpr StrictNumeric<Dst> ValueOrDie() const { 86 return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) 87 ? static_cast<Dst>(state_.value()) 88 : CheckHandler::template HandleFailure<Dst>(); 89 } 90 91 // ValueOrDefault(T default_value) - A convenience method that returns the 92 // current value if the state is valid, and the supplied default_value for 93 // any other state. 94 // A range checked destination type can be supplied using the Dst template 95 // parameter. WARNING: This function may fail to compile or CHECK at runtime 96 // if the supplied default_value is not within range of the destination type. 97 template <typename Dst = T, typename Src> ValueOrDefault(const Src default_value)98 constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const { 99 return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) 100 ? static_cast<Dst>(state_.value()) 101 : checked_cast<Dst>(default_value); 102 } 103 104 // Returns a checked numeric of the specified type, cast from the current 105 // CheckedNumeric. If the current state is invalid or the destination cannot 106 // represent the result then the returned CheckedNumeric will be invalid. 107 template <typename Dst> Cast()108 constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const { 109 return *this; 110 } 111 112 // This friend method is available solely for providing more detailed logging 113 // in the tests. Do not implement it in production code, because the 114 // underlying values may change at any time. 115 template <typename U> 116 friend U GetNumericValueForTest(const CheckedNumeric<U>& src); 117 118 // Prototypes for the supported arithmetic operator overloads. 119 template <typename Src> 120 constexpr CheckedNumeric& operator+=(const Src rhs); 121 template <typename Src> 122 constexpr CheckedNumeric& operator-=(const Src rhs); 123 template <typename Src> 124 constexpr CheckedNumeric& operator*=(const Src rhs); 125 template <typename Src> 126 constexpr CheckedNumeric& operator/=(const Src rhs); 127 template <typename Src> 128 constexpr CheckedNumeric& operator%=(const Src rhs); 129 template <typename Src> 130 constexpr CheckedNumeric& operator<<=(const Src rhs); 131 template <typename Src> 132 constexpr CheckedNumeric& operator>>=(const Src rhs); 133 template <typename Src> 134 constexpr CheckedNumeric& operator&=(const Src rhs); 135 template <typename Src> 136 constexpr CheckedNumeric& operator|=(const Src rhs); 137 template <typename Src> 138 constexpr CheckedNumeric& operator^=(const Src rhs); 139 140 constexpr CheckedNumeric operator-() const { 141 // Use an optimized code path for a known run-time variable. 142 if (!PA_IsConstantEvaluated() && std::is_signed<T>::value && 143 std::is_floating_point<T>::value) { 144 return FastRuntimeNegate(); 145 } 146 // The negation of two's complement int min is int min. 147 const bool is_valid = 148 IsValid() && 149 (!std::is_signed<T>::value || std::is_floating_point<T>::value || 150 NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest()); 151 return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid); 152 } 153 154 constexpr CheckedNumeric operator~() const { 155 return CheckedNumeric<decltype(InvertWrapper(T()))>( 156 InvertWrapper(state_.value()), IsValid()); 157 } 158 Abs()159 constexpr CheckedNumeric Abs() const { 160 return !IsValueNegative(state_.value()) ? *this : -*this; 161 } 162 163 template <typename U> Max(const U rhs)164 constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max( 165 const U rhs) const { 166 return CheckMax(*this, rhs); 167 } 168 169 template <typename U> Min(const U rhs)170 constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min( 171 const U rhs) const { 172 return CheckMin(*this, rhs); 173 } 174 175 // This function is available only for integral types. It returns an unsigned 176 // integer of the same width as the source type, containing the absolute value 177 // of the source, and properly handling signed min. 178 constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs()179 UnsignedAbs() const { 180 return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( 181 SafeUnsignedAbs(state_.value()), state_.is_valid()); 182 } 183 184 constexpr CheckedNumeric& operator++() { 185 *this += 1; 186 return *this; 187 } 188 189 constexpr CheckedNumeric operator++(int) { 190 CheckedNumeric value = *this; 191 *this += 1; 192 return value; 193 } 194 195 constexpr CheckedNumeric& operator--() { 196 *this -= 1; 197 return *this; 198 } 199 200 constexpr CheckedNumeric operator--(int) { 201 // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20. 202 const CheckedNumeric value = *this; 203 *this -= 1; 204 return value; 205 } 206 207 // These perform the actual math operations on the CheckedNumerics. 208 // Binary arithmetic operations. 209 template <template <typename, typename, typename> class M, 210 typename L, 211 typename R> MathOp(const L lhs,const R rhs)212 static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) { 213 using Math = typename MathWrapper<M, L, R>::math; 214 T result = 0; 215 const bool is_valid = 216 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && 217 Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); 218 return CheckedNumeric<T>(result, is_valid); 219 } 220 221 // Assignment arithmetic operations. 222 template <template <typename, typename, typename> class M, typename R> MathOp(const R rhs)223 constexpr CheckedNumeric& MathOp(const R rhs) { 224 using Math = typename MathWrapper<M, T, R>::math; 225 T result = 0; // Using T as the destination saves a range check. 226 const bool is_valid = 227 state_.is_valid() && Wrapper<R>::is_valid(rhs) && 228 Math::Do(state_.value(), Wrapper<R>::value(rhs), &result); 229 *this = CheckedNumeric<T>(result, is_valid); 230 return *this; 231 } 232 233 private: 234 CheckedNumericState<T> state_; 235 FastRuntimeNegate()236 CheckedNumeric FastRuntimeNegate() const { 237 T result; 238 const bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result); 239 return CheckedNumeric<T>(result, IsValid() && success); 240 } 241 242 template <typename Src> CheckedNumeric(Src value,bool is_valid)243 constexpr CheckedNumeric(Src value, bool is_valid) 244 : state_(value, is_valid) {} 245 246 // These wrappers allow us to handle state the same way for both 247 // CheckedNumeric and POD arithmetic types. 248 template <typename Src> 249 struct Wrapper { is_validWrapper250 static constexpr bool is_valid(Src) { return true; } valueWrapper251 static constexpr Src value(Src value) { return value; } 252 }; 253 254 template <typename Src> 255 struct Wrapper<CheckedNumeric<Src>> { 256 static constexpr bool is_valid(const CheckedNumeric<Src> v) { 257 return v.IsValid(); 258 } 259 static constexpr Src value(const CheckedNumeric<Src> v) { 260 return v.state_.value(); 261 } 262 }; 263 264 template <typename Src> 265 struct Wrapper<StrictNumeric<Src>> { 266 static constexpr bool is_valid(const StrictNumeric<Src>) { return true; } 267 static constexpr Src value(const StrictNumeric<Src> v) { 268 return static_cast<Src>(v); 269 } 270 }; 271 }; 272 273 // Convenience functions to avoid the ugly template disambiguator syntax. 274 template <typename Dst, typename Src> 275 constexpr bool IsValidForType(const CheckedNumeric<Src> value) { 276 return value.template IsValid<Dst>(); 277 } 278 279 template <typename Dst, typename Src> 280 constexpr StrictNumeric<Dst> ValueOrDieForType( 281 const CheckedNumeric<Src> value) { 282 return value.template ValueOrDie<Dst>(); 283 } 284 285 template <typename Dst, typename Src, typename Default> 286 constexpr StrictNumeric<Dst> ValueOrDefaultForType( 287 const CheckedNumeric<Src> value, 288 const Default default_value) { 289 return value.template ValueOrDefault<Dst>(default_value); 290 } 291 292 // Convenience wrapper to return a new CheckedNumeric from the provided 293 // arithmetic or CheckedNumericType. 294 template <typename T> 295 constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum( 296 const T value) { 297 return value; 298 } 299 300 // These implement the variadic wrapper for the math operations. 301 template <template <typename, typename, typename> class M, 302 typename L, 303 typename R> 304 constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp( 305 const L lhs, 306 const R rhs) { 307 using Math = typename MathWrapper<M, L, R>::math; 308 return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs, 309 rhs); 310 } 311 312 // General purpose wrapper template for arithmetic operations. 313 template <template <typename, typename, typename> class M, 314 typename L, 315 typename R, 316 typename... Args> 317 constexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) { 318 return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...); 319 } 320 321 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=) 322 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=) 323 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=) 324 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=) 325 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=) 326 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=) 327 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=) 328 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=) 329 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=) 330 PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=) 331 PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max) 332 PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min) 333 334 // These are some extra StrictNumeric operators to support simple pointer 335 // arithmetic with our result types. Since wrapping on a pointer is always 336 // bad, we trigger the CHECK condition here. 337 template <typename L, typename R> 338 L* operator+(L* lhs, const StrictNumeric<R> rhs) { 339 const uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs), 340 CheckMul(sizeof(L), static_cast<R>(rhs))) 341 .template ValueOrDie<uintptr_t>(); 342 return reinterpret_cast<L*>(result); 343 } 344 345 template <typename L, typename R> 346 L* operator-(L* lhs, const StrictNumeric<R> rhs) { 347 const uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs), 348 CheckMul(sizeof(L), static_cast<R>(rhs))) 349 .template ValueOrDie<uintptr_t>(); 350 return reinterpret_cast<L*>(result); 351 } 352 353 } // namespace internal 354 355 using internal::CheckAdd; 356 using internal::CheckAnd; 357 using internal::CheckDiv; 358 using internal::CheckedNumeric; 359 using internal::CheckLsh; 360 using internal::CheckMax; 361 using internal::CheckMin; 362 using internal::CheckMod; 363 using internal::CheckMul; 364 using internal::CheckOr; 365 using internal::CheckRsh; 366 using internal::CheckSub; 367 using internal::CheckXor; 368 using internal::IsValidForType; 369 using internal::MakeCheckedNum; 370 using internal::ValueOrDefaultForType; 371 using internal::ValueOrDieForType; 372 373 } // namespace partition_alloc::internal::base 374 375 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_ 376