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