• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
SafeAdd(T arg1,T arg2)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>
SafeUnsignedMult(T arg1,T arg2)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 
SafeInt32Add(std::int32_t arg1,std::int32_t arg2,std::int32_t * result)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 
SafeInt32Add(std::int32_t arg1,std::int32_t arg2)65 std::int32_t SafeInt32Add(std::int32_t arg1, std::int32_t arg2) {
66   return SafeAdd<std::int32_t>(arg1, arg2);
67 }
68 
SafeInt64Add(std::int64_t arg1,std::int64_t arg2)69 std::int64_t SafeInt64Add(std::int64_t arg1, std::int64_t arg2) {
70   return SafeAdd<std::int64_t>(arg1, arg2);
71 }
72 
SafeUint32Add(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t * result)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 
SafeUint32Add(std::uint32_t arg1,std::uint32_t arg2)83 std::uint32_t SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2) {
84   return SafeAdd<std::uint32_t>(arg1, arg2);
85 }
86 
SafeUint64Add(std::uint64_t arg1,std::uint64_t arg2)87 std::uint64_t SafeUint64Add(std::uint64_t arg1, std::uint64_t arg2) {
88   return SafeAdd<std::uint64_t>(arg1, arg2);
89 }
90 
SafeInt32Sub(std::int32_t arg1,std::int32_t arg2,std::int32_t * result)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 
SafeInt32Sub(std::int32_t arg1,std::int32_t arg2)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 
SafeUint32Sub(std::uint32_t arg1,std::uint32_t arg2)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 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t * result)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 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t arg3,std::uint32_t * result)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 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t arg3,std::uint32_t arg4,std::uint32_t * result)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 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2)150 std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2) {
151   return SafeUnsignedMult<std::uint32_t>(arg1, arg2);
152 }
153 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t arg3)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 
SafeUint32Mult(std::uint32_t arg1,std::uint32_t arg2,std::uint32_t arg3,std::uint32_t arg4)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 
SafeInt32Mult(std::int32_t arg1,std::int32_t arg2)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 
SafeSizetMult(std::size_t arg1,std::size_t arg2)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 
SafeInt64MultSlow(std::int64_t arg1,std::int64_t arg2)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 
SafeUint32DivideUp(std::uint32_t arg1,std::uint32_t arg2)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 
RoundUpUint32ToMultiple(std::uint32_t val,std::uint32_t multiple_of,std::uint32_t * result)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 
RoundUpUint32ToMultiple(std::uint32_t val,std::uint32_t multiple_of)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 
ConvertUint32ToInt32(std::uint32_t val,std::int32_t * result)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 
ConvertUint32ToInt32(std::uint32_t val)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 
ConvertDoubleToInt32(double val)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 
ConvertDoubleToUint32(double val)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 
ConvertDoubleToFloat(double val)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