aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2017-08-29 18:21:29 +0200
committerJay Berkenbilt <ejb@ql.org>2017-08-29 18:28:32 +0200
commit6d46346eb93d5032c08cf1e39023b5d57260a766 (patch)
tree6bbb6f3347bee0f402672ab82c0af8ec402c30f1
parentd7d446e0b8aacd122d1a000d38ebafa4dbf5b3d2 (diff)
downloadqpdf-6d46346eb93d5032c08cf1e39023b5d57260a766.tar.zst
Detect integer overflow/underflow
-rw-r--r--include/qpdf/QUtil.hh4
-rw-r--r--libqpdf/QUtil.cc33
-rw-r--r--libtests/qtest/qutil/qutil.out8
-rw-r--r--libtests/qutil.cc67
4 files changed, 110 insertions, 2 deletions
diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh
index 2c58475c..79db229f 100644
--- a/include/qpdf/QUtil.hh
+++ b/include/qpdf/QUtil.hh
@@ -29,8 +29,12 @@ namespace QUtil
QPDF_DLL
std::string double_to_string(double, int decimal_places = 0);
+ // These string to number methods throw std::runtime_error on
+ // underflow/overflow.
QPDF_DLL
long long string_to_ll(char const* str);
+ QPDF_DLL
+ int string_to_int(char const* str);
// Pipeline's write method wants unsigned char*, but we often have
// some other type of string. These methods do combinations of
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index 10ca7d3b..32855bbf 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -81,11 +81,40 @@ QUtil::double_to_string(double num, int decimal_places)
long long
QUtil::string_to_ll(char const* str)
{
+ errno = 0;
#ifdef _MSC_VER
- return _strtoi64(str, 0, 10);
+ long long result = _strtoi64(str, 0, 10);
#else
- return strtoll(str, 0, 10);
+ long long result = strtoll(str, 0, 10);
#endif
+ if (errno == ERANGE)
+ {
+ throw std::runtime_error(
+ std::string("overflow/underflow converting ") + str
+ + " to 64-bit integer");
+ }
+ return result;
+}
+
+int
+QUtil::string_to_int(char const* str)
+{
+ errno = 0;
+ long long_val = strtol(str, 0, 10);
+ if (errno == ERANGE)
+ {
+ throw std::runtime_error(
+ std::string("overflow/underflow converting ") + str
+ + " to long integer");
+ }
+ int result = static_cast<int>(long_val);
+ if (static_cast<long>(result) != long_val)
+ {
+ throw std::runtime_error(
+ std::string("overflow/underflow converting ") + str
+ + " to integer");
+ }
+ return result;
}
unsigned char*
diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out
index 7a0bba48..c76beb2b 100644
--- a/libtests/qtest/qutil/qutil.out
+++ b/libtests/qtest/qutil/qutil.out
@@ -14,6 +14,14 @@
one
7
compare okay
+-2147483648 to int: PASSED
+2147483647 to int: PASSED
+2147483648 to int threw: PASSED
+-2147483649 to int threw: PASSED
+9999999999999999999999999 to int threw: PASSED
+2147483648 to int: PASSED
+-2147483649 to int: PASSED
+99999999999999999999999999999999999999999999999999 to int threw: PASSED
----
before remove
exception: remove file: No such file or directory
diff --git a/libtests/qutil.cc b/libtests/qutil.cc
index 04ca03c0..7fc5960d 100644
--- a/libtests/qutil.cc
+++ b/libtests/qutil.cc
@@ -6,6 +6,7 @@
#include <qpdf/QUtil.hh>
#include <qpdf/PointerHolder.hh>
#include <string.h>
+#include <limits.h>
#ifdef _WIN32
# include <io.h>
@@ -13,6 +14,57 @@
# include <unistd.h>
#endif
+template <class int_T>
+void test_to_number(char const* str, int_T wanted, bool error,
+ int_T (*fn)(char const*))
+{
+ bool threw = false;
+ bool worked = false;
+ int_T result = 0;
+ try
+ {
+ result = fn(str);
+ worked = (wanted == result);
+ }
+ catch (std::runtime_error)
+ {
+ threw = true;
+ }
+ if (threw)
+ {
+ if (error)
+ {
+ std::cout << str << " to int threw: PASSED" << std::endl;
+ }
+ else
+ {
+ std::cout << str << " to int threw but wanted "
+ << wanted << std::endl;
+ }
+ }
+ else
+ {
+ if (worked)
+ {
+ std::cout << str << " to int: PASSED" << std::endl;
+ }
+ else
+ {
+ std::cout << str << " to int failed; got " << result << std::endl;
+ }
+ }
+}
+
+void test_to_int(char const* str, int wanted, bool error)
+{
+ test_to_number(str, wanted, error, QUtil::string_to_int);
+}
+
+void test_to_ll(char const* str, long long wanted, bool error)
+{
+ test_to_number(str, wanted, error, QUtil::string_to_ll);
+}
+
void string_conversion_test()
{
std::cout << QUtil::int_to_string(16059) << std::endl
@@ -44,6 +96,21 @@ void string_conversion_test()
std::cout << "compare failed" << std::endl;
}
delete [] tmp;
+
+ std::string int_max_str = QUtil::int_to_string(INT_MAX);
+ std::string int_min_str = QUtil::int_to_string(INT_MIN);
+ long long int_max_plus_1 = static_cast<long long>(INT_MAX) + 1;
+ long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1;
+ std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1);
+ std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1);
+ test_to_int(int_min_str.c_str(), INT_MIN, false);
+ test_to_int(int_max_str.c_str(), INT_MAX, false);
+ test_to_int(int_max_plus_1_str.c_str(), 0, true);
+ test_to_int(int_min_minus_1_str.c_str(), 0, true);
+ test_to_int("9999999999999999999999999", 0, true);
+ test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false);
+ test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false);
+ test_to_ll("99999999999999999999999999999999999999999999999999", 0, true);
}
void os_wrapper_test()