Blame src/safe_op.hpp

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_