From 4022f87eb8716155291543efaaf289e51d7cbf43 Mon Sep 17 00:00:00 2001 From: tsepez Date: Mon, 23 Jan 2017 14:36:20 -0800 Subject: Update safe numerics package to get bitwise ops Fix callers conventions to avoid ambiguity. Fix bad bounds check unmasked by change. Directly include headers no longer pulled in by numerics itself. Review-Url: https://codereview.chromium.org/2640143003 --- third_party/base/numerics/safe_math.h | 602 ++++++++++++++++++++++++---------- 1 file changed, 420 insertions(+), 182 deletions(-) (limited to 'third_party/base/numerics/safe_math.h') diff --git a/third_party/base/numerics/safe_math.h b/third_party/base/numerics/safe_math.h index 013af1eb60..a0c41a467b 100644 --- a/third_party/base/numerics/safe_math.h +++ b/third_party/base/numerics/safe_math.h @@ -2,140 +2,268 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef PDFIUM_THIRD_PARTY_BASE_SAFE_MATH_H_ -#define PDFIUM_THIRD_PARTY_BASE_SAFE_MATH_H_ +#ifndef PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_ +#define PDFIUM_THIRD_PARTY_BASE_NUMERICS_SAFE_MATH_H_ -#include "safe_math_impl.h" +#include + +#include +#include + +#include "third_party/base/numerics/safe_math_impl.h" namespace pdfium { namespace base { namespace internal { -// CheckedNumeric implements all the logic and operators for detecting integer +// CheckedNumeric<> implements all the logic and operators for detecting integer // boundary conditions such as overflow, underflow, and invalid conversions. // The CheckedNumeric type implicitly converts from floating point and integer // data types, and contains overloads for basic arithmetic operations (i.e.: +, -// -, *, /, %). +// -, *, / for all types and %, <<, >>, &, |, ^ for integers). Type promotions +// are a slightly modified version of the standard C arithmetic rules with the +// two differences being that there is no default promotion to int and bitwise +// logical operations always return an unsigned of the wider type. +// +// You may also use one of the variadic convenience functions, which accept +// standard arithmetic or CheckedNumeric types, perform arithmetic operations, +// and return a CheckedNumeric result. The supported functions are: +// CheckAdd() - Addition. +// CheckSub() - Subtraction. +// CheckMul() - Multiplication. +// CheckDiv() - Division. +// CheckMod() - Modulous (integer only). +// CheckLsh() - Left integer shift (integer only). +// CheckRsh() - Right integer shift (integer only). +// CheckAnd() - Bitwise AND (integer only with unsigned result). +// CheckOr() - Bitwise OR (integer only with unsigned result). +// CheckXor() - Bitwise XOR (integer only with unsigned result). +// CheckMax() - Maximum of supplied arguments. +// CheckMin() - Minimum of supplied arguments. +// +// The unary negation, increment, and decrement operators are supported, along +// with the following unary arithmetic methods, which return a new +// CheckedNumeric as a result of the operation: +// Abs() - Absolute value. +// UnsignedAbs() - Absolute value as an equal-width unsigned underlying type +// (valid for only integral types). +// Max() - Returns whichever is greater of the current instance or argument. +// The underlying return type is whichever has the greatest magnitude. +// Min() - Returns whichever is lowest of the current instance or argument. +// The underlying return type is whichever has can represent the lowest +// number in the smallest width (e.g. int8_t over unsigned, int over +// int8_t, and float over int). // // The following methods convert from CheckedNumeric to standard numeric values: -// IsValid() - Returns true if the underlying numeric value is valid (i.e. has -// has not wrapped and is not the result of an invalid conversion). -// ValueOrDie() - Returns the underlying value. If the state is not valid this -// call will crash on a CHECK. -// ValueOrDefault() - Returns the current value, or the supplied default if the -// state is not valid. -// ValueFloating() - Returns the underlying floating point value (valid only -// only for floating point CheckedNumeric types). +// AssignIfValid() - Assigns the underlying value to the supplied destination +// pointer if the value is currently valid and within the range +// supported by the destination type. Returns true on success. +// **************************************************************************** +// * WARNING: All of the following functions return a StrictNumeric, which * +// * is valid for comparison and assignment operations, but will trigger a * +// * compile failure on attempts to assign to a type of insufficient range. * +// **************************************************************************** +// IsValid() - Returns true if the underlying numeric value is valid (i.e. has +// has not wrapped and is not the result of an invalid conversion). +// ValueOrDie() - Returns the underlying value. If the state is not valid this +// call will crash on a CHECK. +// ValueOrDefault() - Returns the current value, or the supplied default if the +// state is not valid (will not trigger a CHECK). // -// Bitwise operations are explicitly not supported, because correct -// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison -// operations are explicitly not supported because they could result in a crash -// on a CHECK condition. You should use patterns like the following for these -// operations: -// Bitwise operation: -// CheckedNumeric checked_int = untrusted_input_value; -// int x = checked_int.ValueOrDefault(0) | kFlagValues; -// Comparison: -// CheckedNumeric checked_size; -// CheckedNumeric checked_size = untrusted_input_value; -// checked_size = checked_size + HEADER LENGTH; +// The following wrapper functions can be used to avoid the template +// disambiguator syntax when converting a destination type. +// IsValidForType<>() in place of: a.template IsValid() +// ValueOrDieForType<>() in place of: a.template ValueOrDie() +// ValueOrDefaultForType<>() in place of: a.template ValueOrDefault(default) +// +// The following are general utility methods that are useful for converting +// between arithmetic types and CheckedNumeric types: +// CheckedNumeric::Cast() - Instance method returning a CheckedNumeric +// derived from casting the current instance to a CheckedNumeric of +// the supplied destination type. +// MakeCheckedNum() - Creates a new CheckedNumeric from the underlying type of +// the supplied arithmetic, CheckedNumeric, or StrictNumeric type. +// +// Comparison operations are explicitly not supported because they could result +// in a crash on an unexpected CHECK condition. You should use patterns like the +// following for comparisons: +// CheckedNumeric checked_size = untrusted_input_value; +// checked_size += HEADER LENGTH; // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) // Do stuff... + template class CheckedNumeric { + static_assert(std::is_arithmetic::value, + "CheckedNumeric: T must be a numeric type."); + public: - typedef T type; + using type = T; - CheckedNumeric() {} + constexpr CheckedNumeric() {} // Copy constructor. template - CheckedNumeric(const CheckedNumeric& rhs) - : state_(rhs.ValueUnsafe(), rhs.validity()) {} + constexpr CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} template - CheckedNumeric(Src value, RangeConstraint validity) - : state_(value, validity) {} + friend class CheckedNumeric; // This is not an explicit constructor because we implicitly upgrade regular // numerics to CheckedNumerics to make them easier to use. template - CheckedNumeric(Src value) + constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) : state_(value) { - COMPILE_ASSERT(std::numeric_limits::is_specialized, - argument_must_be_numeric); + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + constexpr CheckedNumeric( + StrictNumeric value) // NOLINT(runtime/explicit) + : state_(static_cast(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType(state_.value()); } - // IsValid() is the public API to test if a CheckedNumeric is currently valid. - bool IsValid() const { return validity() == RANGE_VALID; } + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template + constexpr bool AssignIfValid(Dst* result) const { + return IsValid() ? ((*result = static_cast(state_.value())), true) + : false; + } - // ValueOrDie() The primary accessor for the underlying value. If the current - // state is not valid it will CHECK and crash. - T ValueOrDie() const { - CHECK(IsValid()); - return state_.value(); + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template + constexpr StrictNumeric ValueOrDie() const { + return IsValid() ? static_cast(state_.value()) + : CheckHandler::template HandleFailure(); } - // ValueOrDefault(T default_value) A convenience method that returns the + // ValueOrDefault(T default_value) - A convenience method that returns the // current value if the state is valid, and the supplied default_value for // any other state. - T ValueOrDefault(T default_value) const { - return IsValid() ? state_.value() : default_value; + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template + constexpr StrictNumeric ValueOrDefault(const Src default_value) const { + return IsValid() ? static_cast(state_.value()) + : checked_cast(default_value); } - // ValueFloating() - Since floating point values include their validity state, - // we provide an easy method for extracting them directly, without a risk of - // crashing on a CHECK. - T ValueFloating() const { - COMPILE_ASSERT(std::numeric_limits::is_iec559, argument_must_be_float); - return CheckedNumeric::cast(*this).ValueUnsafe(); + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template + constexpr CheckedNumeric::type> Cast() const { + return *this; } - // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for - // tests and to avoid a big matrix of friend operator overloads. But the - // values it returns are likely to change in the future. - // Returns: current validity state (i.e. valid, overflow, underflow, nan). - // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for - // saturation/wrapping so we can expose this state consistently and implement - // saturated arithmetic. - RangeConstraint validity() const { return state_.validity(); } - - // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now - // for tests and to avoid a big matrix of friend operator overloads. But the - // values it returns are likely to change in the future. - // Returns: the raw numeric value, regardless of the current state. - // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for - // saturation/wrapping so we can expose this state consistently and implement - // saturated arithmetic. - T ValueUnsafe() const { return state_.value(); } + // This friend method is available solely for providing more detailed logging + // in the the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template + friend U GetNumericValueForTest(const CheckedNumeric& src); // Prototypes for the supported arithmetic operator overloads. - template CheckedNumeric& operator+=(Src rhs); - template CheckedNumeric& operator-=(Src rhs); - template CheckedNumeric& operator*=(Src rhs); - template CheckedNumeric& operator/=(Src rhs); - template CheckedNumeric& operator%=(Src rhs); - - CheckedNumeric operator-() const { - RangeConstraint validity; - T value = CheckedNeg(state_.value(), &validity); - // Negation is always valid for floating point. - if (std::numeric_limits::is_iec559) - return CheckedNumeric(value); - - validity = GetRangeConstraint(state_.validity() | validity); - return CheckedNumeric(value, validity); + template + CheckedNumeric& operator+=(const Src rhs); + template + CheckedNumeric& operator-=(const Src rhs); + template + CheckedNumeric& operator*=(const Src rhs); + template + CheckedNumeric& operator/=(const Src rhs); + template + CheckedNumeric& operator%=(const Src rhs); + template + CheckedNumeric& operator<<=(const Src rhs); + template + CheckedNumeric& operator>>=(const Src rhs); + template + CheckedNumeric& operator&=(const Src rhs); + template + CheckedNumeric& operator|=(const Src rhs); + template + CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + return CheckedNumeric( + NegateWrapper(state_.value()), + IsValid() && + (!std::is_signed::value || std::is_floating_point::value || + NegateWrapper(state_.value()) != + std::numeric_limits::lowest())); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric( + InvertWrapper(state_.value()), IsValid()); } - CheckedNumeric Abs() const { - RangeConstraint validity; - T value = CheckedAbs(state_.value(), &validity); - // Absolute value is always valid for floating point. - if (std::numeric_limits::is_iec559) - return CheckedNumeric(value); + constexpr CheckedNumeric Abs() const { + return CheckedNumeric( + AbsWrapper(state_.value()), + IsValid() && + (!std::is_signed::value || std::is_floating_point::value || + AbsWrapper(state_.value()) != std::numeric_limits::lowest())); + } + + template + constexpr CheckedNumeric::type> Max( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsGreater::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } - validity = GetRangeConstraint(state_.validity() | validity); - return CheckedNumeric(value, validity); + template + constexpr CheckedNumeric::type> Min( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsLess::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric::type> + UnsignedAbs() const { + return CheckedNumeric::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); } CheckedNumeric& operator++() { @@ -160,113 +288,223 @@ class CheckedNumeric { return value; } - // These static methods behave like a convenience cast operator targeting - // the desired CheckedNumeric type. As an optimization, a reference is - // returned when Src is the same type as T. + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template