2022-02-04 21:36:22 +00:00
|
|
|
// Copyright (c) 2005-2022 Jay Berkenbilt
|
2019-06-19 22:53:22 +00:00
|
|
|
//
|
|
|
|
// This file is part of qpdf.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// Versions of qpdf prior to version 7 were released under the terms
|
|
|
|
// of version 2.0 of the Artistic License. At your option, you may
|
|
|
|
// continue to consider qpdf to be licensed under those terms. Please
|
|
|
|
// see the manual for additional information.
|
|
|
|
|
|
|
|
#ifndef QINTC_HH
|
|
|
|
#define QINTC_HH
|
|
|
|
|
|
|
|
#include <qpdf/DLL.h>
|
|
|
|
#include <qpdf/Types.h>
|
2022-04-02 21:14:10 +00:00
|
|
|
#include <cassert>
|
2019-06-19 22:53:22 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <limits>
|
2020-10-21 19:29:28 +00:00
|
|
|
#include <locale>
|
2022-04-02 21:14:10 +00:00
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
2020-04-03 16:05:50 +00:00
|
|
|
#include <type_traits>
|
2019-06-19 22:53:22 +00:00
|
|
|
|
|
|
|
// This namespace provides safe integer conversion that detects
|
|
|
|
// overflows. It uses short, cryptic names for brevity.
|
|
|
|
|
|
|
|
namespace QIntC // QIntC = qpdf Integer Conversion
|
|
|
|
{
|
2020-04-03 16:05:50 +00:00
|
|
|
// to_u is here for backward-compatibility from before we required
|
|
|
|
// C++-11.
|
2019-06-19 22:53:22 +00:00
|
|
|
template <typename T>
|
|
|
|
class to_u
|
|
|
|
{
|
|
|
|
public:
|
2020-04-03 16:05:50 +00:00
|
|
|
typedef typename std::make_unsigned<T>::type type;
|
2019-06-19 22:53:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Basic IntConverter class, which converts an integer from the
|
|
|
|
// From class to one of the To class if it can be done safely and
|
|
|
|
// throws a range_error otherwise. This class is specialized for
|
|
|
|
// each permutation of signed/unsigned for the From and To
|
|
|
|
// classes.
|
2022-04-02 21:14:10 +00:00
|
|
|
template <
|
|
|
|
typename From,
|
|
|
|
typename To,
|
|
|
|
bool From_signed = std::numeric_limits<From>::is_signed,
|
|
|
|
bool To_signed = std::numeric_limits<To>::is_signed>
|
2019-06-19 22:53:22 +00:00
|
|
|
class IntConverter
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename From, typename To>
|
|
|
|
class IntConverter<From, To, false, false>
|
|
|
|
{
|
|
|
|
public:
|
2022-04-02 21:14:10 +00:00
|
|
|
static To
|
|
|
|
convert(From const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
// From and To are both unsigned.
|
2022-04-02 21:14:10 +00:00
|
|
|
if (i > std::numeric_limits<To>::max()) {
|
2019-06-19 22:53:22 +00:00
|
|
|
std::ostringstream msg;
|
2020-10-21 19:29:28 +00:00
|
|
|
msg.imbue(std::locale::classic());
|
2022-04-02 21:14:10 +00:00
|
|
|
msg << "integer out of range converting " << i << " from a "
|
|
|
|
<< sizeof(From) << "-byte unsigned type to a " << sizeof(To)
|
|
|
|
<< "-byte unsigned type";
|
2019-06-19 22:53:22 +00:00
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
|
|
|
return static_cast<To>(i);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename From, typename To>
|
|
|
|
class IntConverter<From, To, true, true>
|
|
|
|
{
|
|
|
|
public:
|
2022-04-02 21:14:10 +00:00
|
|
|
static To
|
|
|
|
convert(From const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
// From and To are both signed.
|
|
|
|
if ((i < std::numeric_limits<To>::min()) ||
|
2022-04-02 21:14:10 +00:00
|
|
|
(i > std::numeric_limits<To>::max())) {
|
2019-06-19 22:53:22 +00:00
|
|
|
std::ostringstream msg;
|
2020-10-21 19:29:28 +00:00
|
|
|
msg.imbue(std::locale::classic());
|
2022-04-02 21:14:10 +00:00
|
|
|
msg << "integer out of range converting " << i << " from a "
|
|
|
|
<< sizeof(From) << "-byte signed type to a " << sizeof(To)
|
|
|
|
<< "-byte signed type";
|
2019-06-19 22:53:22 +00:00
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
|
|
|
return static_cast<To>(i);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename From, typename To>
|
|
|
|
class IntConverter<From, To, true, false>
|
|
|
|
{
|
|
|
|
public:
|
2022-04-02 21:14:10 +00:00
|
|
|
static To
|
|
|
|
convert(From const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
// From is signed, and To is unsigned. If i > 0, it's safe to
|
|
|
|
// convert it to the corresponding unsigned type and to
|
|
|
|
// compare with To's max.
|
2020-04-03 16:05:50 +00:00
|
|
|
auto ii = static_cast<typename to_u<From>::type>(i);
|
2022-04-02 21:14:10 +00:00
|
|
|
if ((i < 0) || (ii > std::numeric_limits<To>::max())) {
|
2019-06-19 22:53:22 +00:00
|
|
|
std::ostringstream msg;
|
2020-10-21 19:29:28 +00:00
|
|
|
msg.imbue(std::locale::classic());
|
2022-04-02 21:14:10 +00:00
|
|
|
msg << "integer out of range converting " << i << " from a "
|
|
|
|
<< sizeof(From) << "-byte signed type to a " << sizeof(To)
|
|
|
|
<< "-byte unsigned type";
|
2019-06-19 22:53:22 +00:00
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
|
|
|
return static_cast<To>(i);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename From, typename To>
|
|
|
|
class IntConverter<From, To, false, true>
|
|
|
|
{
|
|
|
|
public:
|
2022-04-02 21:14:10 +00:00
|
|
|
static To
|
|
|
|
convert(From const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
// From is unsigned, and to is signed. Convert To's max to the
|
|
|
|
// unsigned version of To and compare i against that.
|
2020-04-03 16:05:50 +00:00
|
|
|
auto maxval = static_cast<typename to_u<To>::type>(
|
|
|
|
std::numeric_limits<To>::max());
|
2022-04-02 21:14:10 +00:00
|
|
|
if (i > maxval) {
|
2019-06-19 22:53:22 +00:00
|
|
|
std::ostringstream msg;
|
2020-10-21 19:29:28 +00:00
|
|
|
msg.imbue(std::locale::classic());
|
2022-04-02 21:14:10 +00:00
|
|
|
msg << "integer out of range converting " << i << " from a "
|
|
|
|
<< sizeof(From) << "-byte unsigned type to a " << sizeof(To)
|
|
|
|
<< "-byte signed type";
|
2019-06-19 22:53:22 +00:00
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
|
|
|
return static_cast<To>(i);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-08-31 12:43:18 +00:00
|
|
|
// Specific converters. The return type of each function must match
|
|
|
|
// the second template parameter to IntConverter.
|
2019-06-19 22:53:22 +00:00
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
char
|
|
|
|
to_char(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, char>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
unsigned char
|
|
|
|
to_uchar(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, unsigned char>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
short
|
|
|
|
to_short(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, short>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
unsigned short
|
|
|
|
to_ushort(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, unsigned short>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
int
|
|
|
|
to_int(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, int>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
unsigned int
|
|
|
|
to_uint(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, unsigned int>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
size_t
|
|
|
|
to_size(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, size_t>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
qpdf_offset_t
|
|
|
|
to_offset(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, qpdf_offset_t>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
long
|
|
|
|
to_long(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
2022-04-02 21:14:10 +00:00
|
|
|
return IntConverter<T, long>::convert(i);
|
2019-06-19 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
unsigned long
|
|
|
|
to_ulong(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
2022-04-02 21:14:10 +00:00
|
|
|
return IntConverter<T, unsigned long>::convert(i);
|
2019-06-19 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
long long
|
|
|
|
to_longlong(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, long long>::convert(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
unsigned long long
|
|
|
|
to_ulonglong(T const& i)
|
2019-06-19 22:53:22 +00:00
|
|
|
{
|
|
|
|
return IntConverter<T, unsigned long long>::convert(i);
|
|
|
|
}
|
2020-10-22 09:44:48 +00:00
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
void
|
|
|
|
range_check(T const& cur, T const& delta)
|
2020-10-22 09:44:48 +00:00
|
|
|
{
|
2022-04-02 21:14:10 +00:00
|
|
|
if ((delta > 0) != (cur > 0)) {
|
2020-11-21 18:12:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-02 21:14:10 +00:00
|
|
|
if ((delta > 0) && ((std::numeric_limits<T>::max() - cur) < delta)) {
|
2020-10-22 09:44:48 +00:00
|
|
|
std::ostringstream msg;
|
|
|
|
msg.imbue(std::locale::classic());
|
|
|
|
msg << "adding " << delta << " to " << cur
|
|
|
|
<< " would cause an integer overflow";
|
|
|
|
throw std::range_error(msg.str());
|
2022-04-02 21:14:10 +00:00
|
|
|
} else if (
|
|
|
|
(delta < 0) && ((std::numeric_limits<T>::min() - cur) > delta)) {
|
2020-11-21 18:12:31 +00:00
|
|
|
std::ostringstream msg;
|
|
|
|
msg.imbue(std::locale::classic());
|
|
|
|
msg << "adding " << delta << " to " << cur
|
|
|
|
<< " would cause an integer underflow";
|
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
2020-10-22 09:44:48 +00:00
|
|
|
}
|
2021-11-04 17:52:41 +00:00
|
|
|
|
|
|
|
template <typename T>
|
2022-04-02 21:14:10 +00:00
|
|
|
void
|
|
|
|
range_check_substract(T const& cur, T const& delta)
|
2021-11-04 17:52:41 +00:00
|
|
|
{
|
2022-04-02 21:14:10 +00:00
|
|
|
if ((delta >= 0) == (cur >= 0)) {
|
2021-11-04 17:52:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-02 21:14:10 +00:00
|
|
|
if ((delta > 0) && ((std::numeric_limits<T>::min() + delta) > cur)) {
|
2021-11-04 17:52:41 +00:00
|
|
|
std::ostringstream msg;
|
|
|
|
msg.imbue(std::locale::classic());
|
|
|
|
msg << "subtracting " << delta << " from " << cur
|
|
|
|
<< " would cause an integer underflow";
|
|
|
|
throw std::range_error(msg.str());
|
2022-04-02 21:14:10 +00:00
|
|
|
} else if (
|
|
|
|
(delta < 0) && ((std::numeric_limits<T>::max() + delta) < cur)) {
|
2021-11-04 17:52:41 +00:00
|
|
|
std::ostringstream msg;
|
|
|
|
msg.imbue(std::locale::classic());
|
|
|
|
msg << "subtracting " << delta << " from " << cur
|
|
|
|
<< " would cause an integer overflow";
|
|
|
|
throw std::range_error(msg.str());
|
|
|
|
}
|
|
|
|
}
|
2022-04-02 21:14:10 +00:00
|
|
|
}; // namespace QIntC
|
2019-06-19 22:53:22 +00:00
|
|
|
|
|
|
|
#endif // QINTC_HH
|