/src/dng_sdk/source/dng_safe_arithmetic.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 890M | 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 | 890M | if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) || |
33 | 890M | (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) { |
34 | 890M | return arg1 + arg2; |
35 | 890M | } else { |
36 | 1.33k | ThrowProgramError("Arithmetic overflow"); |
37 | 1.33k | abort(); // Never reached. |
38 | 1.33k | } |
39 | 890M | } dng_safe_arithmetic.cpp:int (anonymous namespace)::SafeAdd<int>(int, int) Line | Count | Source | 25 | 5.46M | 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 | 5.46M | if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) || | 33 | 5.46M | (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) { | 34 | 5.46M | return arg1 + arg2; | 35 | 5.46M | } else { | 36 | 0 | ThrowProgramError("Arithmetic overflow"); | 37 | 0 | abort(); // Never reached. | 38 | 0 | } | 39 | 5.46M | } |
dng_safe_arithmetic.cpp:long (anonymous namespace)::SafeAdd<long>(long, long) Line | Count | Source | 25 | 879M | 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 | 879M | if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) || | 33 | 879M | (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) { | 34 | 879M | return arg1 + arg2; | 35 | 879M | } else { | 36 | 0 | ThrowProgramError("Arithmetic overflow"); | 37 | 0 | abort(); // Never reached. | 38 | 0 | } | 39 | 879M | } |
dng_safe_arithmetic.cpp:unsigned int (anonymous namespace)::SafeAdd<unsigned int>(unsigned int, unsigned int) Line | Count | Source | 25 | 5.70M | 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 | 5.70M | if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) || | 33 | 5.70M | (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) { | 34 | 5.69M | return arg1 + arg2; | 35 | 5.69M | } else { | 36 | 1.32k | ThrowProgramError("Arithmetic overflow"); | 37 | 1.32k | abort(); // Never reached. | 38 | 1.32k | } | 39 | 5.70M | } |
dng_safe_arithmetic.cpp:unsigned long (anonymous namespace)::SafeAdd<unsigned long>(unsigned long, unsigned long) Line | Count | Source | 25 | 177k | 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 | 177k | if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) || | 33 | 177k | (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) { | 34 | 177k | return arg1 + arg2; | 35 | 177k | } else { | 36 | 14 | ThrowProgramError("Arithmetic overflow"); | 37 | 14 | abort(); // Never reached. | 38 | 14 | } | 39 | 177k | } |
|
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 | 51.6M | T SafeUnsignedMult(T arg1, T arg2) { |
46 | 51.6M | if (arg1 == 0 || arg2 <= std::numeric_limits<T>::max() / arg1) { |
47 | 51.6M | return arg1 * arg2; |
48 | 51.6M | } else { |
49 | 15.1k | ThrowProgramError("Arithmetic overflow"); |
50 | 15.1k | abort(); // Never reached. |
51 | 15.1k | } |
52 | 51.6M | } dng_safe_arithmetic.cpp:unsigned int (anonymous namespace)::SafeUnsignedMult<unsigned int>(unsigned int, unsigned int) Line | Count | Source | 45 | 51.4M | T SafeUnsignedMult(T arg1, T arg2) { | 46 | 51.4M | if (arg1 == 0 || arg2 <= std::numeric_limits<T>::max() / arg1) { | 47 | 51.4M | return arg1 * arg2; | 48 | 51.4M | } else { | 49 | 15.1k | ThrowProgramError("Arithmetic overflow"); | 50 | 15.1k | abort(); // Never reached. | 51 | 15.1k | } | 52 | 51.4M | } |
dng_safe_arithmetic.cpp:unsigned long (anonymous namespace)::SafeUnsignedMult<unsigned long>(unsigned long, unsigned long) Line | Count | Source | 45 | 192k | T SafeUnsignedMult(T arg1, T arg2) { | 46 | 192k | if (arg1 == 0 || arg2 <= std::numeric_limits<T>::max() / arg1) { | 47 | 192k | return arg1 * arg2; | 48 | 192k | } else { | 49 | 0 | ThrowProgramError("Arithmetic overflow"); | 50 | 0 | abort(); // Never reached. | 51 | 0 | } | 52 | 192k | } |
|
53 | | |
54 | | } // namespace |
55 | | |
56 | 0 | bool SafeInt32Add(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) { |
57 | 0 | try { |
58 | 0 | *result = SafeInt32Add(arg1, arg2); |
59 | 0 | return true; |
60 | 0 | } catch (const dng_exception &) { |
61 | 0 | return false; |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | 5.46M | std::int32_t SafeInt32Add(std::int32_t arg1, std::int32_t arg2) { |
66 | 5.46M | return SafeAdd<std::int32_t>(arg1, arg2); |
67 | 5.46M | } |
68 | | |
69 | 879M | std::int64_t SafeInt64Add(std::int64_t arg1, std::int64_t arg2) { |
70 | 879M | return SafeAdd<std::int64_t>(arg1, arg2); |
71 | 879M | } |
72 | | |
73 | | bool SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2, |
74 | 456k | std::uint32_t *result) { |
75 | 456k | try { |
76 | 456k | *result = SafeUint32Add(arg1, arg2); |
77 | 456k | return true; |
78 | 456k | } catch (const dng_exception &) { |
79 | 571 | return false; |
80 | 571 | } |
81 | 456k | } |
82 | | |
83 | 5.70M | std::uint32_t SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2) { |
84 | 5.70M | return SafeAdd<std::uint32_t>(arg1, arg2); |
85 | 5.70M | } |
86 | | |
87 | 177k | std::uint64_t SafeUint64Add(std::uint64_t arg1, std::uint64_t arg2) { |
88 | 177k | return SafeAdd<std::uint64_t>(arg1, arg2); |
89 | 177k | } |
90 | | |
91 | 59.4M | bool SafeInt32Sub(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) { |
92 | 59.4M | if ((arg2 >= 0 && arg1 >= std::numeric_limits<int32_t>::min() + arg2) || |
93 | 59.4M | (arg2 < 0 && arg1 <= std::numeric_limits<int32_t>::max() + arg2)) { |
94 | 59.4M | *result = arg1 - arg2; |
95 | 59.4M | return true; |
96 | 59.4M | } else { |
97 | 143 | return false; |
98 | 143 | } |
99 | 59.4M | } |
100 | | |
101 | 1.82M | std::int32_t SafeInt32Sub(std::int32_t arg1, std::int32_t arg2) { |
102 | 1.82M | std::int32_t result = 0; |
103 | | |
104 | 1.82M | if (!SafeInt32Sub(arg1, arg2, &result)) { |
105 | 0 | ThrowProgramError("Arithmetic overflow"); |
106 | 0 | } |
107 | | |
108 | 1.82M | return result; |
109 | 1.82M | } |
110 | | |
111 | 1.27M | std::uint32_t SafeUint32Sub(std::uint32_t arg1, std::uint32_t arg2) { |
112 | 1.27M | if (arg1 >= arg2) { |
113 | 1.27M | return arg1 - arg2; |
114 | 1.27M | } else { |
115 | 848 | ThrowProgramError("Arithmetic overflow"); |
116 | 848 | abort(); // Never reached. |
117 | 848 | } |
118 | 1.27M | } |
119 | | |
120 | | bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, |
121 | 4.71M | std::uint32_t *result) { |
122 | 4.71M | try { |
123 | 4.71M | *result = SafeUint32Mult(arg1, arg2); |
124 | 4.71M | return true; |
125 | 4.71M | } catch (const dng_exception &) { |
126 | 408 | return false; |
127 | 408 | } |
128 | 4.71M | } |
129 | | |
130 | | bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3, |
131 | 0 | std::uint32_t *result) { |
132 | 0 | try { |
133 | 0 | *result = SafeUint32Mult(arg1, arg2, arg3); |
134 | 0 | return true; |
135 | 0 | } catch (const dng_exception &) { |
136 | 0 | return false; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | bool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3, |
141 | 0 | std::uint32_t arg4, std::uint32_t *result) { |
142 | 0 | try { |
143 | 0 | *result = SafeUint32Mult(arg1, arg2, arg3, arg4); |
144 | 0 | return true; |
145 | 0 | } catch (const dng_exception &) { |
146 | 0 | return false; |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | 51.4M | std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2) { |
151 | 51.4M | return SafeUnsignedMult<std::uint32_t>(arg1, arg2); |
152 | 51.4M | } |
153 | | |
154 | | std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, |
155 | 147k | std::uint32_t arg3) { |
156 | 147k | return SafeUint32Mult(SafeUint32Mult(arg1, arg2), arg3); |
157 | 147k | } |
158 | | |
159 | | std::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, |
160 | 79.5k | std::uint32_t arg3, std::uint32_t arg4) { |
161 | 79.5k | return SafeUint32Mult(SafeUint32Mult(arg1, arg2, arg3), arg4); |
162 | 79.5k | } |
163 | | |
164 | 2.17k | std::int32_t SafeInt32Mult(std::int32_t arg1, std::int32_t arg2) { |
165 | 2.17k | const std::int64_t tmp = |
166 | 2.17k | static_cast<std::int64_t>(arg1) * static_cast<std::int64_t>(arg2); |
167 | 2.17k | if (tmp >= std::numeric_limits<std::int32_t>::min() && |
168 | 2.17k | tmp <= std::numeric_limits<std::int32_t>::max()) { |
169 | 2.00k | return static_cast<std::int32_t>(tmp); |
170 | 2.00k | } else { |
171 | 168 | ThrowProgramError("Arithmetic overflow"); |
172 | 168 | abort(); |
173 | 168 | } |
174 | 2.17k | } |
175 | | |
176 | 192k | std::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2) { |
177 | 192k | return SafeUnsignedMult<std::size_t>(arg1, arg2); |
178 | 192k | } |
179 | | |
180 | | namespace dng_internal { |
181 | | |
182 | 0 | std::int64_t SafeInt64MultSlow(std::int64_t arg1, std::int64_t arg2) { |
183 | 0 | bool overflow = true; |
184 | |
|
185 | 0 | if (arg1 > 0) { |
186 | 0 | if (arg2 > 0) { |
187 | 0 | overflow = (arg1 > std::numeric_limits<std::int64_t>::max() / arg2); |
188 | 0 | } else { |
189 | 0 | overflow = (arg2 < std::numeric_limits<std::int64_t>::min() / arg1); |
190 | 0 | } |
191 | 0 | } else { |
192 | 0 | if (arg2 > 0) { |
193 | 0 | overflow = (arg1 < std::numeric_limits<std::int64_t>::min() / arg2); |
194 | 0 | } else { |
195 | 0 | overflow = (arg1 != 0 && |
196 | 0 | arg2 < std::numeric_limits<std::int64_t>::max() / arg1); |
197 | 0 | } |
198 | 0 | } |
199 | |
|
200 | 0 | if (overflow) { |
201 | 0 | ThrowProgramError("Arithmetic overflow"); |
202 | 0 | abort(); // Never reached. |
203 | 0 | } else { |
204 | 0 | return arg1 * arg2; |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | } // namespace dng_internal |
209 | | |
210 | 200k | 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 | 200k | if (arg2 == 0) { |
218 | 0 | ThrowProgramError("Division by zero"); |
219 | 0 | abort(); // Never reached. |
220 | 200k | } else if (arg1 == 0) { |
221 | | // If arg1 is zero, return zero to avoid wraparound in the expression |
222 | | // "arg1 - 1" below. |
223 | 255 | return 0; |
224 | 199k | } else { |
225 | 199k | return (arg1 - 1) / arg2 + 1; |
226 | 199k | } |
227 | 200k | } |
228 | | |
229 | | bool RoundUpUint32ToMultiple(std::uint32_t val, std::uint32_t multiple_of, |
230 | 3.64M | std::uint32_t *result) { |
231 | 3.64M | try { |
232 | 3.64M | *result = RoundUpUint32ToMultiple(val, multiple_of); |
233 | 3.64M | return true; |
234 | 3.64M | } catch (const dng_exception &) { |
235 | 0 | return false; |
236 | 0 | } |
237 | 3.64M | } |
238 | | |
239 | | std::uint32_t RoundUpUint32ToMultiple(std::uint32_t val, |
240 | 3.67M | std::uint32_t multiple_of) { |
241 | 3.67M | if (multiple_of == 0) { |
242 | 0 | ThrowProgramError("multiple_of is zero in RoundUpUint32ToMultiple"); |
243 | 0 | } |
244 | | |
245 | 3.67M | const std::uint32_t remainder = val % multiple_of; |
246 | 3.67M | if (remainder == 0) { |
247 | 2.16M | return val; |
248 | 2.16M | } else { |
249 | 1.50M | return SafeUint32Add(val, multiple_of - remainder); |
250 | 1.50M | } |
251 | 3.67M | } |
252 | | |
253 | 11.5M | bool ConvertUint32ToInt32(std::uint32_t val, std::int32_t *result) { |
254 | 11.5M | try { |
255 | 11.5M | *result = ConvertUint32ToInt32(val); |
256 | 11.5M | return true; |
257 | 11.5M | } catch (const dng_exception &) { |
258 | 4 | return false; |
259 | 4 | } |
260 | 11.5M | } |
261 | | |
262 | 13.4M | std::int32_t ConvertUint32ToInt32(std::uint32_t val) { |
263 | 13.4M | const std::uint32_t kInt32MaxAsUint32 = |
264 | 13.4M | static_cast<std::uint32_t>(std::numeric_limits<std::int32_t>::max()); |
265 | | |
266 | 13.4M | if (val <= kInt32MaxAsUint32) { |
267 | 13.4M | return static_cast<std::int32_t>(val); |
268 | 13.4M | } else { |
269 | 14 | ThrowProgramError("Arithmetic overflow"); |
270 | 14 | abort(); // Never reached. |
271 | 14 | } |
272 | 13.4M | } |
273 | | |
274 | 130k | std::int32_t ConvertDoubleToInt32(double val) { |
275 | 130k | const double kMin = |
276 | 130k | static_cast<double>(std::numeric_limits<std::int32_t>::min()); |
277 | 130k | const double kMax = |
278 | 130k | static_cast<double>(std::numeric_limits<std::int32_t>::max()); |
279 | | // NaNs will fail this test; they always compare false. |
280 | 130k | if (val > kMin - 1.0 && val < kMax + 1.0) { |
281 | 129k | return static_cast<std::int32_t>(val); |
282 | 129k | } else { |
283 | 806 | ThrowProgramError("Argument not in range in ConvertDoubleToInt32"); |
284 | 806 | abort(); // Never reached. |
285 | 806 | } |
286 | 130k | } |
287 | | |
288 | 414M | std::uint32_t ConvertDoubleToUint32(double val) { |
289 | 414M | const double kMax = |
290 | 414M | static_cast<double>(std::numeric_limits<std::uint32_t>::max()); |
291 | | // NaNs will fail this test; they always compare false. |
292 | 414M | if (val >= 0.0 && val < kMax + 1.0) { |
293 | 414M | return static_cast<std::uint32_t>(val); |
294 | 414M | } else { |
295 | 847 | ThrowProgramError("Argument not in range in ConvertDoubleToUint32"); |
296 | 847 | abort(); // Never reached. |
297 | 847 | } |
298 | 414M | } |
299 | | |
300 | 10.6k | float ConvertDoubleToFloat(double val) { |
301 | 10.6k | const double kMax = std::numeric_limits<float>::max(); |
302 | 10.6k | if (val > kMax) { |
303 | 193 | return std::numeric_limits<float>::infinity(); |
304 | 10.4k | } else if (val < -kMax) { |
305 | 387 | return -std::numeric_limits<float>::infinity(); |
306 | 10.0k | } else { |
307 | | // The cases that end up here are: |
308 | | // - values in [-kMax, kMax] |
309 | | // - NaN (because it always compares false) |
310 | 10.0k | return static_cast<float>(val); |
311 | 10.0k | } |
312 | 10.6k | } |