/src/qpdf/include/qpdf/QIntC.hh
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2005-2021 Jay Berkenbilt |
2 | | // Copyright (c) 2022-2025 Jay Berkenbilt and Manfred Holger |
3 | | // |
4 | | // This file is part of qpdf. |
5 | | // |
6 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | // you may not use this file except in compliance with the License. |
8 | | // You may obtain a copy of the License at |
9 | | // |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // |
12 | | // Unless required by applicable law or agreed to in writing, software |
13 | | // distributed under the License is distributed on an "AS IS" BASIS, |
14 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | // See the License for the specific language governing permissions and |
16 | | // limitations under the License. |
17 | | // |
18 | | // Versions of qpdf prior to version 7 were released under the terms |
19 | | // of version 2.0 of the Artistic License. At your option, you may |
20 | | // continue to consider qpdf to be licensed under those terms. Please |
21 | | // see the manual for additional information. |
22 | | |
23 | | #ifndef QINTC_HH |
24 | | #define QINTC_HH |
25 | | |
26 | | #include <qpdf/DLL.h> |
27 | | #include <qpdf/Types.h> |
28 | | #include <iostream> |
29 | | #include <limits> |
30 | | #include <locale> |
31 | | #include <sstream> |
32 | | #include <stdexcept> |
33 | | #include <type_traits> |
34 | | |
35 | | // This namespace provides safe integer conversion that detects |
36 | | // overflows. It uses short, cryptic names for brevity. |
37 | | |
38 | | namespace QIntC // QIntC = qpdf Integer Conversion |
39 | | { |
40 | | // to_u is here for backward-compatibility from before we required |
41 | | // C++-11. |
42 | | template <typename T> |
43 | | class to_u |
44 | | { |
45 | | public: |
46 | | typedef typename std::make_unsigned<T>::type type; |
47 | | }; |
48 | | |
49 | | // Basic IntConverter class, which converts an integer from the |
50 | | // From class to one of the To class if it can be done safely and |
51 | | // throws a range_error otherwise. This class is specialized for |
52 | | // each permutation of signed/unsigned for the From and To |
53 | | // classes. |
54 | | template < |
55 | | typename From, |
56 | | typename To, |
57 | | bool From_signed = std::numeric_limits<From>::is_signed, |
58 | | bool To_signed = std::numeric_limits<To>::is_signed> |
59 | | class IntConverter |
60 | | { |
61 | | }; |
62 | | |
63 | | template <typename From, typename To> |
64 | | class IntConverter<From, To, false, false> |
65 | | { |
66 | | public: |
67 | | inline static To |
68 | | convert(From const& i) |
69 | 832k | { |
70 | | // From and To are both unsigned. |
71 | 832k | if (i > std::numeric_limits<To>::max()) { |
72 | 0 | error(i); |
73 | 0 | } |
74 | 832k | return static_cast<To>(i); |
75 | 832k | } |
76 | | |
77 | | static void |
78 | | error(From i) |
79 | 0 | { |
80 | 0 | std::ostringstream msg; |
81 | 0 | msg.imbue(std::locale::classic()); |
82 | 0 | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
83 | 0 | << "-byte unsigned type to a " << sizeof(To) << "-byte unsigned type"; |
84 | 0 | throw std::range_error(msg.str()); |
85 | 0 | } |
86 | | }; |
87 | | |
88 | | template <typename From, typename To> |
89 | | class IntConverter<From, To, true, true> |
90 | | { |
91 | | public: |
92 | | inline static To |
93 | | convert(From const& i) |
94 | | { |
95 | | // From and To are both signed. |
96 | | if ((i < std::numeric_limits<To>::min()) || (i > std::numeric_limits<To>::max())) { |
97 | | error(i); |
98 | | } |
99 | | return static_cast<To>(i); |
100 | | } |
101 | | |
102 | | static void |
103 | | error(From i) |
104 | | { |
105 | | std::ostringstream msg; |
106 | | msg.imbue(std::locale::classic()); |
107 | | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
108 | | << "-byte signed type to a " << sizeof(To) << "-byte signed type"; |
109 | | throw std::range_error(msg.str()); |
110 | | } |
111 | | }; |
112 | | |
113 | | template <typename From, typename To> |
114 | | class IntConverter<From, To, true, false> |
115 | | { |
116 | | public: |
117 | | inline static To |
118 | | convert(From const& i) |
119 | | { |
120 | | // From is signed, and To is unsigned. If i > 0, it's safe to |
121 | | // convert it to the corresponding unsigned type and to |
122 | | // compare with To's max. |
123 | | auto ii = static_cast<typename to_u<From>::type>(i); |
124 | | if ((i < 0) || (ii > std::numeric_limits<To>::max())) { |
125 | | error(i); |
126 | | } |
127 | | return static_cast<To>(i); |
128 | | } |
129 | | |
130 | | static void |
131 | | error(From i) |
132 | | { |
133 | | std::ostringstream msg; |
134 | | msg.imbue(std::locale::classic()); |
135 | | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
136 | | << "-byte signed type to a " << sizeof(To) << "-byte unsigned type"; |
137 | | throw std::range_error(msg.str()); |
138 | | } |
139 | | }; |
140 | | |
141 | | template <typename From, typename To> |
142 | | class IntConverter<From, To, false, true> |
143 | | { |
144 | | public: |
145 | | inline static To |
146 | | convert(From const& i) |
147 | | { |
148 | | // From is unsigned, and to is signed. Convert To's max to the |
149 | | // unsigned version of To and compare i against that. |
150 | | auto maxval = static_cast<typename to_u<To>::type>(std::numeric_limits<To>::max()); |
151 | | if (i > maxval) { |
152 | | error(i); |
153 | | } |
154 | | return static_cast<To>(i); |
155 | | } |
156 | | |
157 | | static void |
158 | | error(From i) |
159 | | { |
160 | | std::ostringstream msg; |
161 | | msg.imbue(std::locale::classic()); |
162 | | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
163 | | << "-byte unsigned type to a " << sizeof(To) << "-byte signed type"; |
164 | | throw std::range_error(msg.str()); |
165 | | } |
166 | | }; |
167 | | |
168 | | // Specific converters. The return type of each function must match |
169 | | // the second template parameter to IntConverter. |
170 | | template <typename T> |
171 | | inline char |
172 | | to_char(T const& i) |
173 | | { |
174 | | return IntConverter<T, char>::convert(i); |
175 | | } |
176 | | |
177 | | template <typename T> |
178 | | inline unsigned char |
179 | | to_uchar(T const& i) |
180 | | { |
181 | | return IntConverter<T, unsigned char>::convert(i); |
182 | | } |
183 | | |
184 | | template <typename T> |
185 | | inline short |
186 | | to_short(T const& i) |
187 | | { |
188 | | return IntConverter<T, short>::convert(i); |
189 | | } |
190 | | |
191 | | template <typename T> |
192 | | inline unsigned short |
193 | | to_ushort(T const& i) |
194 | | { |
195 | | return IntConverter<T, unsigned short>::convert(i); |
196 | | } |
197 | | |
198 | | template <typename T> |
199 | | inline int |
200 | | to_int(T const& i) |
201 | | { |
202 | | return IntConverter<T, int>::convert(i); |
203 | | } |
204 | | |
205 | | template <typename T> |
206 | | inline unsigned int |
207 | | to_uint(T const& i) |
208 | 832k | { |
209 | 832k | return IntConverter<T, unsigned int>::convert(i); |
210 | 832k | } |
211 | | |
212 | | template <typename T> |
213 | | inline size_t |
214 | | to_size(T const& i) |
215 | | { |
216 | | return IntConverter<T, size_t>::convert(i); |
217 | | } |
218 | | |
219 | | template <typename T> |
220 | | inline qpdf_offset_t |
221 | | to_offset(T const& i) |
222 | | { |
223 | | return IntConverter<T, qpdf_offset_t>::convert(i); |
224 | | } |
225 | | |
226 | | template <typename T> |
227 | | inline long |
228 | | to_long(T const& i) |
229 | | { |
230 | | return IntConverter<T, long>::convert(i); |
231 | | } |
232 | | |
233 | | template <typename T> |
234 | | inline unsigned long |
235 | | to_ulong(T const& i) |
236 | | { |
237 | | return IntConverter<T, unsigned long>::convert(i); |
238 | | } |
239 | | |
240 | | template <typename T> |
241 | | inline long long |
242 | | to_longlong(T const& i) |
243 | | { |
244 | | return IntConverter<T, long long>::convert(i); |
245 | | } |
246 | | |
247 | | template <typename T> |
248 | | inline unsigned long long |
249 | | to_ulonglong(T const& i) |
250 | | { |
251 | | return IntConverter<T, unsigned long long>::convert(i); |
252 | | } |
253 | | |
254 | | template <typename T> |
255 | | void |
256 | | range_check_error(T const& cur, T const& delta) |
257 | | { |
258 | | if ((delta > 0) && ((std::numeric_limits<T>::max() - cur) < delta)) { |
259 | | std::ostringstream msg; |
260 | | msg.imbue(std::locale::classic()); |
261 | | msg << "adding " << delta << " to " << cur << " would cause an integer overflow"; |
262 | | throw std::range_error(msg.str()); |
263 | | } else if ((delta < 0) && ((std::numeric_limits<T>::min() - cur) > delta)) { |
264 | | std::ostringstream msg; |
265 | | msg.imbue(std::locale::classic()); |
266 | | msg << "adding " << delta << " to " << cur << " would cause an integer underflow"; |
267 | | throw std::range_error(msg.str()); |
268 | | } |
269 | | } |
270 | | |
271 | | template <typename T> |
272 | | inline void |
273 | | range_check(T const& cur, T const& delta) |
274 | | { |
275 | | if ((delta > 0) != (cur > 0)) { |
276 | | return; |
277 | | } |
278 | | QIntC::range_check_error<T>(cur, delta); |
279 | | } |
280 | | |
281 | | template <typename T> |
282 | | void |
283 | | range_check_subtract_error(T const& cur, T const& delta) |
284 | | { |
285 | | if ((delta > 0) && ((std::numeric_limits<T>::min() + delta) > cur)) { |
286 | | std::ostringstream msg; |
287 | | msg.imbue(std::locale::classic()); |
288 | | msg << "subtracting " << delta << " from " << cur |
289 | | << " would cause an integer underflow"; |
290 | | throw std::range_error(msg.str()); |
291 | | } else if ((delta < 0) && ((std::numeric_limits<T>::max() + delta) < cur)) { |
292 | | std::ostringstream msg; |
293 | | msg.imbue(std::locale::classic()); |
294 | | msg << "subtracting " << delta << " from " << cur << " would cause an integer overflow"; |
295 | | throw std::range_error(msg.str()); |
296 | | } |
297 | | } |
298 | | |
299 | | template <typename T> |
300 | | inline void |
301 | | range_check_subtract(T const& cur, T const& delta) |
302 | | { |
303 | | if ((delta >= 0) == (cur >= 0)) { |
304 | | return; |
305 | | } |
306 | | QIntC::range_check_subtract_error<T>(cur, delta); |
307 | | } |
308 | | }; // namespace QIntC |
309 | | |
310 | | #endif // QINTC_HH |