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 | |