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