Home | History | Annotate | Download | only in source
      1 #include "dng_safe_arithmetic.h"
      2 
      3 #include <cmath>
      4 #include <limits>
      5 
      6 #include "dng_exceptions.h"
      7 
      8 // Implementation of safe integer arithmetic follows guidelines from
      9 // https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
     10 // and
     11 // https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
     12 
     13 namespace {
     14 
     15 // Template functions for safe arithmetic. These functions are not exposed in
     16 // the header for the time being to avoid having to add checks for the various
     17 // constraints on the template argument (e.g. that it is integral and possibly
     18 // signed or unsigned only). This should be done using a static_assert(), but
     19 // we want to be portable to pre-C++11 compilers.
     20 
     21 // Returns the result of adding arg1 and arg2 if it will fit in a T (where T is
     22 // a signed or unsigned integer type). Otherwise, throws a dng_exception with
     23 // error code dng_error_unknown.
     24 template <class T>
     25 T SafeAdd(T arg1, T arg2) {
     26   // The condition is reformulated relative to the version on
     27   // www.securecoding.cert.org to check for valid instead of invalid cases. It
     28   // seems safer to enumerate the valid cases (and potentially miss one) than
     29   // enumerate the invalid cases.
     30   // If T is an unsigned type, the second half of the condition always evaluates
     31   // to false and will presumably be compiled out by the compiler.
     32   if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) ||
     33       (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) {
     34     return arg1 + arg2;
     35   } else {
     36     ThrowProgramError("Arithmetic overflow");
     37     abort();  // Never reached.
     38   }
     39 }
     40 
     41 // Returns the result of multiplying arg1 and arg2 if it will fit in a T (where
     42 // T is an unsigned integer type). Otherwise, throws a dng_exception with error
     43 // code dng_error_unknown.
     44 template <class T>
     45 T SafeUnsignedMult(T arg1, T arg2) {
     46   if (arg1 == 0 || arg2 <= std::numeric_limits<T>::max() / arg1) {
     47     return arg1 * arg2;
     48   } else {
     49     ThrowProgramError("Arithmetic overflow");
     50     abort();  // Never reached.
     51   }
     52 }
     53 
     54 }  // namespace
     55 
     56 bool SafeInt32Add(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) {
     57   try {
     58     *result = SafeInt32Add(arg1, arg2);
     59     return true;
     60   } catch (const dng_exception &) {
     61     return false;
     62   }
     63 }
     64 
     65 std::int32_t SafeInt32Add(std::int32_t arg1, std::int32_t arg2) {
     66   return SafeAdd<std::int32_t>(arg1, arg2);
     67 }
     68 
     69 std::int64_t SafeInt64Add(std::int64_t arg1, std::int64_t arg2) {
     70   return SafeAdd<std::int64_t>(arg1, arg2);
     71 }
     72 
     73 bool SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2,
     74                    std::uint32_t *result) {
     75   try {
     76     *result = SafeUint32Add(arg1, arg2);
     77     return true;
     78   } catch (const dng_exception &) {
     79     return false;
     80   }
     81 }
     82 
     83 std::uint32_t SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2) {
     84   return SafeAdd<std::uint32_t>(arg1, arg2);
     85 }
     86 
     87 std::uint64_t SafeUint64Add(std::uint64_t arg1, std::uint64_t arg2) {
     88   return SafeAdd<std::uint64_t>(arg1, arg2);
     89 }
     90 
     91 bool SafeInt32Sub(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) {
     92   if ((arg2 >= 0 && arg1 >= std::numeric_limits<int32_t>::min() + arg2) ||
     93       (arg2 < 0 && arg1 <= std::numeric_limits<int32_t>::max() + arg2)) {
     94     *result = arg1 - arg2;
     95     return true;
     96   } else {
     97     return false;
     98   }
     99 }
    100 
    101 std::int32_t SafeInt32Sub(std::int32_t arg1, std::int32_t arg2) {
    102   std::int32_t result = 0;
    103 
    104   if (!SafeInt32Sub(arg1, arg2, &result)) {
    105     ThrowProgramError("Arithmetic overflow");
    106   }
    107 
    108   return result;
    109 }
    110 
    111 std::uint32_t SafeUint32Sub(std::uint32_t arg1, std::uint32_t arg2) {
    112   if (arg1 >= arg2) {
    113     return arg1 - arg2;
    114   } else {
    115     ThrowProgramError("Arithmetic overflow");
    116     abort();  // Never reached.
    117   }
    118 }
    119 
    120 bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
    121                     std::uint32_t *result) {
    122   try {
    123     *result = SafeUint32Mult(arg1, arg2);
    124     return true;
    125   } catch (const dng_exception &) {
    126     return false;
    127   }
    128 }
    129 
    130 bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3,
    131                     std::uint32_t *result) {
    132   try {
    133     *result = SafeUint32Mult(arg1, arg2, arg3);
    134     return true;
    135   } catch (const dng_exception &) {
    136     return false;
    137   }
    138 }
    139 
    140 bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3,
    141                     std::uint32_t arg4, std::uint32_t *result) {
    142   try {
    143     *result = SafeUint32Mult(arg1, arg2, arg3, arg4);
    144     return true;
    145   } catch (const dng_exception &) {
    146     return false;
    147   }
    148 }
    149 
    150 std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2) {
    151   return SafeUnsignedMult<std::uint32_t>(arg1, arg2);
    152 }
    153 
    154 std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
    155                              std::uint32_t arg3) {
    156   return SafeUint32Mult(SafeUint32Mult(arg1, arg2), arg3);
    157 }
    158 
    159 std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
    160                              std::uint32_t arg3, std::uint32_t arg4) {
    161   return SafeUint32Mult(SafeUint32Mult(arg1, arg2, arg3), arg4);
    162 }
    163 
    164 std::int32_t SafeInt32Mult(std::int32_t arg1, std::int32_t arg2) {
    165   const std::int64_t tmp =
    166       static_cast<std::int64_t>(arg1) * static_cast<std::int64_t>(arg2);
    167   if (tmp >= std::numeric_limits<std::int32_t>::min() &&
    168       tmp <= std::numeric_limits<std::int32_t>::max()) {
    169     return static_cast<std::int32_t>(tmp);
    170   } else {
    171     ThrowProgramError("Arithmetic overflow");
    172     abort();
    173   }
    174 }
    175 
    176 std::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2) {
    177   return SafeUnsignedMult<std::size_t>(arg1, arg2);
    178 }
    179 
    180 namespace dng_internal {
    181 
    182 std::int64_t SafeInt64MultSlow(std::int64_t arg1, std::int64_t arg2) {
    183   bool overflow = true;
    184 
    185   if (arg1 > 0) {
    186     if (arg2 > 0) {
    187       overflow = (arg1 > std::numeric_limits<std::int64_t>::max() / arg2);
    188     } else {
    189       overflow = (arg2 < std::numeric_limits<std::int64_t>::min() / arg1);
    190     }
    191   } else {
    192     if (arg2 > 0) {
    193       overflow = (arg1 < std::numeric_limits<std::int64_t>::min() / arg2);
    194     } else {
    195       overflow = (arg1 != 0 &&
    196                   arg2 < std::numeric_limits<std::int64_t>::max() / arg1);
    197     }
    198   }
    199 
    200   if (overflow) {
    201     ThrowProgramError("Arithmetic overflow");
    202     abort();  // Never reached.
    203   } else {
    204     return arg1 * arg2;
    205   }
    206 }
    207 
    208 }  // namespace dng_internal
    209 
    210 std::uint32_t SafeUint32DivideUp(std::uint32_t arg1, std::uint32_t arg2) {
    211   // It might seem more intuitive to implement this function simply as
    212   //
    213   //   return arg2 == 0 ? 0 : (arg1 + arg2 - 1) / arg2;
    214   //
    215   // but the expression "arg1 + arg2" can wrap around.
    216 
    217   if (arg2 == 0) {
    218     ThrowProgramError("Division by zero");
    219     abort();  // Never reached.
    220   } else if (arg1 == 0) {
    221     // If arg1 is zero, return zero to avoid wraparound in the expression
    222     //   "arg1 - 1" below.
    223     return 0;
    224   } else {
    225     return (arg1 - 1) / arg2 + 1;
    226   }
    227 }
    228 
    229 bool RoundUpUint32ToMultiple(std::uint32_t val, std::uint32_t multiple_of,
    230                              std::uint32_t *result) {
    231   try {
    232     *result = RoundUpUint32ToMultiple(val, multiple_of);
    233     return true;
    234   } catch (const dng_exception &) {
    235     return false;
    236   }
    237 }
    238 
    239 std::uint32_t RoundUpUint32ToMultiple(std::uint32_t val,
    240                                       std::uint32_t multiple_of) {
    241   if (multiple_of == 0) {
    242     ThrowProgramError("multiple_of is zero in RoundUpUint32ToMultiple");
    243   }
    244 
    245   const std::uint32_t remainder = val % multiple_of;
    246   if (remainder == 0) {
    247     return val;
    248   } else {
    249     return SafeUint32Add(val, multiple_of - remainder);
    250   }
    251 }
    252 
    253 bool ConvertUint32ToInt32(std::uint32_t val, std::int32_t *result) {
    254   try {
    255     *result = ConvertUint32ToInt32(val);
    256     return true;
    257   } catch (const dng_exception &) {
    258     return false;
    259   }
    260 }
    261 
    262 std::int32_t ConvertUint32ToInt32(std::uint32_t val) {
    263   const std::uint32_t kInt32MaxAsUint32 =
    264       static_cast<std::uint32_t>(std::numeric_limits<std::int32_t>::max());
    265 
    266   if (val <= kInt32MaxAsUint32) {
    267     return static_cast<std::int32_t>(val);
    268   } else {
    269     ThrowProgramError("Arithmetic overflow");
    270     abort();  // Never reached.
    271   }
    272 }
    273 
    274 std::int32_t ConvertDoubleToInt32(double val) {
    275   const double kMin =
    276       static_cast<double>(std::numeric_limits<std::int32_t>::min());
    277   const double kMax =
    278       static_cast<double>(std::numeric_limits<std::int32_t>::max());
    279   // NaNs will fail this test; they always compare false.
    280   if (val > kMin - 1.0 && val < kMax + 1.0) {
    281     return static_cast<std::int32_t>(val);
    282   } else {
    283     ThrowProgramError("Argument not in range in ConvertDoubleToInt32");
    284     abort();  // Never reached.
    285   }
    286 }
    287 
    288 std::uint32_t ConvertDoubleToUint32(double val) {
    289   const double kMax =
    290       static_cast<double>(std::numeric_limits<std::uint32_t>::max());
    291   // NaNs will fail this test; they always compare false.
    292   if (val >= 0.0 && val < kMax + 1.0) {
    293     return static_cast<std::uint32_t>(val);
    294   } else {
    295     ThrowProgramError("Argument not in range in ConvertDoubleToUint32");
    296     abort();  // Never reached.
    297   }
    298 }
    299 
    300 float ConvertDoubleToFloat(double val) {
    301   const double kMax = std::numeric_limits<float>::max();
    302   if (val > kMax) {
    303     return std::numeric_limits<float>::infinity();
    304   } else if (val < -kMax) {
    305     return -std::numeric_limits<float>::infinity();
    306   } else {
    307     // The cases that end up here are:
    308     // - values in [-kMax, kMax]
    309     // - NaN (because it always compares false)
    310     return static_cast<float>(val);
    311   }
    312 }
    313