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
13namespace {
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.
24template <class T>
25T 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.
44template <class T>
45T 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
56bool 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
65std::int32_t SafeInt32Add(std::int32_t arg1, std::int32_t arg2) {
66 return SafeAdd<std::int32_t>(arg1, arg2);
67}
68
69std::int64_t SafeInt64Add(std::int64_t arg1, std::int64_t arg2) {
70 return SafeAdd<std::int64_t>(arg1, arg2);
71}
72
73bool 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
83std::uint32_t SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2) {
84 return SafeAdd<std::uint32_t>(arg1, arg2);
85}
86
87std::uint64_t SafeUint64Add(std::uint64_t arg1, std::uint64_t arg2) {
88 return SafeAdd<std::uint64_t>(arg1, arg2);
89}
90
91bool 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
101std::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
111std::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
120bool 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
130bool 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
140bool 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
150std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2) {
151 return SafeUnsignedMult<std::uint32_t>(arg1, arg2);
152}
153
154std::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
159std::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
164std::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
176std::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2) {
177 return SafeUnsignedMult<std::size_t>(arg1, arg2);
178}
179
180namespace dng_internal {
181
182std::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
210std::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
229bool 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
239std::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
253bool 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
262std::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
274std::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
288std::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
300float 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