1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #pragma once
11 
12 #include <sal/config.h>
13 
14 #include <algorithm>
15 #include <cassert>
16 #include <limits>
17 #include <type_traits>
18 
19 #if defined(_MSC_VER)
20 #include <safeint.h>
21 #else
22 #ifndef __has_builtin
23 #   define __has_builtin(x) 0
24 #endif
25 #endif
26 
27 namespace o3tl
28 {
29 
30 template<typename T> inline
saturating_add(T a,T b)31 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_add(
32     T a, T b)
33 {
34     if (b >= 0) {
35         if (a <= std::numeric_limits<T>::max() - b) {
36             return a + b;
37         } else {
38             return std::numeric_limits<T>::max();
39         }
40     } else {
41         if (a >= std::numeric_limits<T>::min() - b) {
42             return a + b;
43         } else {
44             return std::numeric_limits<T>::min();
45         }
46     }
47 }
48 
49 template<typename T> inline
saturating_add(T a,T b)50 typename std::enable_if<std::is_unsigned<T>::value, T>::type saturating_add(
51     T a, T b)
52 {
53     if (a <= std::numeric_limits<T>::max() - b) {
54         return a + b;
55     } else {
56         return std::numeric_limits<T>::max();
57     }
58 }
59 
60 template<typename T> inline
saturating_sub(T a,T b)61 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_sub(
62     T a, T b)
63 {
64     if (b >= 0) {
65         if (a >= std::numeric_limits<T>::min() + b) {
66             return a - b;
67         } else {
68             return std::numeric_limits<T>::min();
69         }
70     } else {
71         if (a <= std::numeric_limits<T>::max() + b) {
72             return a - b;
73         } else {
74             return std::numeric_limits<T>::max();
75         }
76     }
77 }
78 
79 template<typename T> inline
saturating_sub(T a,T b)80 typename std::enable_if<std::is_unsigned<T>::value, T>::type saturating_sub(
81     T a, T b)
82 {
83     if (a >= std::numeric_limits<T>::min() + b) {
84         return a - b;
85     } else {
86         return std::numeric_limits<T>::min();
87     }
88 }
89 
90 template<typename T> inline
saturating_toggle_sign(T a)91 typename std::enable_if<std::is_signed<T>::value, T>::type saturating_toggle_sign(
92     T a)
93 {
94     if (a == std::numeric_limits<T>::min())
95         return std::numeric_limits<T>::max();
96     return a * -1;
97 }
98 
99 #if defined(_MSC_VER)
100 
checked_multiply(T a,T b,T & result)101 template<typename T> inline bool checked_multiply(T a, T b, T& result)
102 {
103     return !msl::utilities::SafeMultiply(a, b, result);
104 }
105 
checked_add(T a,T b,T & result)106 template<typename T> inline bool checked_add(T a, T b, T& result)
107 {
108     return !msl::utilities::SafeAdd(a, b, result);
109 }
110 
checked_sub(T a,T b,T & result)111 template<typename T> inline bool checked_sub(T a, T b, T& result)
112 {
113     return !msl::utilities::SafeSubtract(a, b, result);
114 }
115 
116 #elif (defined __GNUC__ && !defined __clang__) || (__has_builtin(__builtin_mul_overflow) && !(defined ANDROID && defined __clang__) && !(defined(__clang__) && (defined(__arm__) || defined(__i386__))))
117 // 32-bit clang fails with undefined reference to `__mulodi4'
118 
checked_multiply(T a,T b,T & result)119 template<typename T> inline bool checked_multiply(T a, T b, T& result)
120 {
121     return __builtin_mul_overflow(a, b, &result);
122 }
123 
checked_add(T a,T b,T & result)124 template<typename T> inline bool checked_add(T a, T b, T& result)
125 {
126     return __builtin_add_overflow(a, b, &result);
127 }
128 
checked_sub(T a,T b,T & result)129 template<typename T> inline bool checked_sub(T a, T b, T& result)
130 {
131     return __builtin_sub_overflow(a, b, &result);
132 }
133 
134 #else
135 
136 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_multiply(T a,T b,T & result)137 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_multiply(T a, T b, T& result)
138 {
139   if (a > 0) {  /* a is positive */
140     if (b > 0) {  /* a and b are positive */
141       if (a > (std::numeric_limits<T>::max() / b)) {
142         return true; /* Handle error */
143       }
144     } else { /* a positive, b nonpositive */
145       if (b < (std::numeric_limits<T>::min() / a)) {
146         return true; /* Handle error */
147       }
148     } /* a positive, b nonpositive */
149   } else { /* a is nonpositive */
150     if (b > 0) { /* a is nonpositive, b is positive */
151       if (a < (std::numeric_limits<T>::min() / b)) {
152         return true; /* Handle error */
153       }
154     } else { /* a and b are nonpositive */
155       if ( (a != 0) && (b < (std::numeric_limits<T>::max() / a))) {
156         return true; /* Handle error */
157       }
158     } /* End if a and b are nonpositive */
159   } /* End if a is nonpositive */
160 
161   result = a * b;
162 
163   return false;
164 }
165 
166 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_multiply(T a,T b,T & result)167 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_multiply(T a, T b, T& result)
168 {
169     if (b && a > std::numeric_limits<T>::max() / b) {
170         return true;/* Handle error */
171     }
172 
173     result = a * b;
174 
175     return false;
176 }
177 
178 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_add(T a,T b,T & result)179 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_add(T a, T b, T& result)
180 {
181     if (((b > 0) && (a > (std::numeric_limits<T>::max() - b))) ||
182         ((b < 0) && (a < (std::numeric_limits<T>::min() - b)))) {
183         return true;
184     }
185 
186     result = a + b;
187 
188     return false;
189 }
190 
191 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_add(T a,T b,T & result)192 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_add(T a, T b, T& result)
193 {
194     if (std::numeric_limits<T>::max() - a < b) {
195         return true;/* Handle error */
196     }
197 
198     result = a + b;
199 
200     return false;
201 }
202 
203 //https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
checked_sub(T a,T b,T & result)204 template<typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type checked_sub(T a, T b, T& result)
205 {
206     if ((b > 0 && a < std::numeric_limits<T>::min() + b) ||
207         (b < 0 && a > std::numeric_limits<T>::max() + b)) {
208         return true;
209     }
210 
211     result = a - b;
212 
213     return false;
214 }
215 
216 //https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
checked_sub(T a,T b,T & result)217 template<typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type checked_sub(T a, T b, T& result)
218 {
219     if (a < b) {
220         return true;
221     }
222 
223     result = a - b;
224 
225     return false;
226 }
227 
228 #endif
229 
230 template<typename T> constexpr std::enable_if_t<std::is_signed_v<T>, std::make_unsigned_t<T>>
make_unsigned(T value)231 make_unsigned(T value)
232 {
233     assert(value >= 0);
234     return value;
235 }
236 
237 // An implicit conversion from T2 to T1, useful in places where an explicit conversion from T2 to
238 // T1 is needed (e.g., in list initialization, if the implicit conversion would be narrowing) but
239 // tools like -fsanitize=implicit-conversion should still be able to detect truncation:
narrowing(T2 value)240 template<typename T1, typename T2> constexpr T1 narrowing(T2 value) { return value; }
241 
242 // std::min wrapped to inform coverity that the result is now deemed sanitized
243 // coverity[ -taint_source ]
sanitizing_min(T a,T b)244 template<typename T> [[nodiscard]] inline T sanitizing_min(T a, T b)
245 {
246     return std::min(a, b);
247 }
248 
249 }
250 
251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
252