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