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