aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--fuzz/qpdf_extra/26994.fuzzbin0 -> 40088 bytes
-rw-r--r--include/qpdf/QIntC.hh14
-rw-r--r--libtests/qintc.cc40
-rw-r--r--libtests/qtest/qintc/qintc.out13
5 files changed, 72 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index b6cd508a..b7571235 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2020-11-21 Jay Berkenbilt <ejb@ql.org>
+
+ * Fix QIntC::range_check to handle negative numbers properly (fuzz
+ issue 26994).
+
2020-11-11 Jay Berkenbilt <ejb@ql.org>
* Treat a direct page object as a runtime error rather than a
diff --git a/fuzz/qpdf_extra/26994.fuzz b/fuzz/qpdf_extra/26994.fuzz
new file mode 100644
index 00000000..ce2860bb
--- /dev/null
+++ b/fuzz/qpdf_extra/26994.fuzz
Binary files differ
diff --git a/include/qpdf/QIntC.hh b/include/qpdf/QIntC.hh
index 5f7f21bb..e3ea0a28 100644
--- a/include/qpdf/QIntC.hh
+++ b/include/qpdf/QIntC.hh
@@ -226,6 +226,11 @@ namespace QIntC // QIntC = qpdf Integer Conversion
template <typename T>
void range_check(T const& cur, T const& delta)
{
+ if ((delta > 0) != (cur > 0))
+ {
+ return;
+ }
+
if ((delta > 0) &&
((std::numeric_limits<T>::max() - cur) < delta))
{
@@ -235,6 +240,15 @@ namespace QIntC // QIntC = qpdf Integer Conversion
<< " would cause an integer overflow";
throw std::range_error(msg.str());
}
+ else if ((delta < 0) &&
+ ((std::numeric_limits<T>::min() - cur) > delta))
+ {
+ std::ostringstream msg;
+ msg.imbue(std::locale::classic());
+ msg << "adding " << delta << " to " << cur
+ << " would cause an integer underflow";
+ throw std::range_error(msg.str());
+ }
}
};
diff --git a/libtests/qintc.cc b/libtests/qintc.cc
index 6b35e837..32c3713f 100644
--- a/libtests/qintc.cc
+++ b/libtests/qintc.cc
@@ -25,6 +25,29 @@ static void try_convert_real(
std::cout << ((passed == exp_pass) ? " PASSED" : " FAILED") << std::endl;
}
+#define try_range_check(exp_pass, a, b) \
+ try_range_check_real(#a " + " #b, exp_pass, a, b)
+
+template <typename T>
+static void try_range_check_real(
+ char const* description, bool exp_pass,
+ T const& a, T const& b)
+{
+ bool passed = false;
+ try
+ {
+ QIntC::range_check(a, b);
+ std::cout << description << ": okay";
+ passed = true;
+ }
+ catch (std::range_error& e)
+ {
+ std::cout << description << ": " << e.what();
+ passed = false;
+ }
+ std::cout << ((passed == exp_pass) ? " PASSED" : " FAILED") << std::endl;
+}
+
int main()
{
uint32_t u1 = 3141592653U; // Too big for signed type
@@ -56,5 +79,22 @@ int main()
try_convert(true, QIntC::to_uchar<char>, c2);
try_convert(true, QIntC::to_char<char>, c2);
+ auto constexpr max_ll = std::numeric_limits<long long>::max();
+ auto constexpr max_ull = std::numeric_limits<unsigned long long>::max();
+ auto constexpr min_ll = std::numeric_limits<long long>::min();
+ auto constexpr max_sc = std::numeric_limits<signed char>::max();
+ try_range_check(true, 1, 2);
+ try_range_check(true, -1, 2);
+ try_range_check(true, -100, -200);
+ try_range_check(true, max_ll, 0LL);
+ try_range_check(false, max_ll, 1LL);
+ try_range_check(true, max_ll, 0LL);
+ try_range_check(false, max_ll, 1LL);
+ try_range_check(true, max_ull, 0ULL);
+ try_range_check(false, max_ull, 1ULL);
+ try_range_check(true, min_ll, 0LL);
+ try_range_check(false, min_ll, -1LL);
+ try_range_check(false, max_sc, max_sc);
+ try_range_check(true, '!', '#');
return 0;
}
diff --git a/libtests/qtest/qintc/qintc.out b/libtests/qtest/qintc/qintc.out
index 2a2ff9f5..5520c635 100644
--- a/libtests/qtest/qintc/qintc.out
+++ b/libtests/qtest/qintc/qintc.out
@@ -13,3 +13,16 @@ QIntC::to_uchar<int32_t>(i2): 81 Q PASSED
QIntC::to_uchar<signed char>(c1): integer out of range converting ÷ from a 1-byte signed type to a 1-byte unsigned type PASSED
QIntC::to_uchar<char>(c2): W W PASSED
QIntC::to_char<char>(c2): W W PASSED
+1 + 2: okay PASSED
+-1 + 2: okay PASSED
+-100 + -200: okay PASSED
+max_ll + 0LL: okay PASSED
+max_ll + 1LL: adding 1 to 9223372036854775807 would cause an integer overflow PASSED
+max_ll + 0LL: okay PASSED
+max_ll + 1LL: adding 1 to 9223372036854775807 would cause an integer overflow PASSED
+max_ull + 0ULL: okay PASSED
+max_ull + 1ULL: adding 1 to 18446744073709551615 would cause an integer overflow PASSED
+min_ll + 0LL: okay PASSED
+min_ll + -1LL: adding -1 to -9223372036854775808 would cause an integer underflow PASSED
+max_sc + max_sc: adding  to  would cause an integer overflow PASSED
+'!' + '#': okay PASSED