/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 | 17.9k | { |
70 | | // From and To are both unsigned. |
71 | 17.9k | if (i > std::numeric_limits<To>::max()) { |
72 | 0 | error(i); |
73 | 0 | } |
74 | 17.9k | return static_cast<To>(i); |
75 | 17.9k | } QIntC::IntConverter<unsigned long, unsigned int, false, false>::convert(unsigned long const&) Line | Count | Source | 69 | 10.9k | { | 70 | | // From and To are both unsigned. | 71 | 10.9k | if (i > std::numeric_limits<To>::max()) { | 72 | 0 | error(i); | 73 | 0 | } | 74 | 10.9k | return static_cast<To>(i); | 75 | 10.9k | } |
QIntC::IntConverter<unsigned int, unsigned long, false, false>::convert(unsigned int const&) Line | Count | Source | 69 | 1.97k | { | 70 | | // From and To are both unsigned. | 71 | 1.97k | if (i > std::numeric_limits<To>::max()) { | 72 | 0 | error(i); | 73 | 0 | } | 74 | 1.97k | return static_cast<To>(i); | 75 | 1.97k | } |
QIntC::IntConverter<unsigned long, unsigned long, false, false>::convert(unsigned long const&) Line | Count | Source | 69 | 4.99k | { | 70 | | // From and To are both unsigned. | 71 | 4.99k | if (i > std::numeric_limits<To>::max()) { | 72 | 0 | error(i); | 73 | 0 | } | 74 | 4.99k | return static_cast<To>(i); | 75 | 4.99k | } |
Unexecuted instantiation: QIntC::IntConverter<unsigned long long, unsigned int, false, false>::convert(unsigned long long const&) Unexecuted instantiation: QIntC::IntConverter<unsigned char, unsigned long, false, false>::convert(unsigned char const&) Unexecuted instantiation: QIntC::IntConverter<unsigned long long, unsigned long, false, false>::convert(unsigned long long const&) |
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 | } Unexecuted instantiation: QIntC::IntConverter<unsigned long, unsigned int, false, false>::error(unsigned long) Unexecuted instantiation: QIntC::IntConverter<unsigned int, unsigned long, false, false>::error(unsigned int) Unexecuted instantiation: QIntC::IntConverter<unsigned long, unsigned long, false, false>::error(unsigned long) Unexecuted instantiation: QIntC::IntConverter<unsigned long long, unsigned int, false, false>::error(unsigned long long) Unexecuted instantiation: QIntC::IntConverter<unsigned char, unsigned long, false, false>::error(unsigned char) Unexecuted instantiation: QIntC::IntConverter<unsigned long long, unsigned long, false, false>::error(unsigned long long) |
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 | 0 | { |
95 | | // From and To are both signed. |
96 | 0 | if ((i < std::numeric_limits<To>::min()) || (i > std::numeric_limits<To>::max())) { |
97 | 0 | error(i); |
98 | 0 | } |
99 | 0 | return static_cast<To>(i); |
100 | 0 | } Unexecuted instantiation: QIntC::IntConverter<long long, int, true, true>::convert(long long const&) Unexecuted instantiation: QIntC::IntConverter<long long, long, true, true>::convert(long long const&) Unexecuted instantiation: QIntC::IntConverter<long, long long, true, true>::convert(long const&) |
101 | | |
102 | | static void |
103 | | error(From i) |
104 | 0 | { |
105 | 0 | std::ostringstream msg; |
106 | 0 | msg.imbue(std::locale::classic()); |
107 | 0 | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
108 | 0 | << "-byte signed type to a " << sizeof(To) << "-byte signed type"; |
109 | 0 | throw std::range_error(msg.str()); |
110 | 0 | } Unexecuted instantiation: QIntC::IntConverter<long long, int, true, true>::error(long long) Unexecuted instantiation: QIntC::IntConverter<long long, long, true, true>::error(long long) Unexecuted instantiation: QIntC::IntConverter<long, long long, true, true>::error(long) |
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 | 0 | { |
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 | 0 | auto ii = static_cast<typename to_u<From>::type>(i); |
124 | 0 | if ((i < 0) || (ii > std::numeric_limits<To>::max())) { |
125 | 0 | error(i); |
126 | 0 | } |
127 | 0 | return static_cast<To>(i); |
128 | 0 | } Unexecuted instantiation: QIntC::IntConverter<char, unsigned long, true, false>::convert(char const&) Unexecuted instantiation: QIntC::IntConverter<int, unsigned long, true, false>::convert(int const&) Unexecuted instantiation: QIntC::IntConverter<int, unsigned char, true, false>::convert(int const&) Unexecuted instantiation: QIntC::IntConverter<long long, unsigned long, true, false>::convert(long long const&) Unexecuted instantiation: QIntC::IntConverter<long, unsigned long, true, false>::convert(long const&) Unexecuted instantiation: QIntC::IntConverter<int, unsigned short, true, false>::convert(int const&) |
129 | | |
130 | | static void |
131 | | error(From i) |
132 | 0 | { |
133 | 0 | std::ostringstream msg; |
134 | 0 | msg.imbue(std::locale::classic()); |
135 | 0 | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
136 | 0 | << "-byte signed type to a " << sizeof(To) << "-byte unsigned type"; |
137 | 0 | throw std::range_error(msg.str()); |
138 | 0 | } Unexecuted instantiation: QIntC::IntConverter<char, unsigned long, true, false>::error(char) Unexecuted instantiation: QIntC::IntConverter<int, unsigned long, true, false>::error(int) Unexecuted instantiation: QIntC::IntConverter<int, unsigned char, true, false>::error(int) Unexecuted instantiation: QIntC::IntConverter<long long, unsigned long, true, false>::error(long long) Unexecuted instantiation: QIntC::IntConverter<long, unsigned long, true, false>::error(long) Unexecuted instantiation: QIntC::IntConverter<int, unsigned short, true, false>::error(int) |
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 | 0 | { |
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 | 0 | auto maxval = static_cast<typename to_u<To>::type>(std::numeric_limits<To>::max()); |
151 | 0 | if (i > maxval) { |
152 | 0 | error(i); |
153 | 0 | } |
154 | 0 | return static_cast<To>(i); |
155 | 0 | } |
156 | | |
157 | | static void |
158 | | error(From i) |
159 | 0 | { |
160 | 0 | std::ostringstream msg; |
161 | 0 | msg.imbue(std::locale::classic()); |
162 | 0 | msg << "integer out of range converting " << i << " from a " << sizeof(From) |
163 | 0 | << "-byte unsigned type to a " << sizeof(To) << "-byte signed type"; |
164 | 0 | throw std::range_error(msg.str()); |
165 | 0 | } |
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 | 0 | { |
181 | 0 | return IntConverter<T, unsigned char>::convert(i); |
182 | 0 | } |
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 | 0 | { |
195 | 0 | return IntConverter<T, unsigned short>::convert(i); |
196 | 0 | } |
197 | | |
198 | | template <typename T> |
199 | | inline int |
200 | | to_int(T const& i) |
201 | 0 | { |
202 | 0 | return IntConverter<T, int>::convert(i); |
203 | 0 | } Unexecuted instantiation: int QIntC::to_int<unsigned long>(unsigned long const&) Unexecuted instantiation: int QIntC::to_int<long long>(long long const&) |
204 | | |
205 | | template <typename T> |
206 | | inline unsigned int |
207 | | to_uint(T const& i) |
208 | 10.9k | { |
209 | 10.9k | return IntConverter<T, unsigned int>::convert(i); |
210 | 10.9k | } unsigned int QIntC::to_uint<unsigned long>(unsigned long const&) Line | Count | Source | 208 | 10.9k | { | 209 | 10.9k | return IntConverter<T, unsigned int>::convert(i); | 210 | 10.9k | } |
Unexecuted instantiation: unsigned int QIntC::to_uint<unsigned long long>(unsigned long long const&) |
211 | | |
212 | | template <typename T> |
213 | | inline size_t |
214 | | to_size(T const& i) |
215 | 1.97k | { |
216 | 1.97k | return IntConverter<T, size_t>::convert(i); |
217 | 1.97k | } unsigned long QIntC::to_size<unsigned int>(unsigned int const&) Line | Count | Source | 215 | 1.97k | { | 216 | 1.97k | return IntConverter<T, size_t>::convert(i); | 217 | 1.97k | } |
Unexecuted instantiation: unsigned long QIntC::to_size<int>(int const&) Unexecuted instantiation: unsigned long QIntC::to_size<long long>(long long const&) Unexecuted instantiation: unsigned long QIntC::to_size<long>(long const&) Unexecuted instantiation: unsigned long QIntC::to_size<unsigned long long>(unsigned long long const&) |
218 | | |
219 | | template <typename T> |
220 | | inline qpdf_offset_t |
221 | | to_offset(T const& i) |
222 | 0 | { |
223 | 0 | return IntConverter<T, qpdf_offset_t>::convert(i); |
224 | 0 | } |
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 | 4.99k | { |
237 | 4.99k | return IntConverter<T, unsigned long>::convert(i); |
238 | 4.99k | } unsigned long QIntC::to_ulong<unsigned long>(unsigned long const&) Line | Count | Source | 236 | 4.99k | { | 237 | 4.99k | return IntConverter<T, unsigned long>::convert(i); | 238 | 4.99k | } |
Unexecuted instantiation: unsigned long QIntC::to_ulong<char>(char const&) Unexecuted instantiation: unsigned long QIntC::to_ulong<unsigned char>(unsigned char const&) Unexecuted instantiation: unsigned long QIntC::to_ulong<int>(int const&) |
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 |