Commit 6d46346eb93d5032c08cf1e39023b5d57260a766

Authored by Jay Berkenbilt
1 parent d7d446e0

Detect integer overflow/underflow

include/qpdf/QUtil.hh
... ... @@ -29,8 +29,12 @@ namespace QUtil
29 29 QPDF_DLL
30 30 std::string double_to_string(double, int decimal_places = 0);
31 31  
  32 + // These string to number methods throw std::runtime_error on
  33 + // underflow/overflow.
32 34 QPDF_DLL
33 35 long long string_to_ll(char const* str);
  36 + QPDF_DLL
  37 + int string_to_int(char const* str);
34 38  
35 39 // Pipeline's write method wants unsigned char*, but we often have
36 40 // some other type of string. These methods do combinations of
... ...
libqpdf/QUtil.cc
... ... @@ -81,11 +81,40 @@ QUtil::double_to_string(double num, int decimal_places)
81 81 long long
82 82 QUtil::string_to_ll(char const* str)
83 83 {
  84 + errno = 0;
84 85 #ifdef _MSC_VER
85   - return _strtoi64(str, 0, 10);
  86 + long long result = _strtoi64(str, 0, 10);
86 87 #else
87   - return strtoll(str, 0, 10);
  88 + long long result = strtoll(str, 0, 10);
88 89 #endif
  90 + if (errno == ERANGE)
  91 + {
  92 + throw std::runtime_error(
  93 + std::string("overflow/underflow converting ") + str
  94 + + " to 64-bit integer");
  95 + }
  96 + return result;
  97 +}
  98 +
  99 +int
  100 +QUtil::string_to_int(char const* str)
  101 +{
  102 + errno = 0;
  103 + long long_val = strtol(str, 0, 10);
  104 + if (errno == ERANGE)
  105 + {
  106 + throw std::runtime_error(
  107 + std::string("overflow/underflow converting ") + str
  108 + + " to long integer");
  109 + }
  110 + int result = static_cast<int>(long_val);
  111 + if (static_cast<long>(result) != long_val)
  112 + {
  113 + throw std::runtime_error(
  114 + std::string("overflow/underflow converting ") + str
  115 + + " to integer");
  116 + }
  117 + return result;
89 118 }
90 119  
91 120 unsigned char*
... ...
libtests/qtest/qutil/qutil.out
... ... @@ -14,6 +14,14 @@
14 14 one
15 15 7
16 16 compare okay
  17 +-2147483648 to int: PASSED
  18 +2147483647 to int: PASSED
  19 +2147483648 to int threw: PASSED
  20 +-2147483649 to int threw: PASSED
  21 +9999999999999999999999999 to int threw: PASSED
  22 +2147483648 to int: PASSED
  23 +-2147483649 to int: PASSED
  24 +99999999999999999999999999999999999999999999999999 to int threw: PASSED
17 25 ----
18 26 before remove
19 27 exception: remove file: No such file or directory
... ...
libtests/qutil.cc
... ... @@ -6,6 +6,7 @@
6 6 #include <qpdf/QUtil.hh>
7 7 #include <qpdf/PointerHolder.hh>
8 8 #include <string.h>
  9 +#include <limits.h>
9 10  
10 11 #ifdef _WIN32
11 12 # include <io.h>
... ... @@ -13,6 +14,57 @@
13 14 # include <unistd.h>
14 15 #endif
15 16  
  17 +template <class int_T>
  18 +void test_to_number(char const* str, int_T wanted, bool error,
  19 + int_T (*fn)(char const*))
  20 +{
  21 + bool threw = false;
  22 + bool worked = false;
  23 + int_T result = 0;
  24 + try
  25 + {
  26 + result = fn(str);
  27 + worked = (wanted == result);
  28 + }
  29 + catch (std::runtime_error)
  30 + {
  31 + threw = true;
  32 + }
  33 + if (threw)
  34 + {
  35 + if (error)
  36 + {
  37 + std::cout << str << " to int threw: PASSED" << std::endl;
  38 + }
  39 + else
  40 + {
  41 + std::cout << str << " to int threw but wanted "
  42 + << wanted << std::endl;
  43 + }
  44 + }
  45 + else
  46 + {
  47 + if (worked)
  48 + {
  49 + std::cout << str << " to int: PASSED" << std::endl;
  50 + }
  51 + else
  52 + {
  53 + std::cout << str << " to int failed; got " << result << std::endl;
  54 + }
  55 + }
  56 +}
  57 +
  58 +void test_to_int(char const* str, int wanted, bool error)
  59 +{
  60 + test_to_number(str, wanted, error, QUtil::string_to_int);
  61 +}
  62 +
  63 +void test_to_ll(char const* str, long long wanted, bool error)
  64 +{
  65 + test_to_number(str, wanted, error, QUtil::string_to_ll);
  66 +}
  67 +
16 68 void string_conversion_test()
17 69 {
18 70 std::cout << QUtil::int_to_string(16059) << std::endl
... ... @@ -44,6 +96,21 @@ void string_conversion_test()
44 96 std::cout << "compare failed" << std::endl;
45 97 }
46 98 delete [] tmp;
  99 +
  100 + std::string int_max_str = QUtil::int_to_string(INT_MAX);
  101 + std::string int_min_str = QUtil::int_to_string(INT_MIN);
  102 + long long int_max_plus_1 = static_cast<long long>(INT_MAX) + 1;
  103 + long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1;
  104 + std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1);
  105 + std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1);
  106 + test_to_int(int_min_str.c_str(), INT_MIN, false);
  107 + test_to_int(int_max_str.c_str(), INT_MAX, false);
  108 + test_to_int(int_max_plus_1_str.c_str(), 0, true);
  109 + test_to_int(int_min_minus_1_str.c_str(), 0, true);
  110 + test_to_int("9999999999999999999999999", 0, true);
  111 + test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false);
  112 + test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false);
  113 + test_to_ll("99999999999999999999999999999999999999999999999999", 0, true);
47 114 }
48 115  
49 116 void os_wrapper_test()
... ...