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