|
Packit |
01d647 |
// ********************************************************* -*- C++ -*-
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
* Copyright (C) 2004-2018 Exiv2 authors
|
|
Packit |
01d647 |
* This program is part of the Exiv2 distribution.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
01d647 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
01d647 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
01d647 |
* of the License, or (at your option) any later version.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
01d647 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
01d647 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
01d647 |
* GNU General Public License for more details.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
01d647 |
* along with this program; if not, write to the Free Software
|
|
Packit |
01d647 |
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
@file safe_op.hpp
|
|
Packit |
01d647 |
@brief Overflow checks for integers
|
|
Packit |
01d647 |
@author Dan Čermák (D4N)
|
|
Packit |
01d647 |
dan.cermak@cgc-instruments.com
|
|
Packit |
01d647 |
@date 14-Dec-17, D4N: created
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifndef SAFE_OP_HPP_
|
|
Packit |
01d647 |
#define SAFE_OP_HPP_
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include <limits>
|
|
Packit |
01d647 |
#include <stdexcept>
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef _MSC_VER
|
|
Packit |
01d647 |
#include <Intsafe.h>
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Arithmetic operations with overflow checks
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
namespace Safe
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Helper functions for providing integer overflow checks.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This namespace contains internal helper functions fallback_$op_overflow
|
|
Packit |
01d647 |
* and builtin_$op_overflow (where $op is an arithmetic operation like add,
|
|
Packit |
01d647 |
* subtract, etc.). Both provide the following interface:
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* bool fallback/builtin_$op_overflow(T first, T second, T& result);
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* where T is an integer type.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* Each function performs checks whether first $op second can be safely
|
|
Packit |
01d647 |
* performed without overflows. If yes, the result is saved in result and
|
|
Packit |
01d647 |
* false is returned. Otherwise true is returned and the contents of result
|
|
Packit |
01d647 |
* are unspecified.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* fallback_$op_overflow implements a portable but slower overflow check.
|
|
Packit |
01d647 |
* builtin_$op_overflow uses compiler builtins (when available) and should
|
|
Packit |
01d647 |
* be faster. As builtins are not available for all types,
|
|
Packit |
01d647 |
* builtin_$op_overflow falls back to fallback_$op_overflow when no builtin
|
|
Packit |
01d647 |
* is available.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
namespace Internal
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Helper struct to determine whether a type is signed or unsigned
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This struct is a backport of std::is_signed from C++11. It has a public
|
|
Packit |
01d647 |
* enum with the property VALUE which is true when the type is signed or
|
|
Packit |
01d647 |
* false if it is unsigned.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
struct is_signed
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
enum
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
VALUE = T(-1) < T(0)
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Helper struct for SFINAE, from C++11
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
* This struct has a public typedef called type typedef'd to T if B is
|
|
Packit |
01d647 |
* true. Otherwise there is no typedef.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <bool B, class T = void>
|
|
Packit |
01d647 |
struct enable_if
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Specialization of enable_if for the case B == true
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <class T>
|
|
Packit |
01d647 |
struct enable_if<true, T>
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
typedef T type;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Check the addition of two numbers for overflows for signed
|
|
Packit |
01d647 |
* integer types larger than int or with the same size as int.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function performs a check if summand_1 + summand_2 would
|
|
Packit |
01d647 |
* overflow and returns true in that case. If no overflow occurs,
|
|
Packit |
01d647 |
* the sum is saved in result and false is returned.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @return true on overflow, false on no overflow
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @param[in] summand_1, summand_2 The summands with are added
|
|
Packit |
01d647 |
* @param[out] result Result of the addition, only populated when no
|
|
Packit |
01d647 |
* overflow occurs.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* Further information:
|
|
Packit |
01d647 |
* https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
typename enable_if<is_signed<T>::VALUE && sizeof(T) >= sizeof(int), bool>::type fallback_add_overflow(
|
|
Packit |
01d647 |
T summand_1, T summand_2, T& result)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (((summand_2 >= 0) && (summand_1 > std::numeric_limits<T>::max() - summand_2)) ||
|
|
Packit |
01d647 |
((summand_2 < 0) && (summand_1 < std::numeric_limits<T>::min() - summand_2))) {
|
|
Packit |
01d647 |
return true;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
result = summand_1 + summand_2;
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Check the addition of two numbers for overflows for signed
|
|
Packit |
01d647 |
* integer types smaller than int.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function adds summand_1 and summand_2 exploiting integer
|
|
Packit |
01d647 |
* promotion rules, thereby not causing undefined behavior. The
|
|
Packit |
01d647 |
* result is checked against the limits of T and true is returned if
|
|
Packit |
01d647 |
* they are exceeded. Otherwise the sum is saved in result and false
|
|
Packit |
01d647 |
* is returned.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @return true on overflow, false on no overflow
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @param[in] summand_1, summand_2 The summands with are added
|
|
Packit |
01d647 |
* @param[out] result Result of the addition, only populated when no
|
|
Packit |
01d647 |
* overflow occurs.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* Further information:
|
|
Packit |
01d647 |
* https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
typename enable_if<is_signed<T>::VALUE && sizeof(T) < sizeof(int), bool>::type fallback_add_overflow(
|
|
Packit |
01d647 |
T summand_1, T summand_2, T& result)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
const int res = summand_1 + summand_2;
|
|
Packit |
01d647 |
if ((res > std::numeric_limits<T>::max()) || (res < std::numeric_limits<T>::min())) {
|
|
Packit |
01d647 |
return true;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
result = static_cast<T>(res);
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Check the addition of two numbers for overflows for unsigned
|
|
Packit |
01d647 |
* integer types.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function adds summand_1 and summand_2 and checks after that if
|
|
Packit |
01d647 |
* the operation overflowed. Since these are unsigned integers, no
|
|
Packit |
01d647 |
* undefined behavior is invoked.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @return true on overflow, false on no overflow
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @param[in] summand_1, summand_2 The summands with are added
|
|
Packit |
01d647 |
* @param[out] result Result of the addition
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* Further information:
|
|
Packit |
01d647 |
* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
typename enable_if::VALUE, bool>::type fallback_add_overflow(T summand_1, T summand_2, T& result)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
result = summand_1 + summand_2;
|
|
Packit |
01d647 |
return result < summand_1;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Overflow addition check using compiler intrinsics.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function behaves exactly like fallback_add_overflow() but it
|
|
Packit |
01d647 |
* relies on compiler intrinsics instead. This version should be faster
|
|
Packit |
01d647 |
* than the fallback version as it can fully utilize available CPU
|
|
Packit |
01d647 |
* instructions & the compiler's diagnostic.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* However, as some compilers don't provide intrinsics for certain
|
|
Packit |
01d647 |
* types, the default implementation is the version from fallback.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function is fully specialized for each compiler.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
bool builtin_add_overflow(T summand_1, T summand_2, T& result)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return fallback_add_overflow(summand_1, summand_2, result);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#if defined(__GNUC__) || defined(__clang__)
|
|
Packit |
01d647 |
#if __GNUC__ >= 5 || __clang_major__ >= 3
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* This macro pastes a specialization of builtin_add_overflow using gcc's &
|
|
Packit |
01d647 |
* clang's __builtin_(s/u)add(l)(l)_overlow()
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* The add function is implemented by forwarding the parameters to the intrinsic
|
|
Packit |
01d647 |
* and returning its value.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* The intrinsics are documented here:
|
|
Packit |
01d647 |
* https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html#Integer-Overflow-Builtins
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
#define SPECIALIZE_builtin_add_overflow(type, builtin_name) \
|
|
Packit |
01d647 |
/* Full specialization of builtin_add_overflow for type using the */ \
|
|
Packit |
01d647 |
/* builtin_name intrinsic */ \
|
|
Packit |
01d647 |
template <> \
|
|
Packit |
01d647 |
inline bool builtin_add_overflow<type>(type summand_1, type summand_2, type & result) \
|
|
Packit |
01d647 |
{ \
|
|
Packit |
01d647 |
return builtin_name(summand_1, summand_2, &result); \
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(int, __builtin_sadd_overflow);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(long, __builtin_saddl_overflow);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(long long, __builtin_saddll_overflow);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(unsigned int, __builtin_uadd_overflow);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(unsigned long, __builtin_uaddl_overflow);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow(unsigned long long, __builtin_uaddll_overflow);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#undef SPECIALIZE_builtin_add_overflow
|
|
Packit |
01d647 |
#endif // __GNUC__ >= 5 || __clang_major >= 3
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#elif defined(_MSC_VER)
|
|
Packit |
01d647 |
// intrinsics are not in available in MSVC 2005 and earlier
|
|
Packit |
01d647 |
#if _MSC_VER >= 1400
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* This macro pastes a specialization of builtin_add_overflow using MSVC's
|
|
Packit |
01d647 |
* U(Int/Long/LongLong)Add.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* The add function is implemented by forwarding the parameters to the
|
|
Packit |
01d647 |
* intrinsic. As MSVC's intrinsics return S_OK on success, this specialization
|
|
Packit |
01d647 |
* returns whether the intrinsics return value does not equal S_OK. This ensures
|
|
Packit |
01d647 |
* a uniform interface of the add function (false is returned when no overflow
|
|
Packit |
01d647 |
* occurs, true on overflow).
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* The intrinsics are documented here:
|
|
Packit |
01d647 |
* https://msdn.microsoft.com/en-us/library/windows/desktop/ff516460(v=vs.85).aspx
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
#define SPECIALIZE_builtin_add_overflow_WIN(type, builtin_name) \
|
|
Packit |
01d647 |
template <> \
|
|
Packit |
01d647 |
inline bool builtin_add_overflow(type summand_1, type summand_2, type& result) \
|
|
Packit |
01d647 |
{ \
|
|
Packit |
01d647 |
return builtin_name(summand_1, summand_2, &result) != S_OK; \
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow_WIN(unsigned int, UIntAdd);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow_WIN(unsigned long, ULongAdd);
|
|
Packit |
01d647 |
SPECIALIZE_builtin_add_overflow_WIN(unsigned long long, ULongLongAdd);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#undef SPECIALIZE_builtin_add_overflow_WIN
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#endif // _MSC_VER >= 1400
|
|
Packit |
01d647 |
#endif // defined(_MSC_VER)
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // namespace Internal
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Safe addition, throws an exception on overflow.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function returns the result of summand_1 and summand_2 only when the
|
|
Packit |
01d647 |
* operation would not overflow, otherwise an exception of type
|
|
Packit |
01d647 |
* std::overflow_error is thrown.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @param[in] summand_1, summand_2 summands to be summed up
|
|
Packit |
01d647 |
* @return the sum of summand_1 and summand_2
|
|
Packit |
01d647 |
* @throws std::overflow_error if the addition would overflow
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function utilizes compiler builtins when available and should have a
|
|
Packit |
01d647 |
* very small performance hit then. When builtins are unavailable, a more
|
|
Packit |
01d647 |
* extensive check is required.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* Builtins are available for the following configurations:
|
|
Packit |
01d647 |
* - GCC/Clang for signed and unsigned int, long and long long (not char & short)
|
|
Packit |
01d647 |
* - MSVC for unsigned int, long and long long
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
T add(T summand_1, T summand_2)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
T res = 0;
|
|
Packit |
01d647 |
if (Internal::builtin_add_overflow(summand_1, summand_2, res)) {
|
|
Packit |
01d647 |
throw std::overflow_error("Overflow in addition");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return res;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/*!
|
|
Packit |
01d647 |
* @brief Calculates the absolute value of a number without producing
|
|
Packit |
01d647 |
* negative values.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* The "standard" implementation of `abs(num)` (`num < 0 ? -num : num`)
|
|
Packit |
01d647 |
* produces negative values when `num` is the smallest negative number. This
|
|
Packit |
01d647 |
* is caused by `-1 * INTMAX = INTMIN + 1`, i.e. the real result of
|
|
Packit |
01d647 |
* `abs(INTMIN)` overflows the integer type and results in `INTMIN` again
|
|
Packit |
01d647 |
* (this is not guaranteed as it invokes undefined behavior).
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This function does not exhibit this behavior, it returns
|
|
Packit |
01d647 |
* `std::numeric_limits<T>::max()` when the input is
|
|
Packit |
01d647 |
* `std::numeric_limits<T>::min()`. The downside of this is that two
|
|
Packit |
01d647 |
* negative values produce the same absolute value:
|
|
Packit |
01d647 |
* `std::numeric_limits<T>::min()` and `std::numeric_limits<T>::min() + 1`.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* @tparam T a signed integer type
|
|
Packit |
01d647 |
* @param[in] num The number which absolute value should be computed.
|
|
Packit |
01d647 |
* @throws Never throws an exception.
|
|
Packit |
01d647 |
* @return The absolute value of `num` or `std::numeric_limits<T>::max()`
|
|
Packit |
01d647 |
* when `num == std::numeric_limits<T>::min()`.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
template <typename T>
|
|
Packit |
01d647 |
typename Internal::enable_if<Internal::is_signed<T>::VALUE, T>::type abs(T num) throw()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (num == std::numeric_limits<T>::min()) {
|
|
Packit |
01d647 |
return std::numeric_limits<T>::max();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return num < 0 ? -num : num;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // namespace Safe
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#endif // SAFE_OP_HPP_
|