mirror of https://github.com/qpdf/qpdf.git
Protect numeric conversion against user's locale (fixes #459)
This commit is contained in:
parent
ef127001b3
commit
98f6c00dad
|
@ -1,5 +1,8 @@
|
||||||
2020-10-21 Jay Berkenbilt <ejb@ql.org>
|
2020-10-21 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Ensure that numeric conversion is not affected by the user's
|
||||||
|
global locale setting. Fixes #459.
|
||||||
|
|
||||||
* Add qpdf-<version>-linux-x86_64.zip to the list of built
|
* Add qpdf-<version>-linux-x86_64.zip to the list of built
|
||||||
distributions. This is a simple zip file that contains just the
|
distributions. This is a simple zip file that contains just the
|
||||||
qpdf executables and the dependent shared libraries that would not
|
qpdf executables and the dependent shared libraries that would not
|
||||||
|
|
|
@ -83,6 +83,14 @@ CODING RULES
|
||||||
|
|
||||||
* Use QIntC for type conversions -- see casting policy in docs.
|
* Use QIntC for type conversions -- see casting policy in docs.
|
||||||
|
|
||||||
|
* Remember to imbue ostringstreams with std::locale::classic() before
|
||||||
|
outputting numbers. This protects against the user's global locale
|
||||||
|
altering otherwise deterministic values. (See github issue #459.)
|
||||||
|
One could argue that error messages containing numbers should
|
||||||
|
respect the user's locale, but I think it's more important for
|
||||||
|
output to be consistent, since the messages in question are not
|
||||||
|
really targetted at the end user.
|
||||||
|
|
||||||
* Use QPDF_DLL on all methods that are to be exported in the shared
|
* Use QPDF_DLL on all methods that are to be exported in the shared
|
||||||
library/DLL. Use QPDF_DLL_CLASS for all classes whose type
|
library/DLL. Use QPDF_DLL_CLASS for all classes whose type
|
||||||
information is needed. This is important for exception classes and
|
information is needed. This is important for exception classes and
|
||||||
|
|
1
TODO
1
TODO
|
@ -7,7 +7,6 @@ Candidates for upcoming release
|
||||||
* Open "next" issues
|
* Open "next" issues
|
||||||
* bugs
|
* bugs
|
||||||
* #473: zsh completion with directories
|
* #473: zsh completion with directories
|
||||||
* #459: locale-sensitivity
|
|
||||||
* #449: internal error with case to reproduce (from pikepdf)
|
* #449: internal error with case to reproduce (from pikepdf)
|
||||||
* #444: concatenated stream/whitespace bug
|
* #444: concatenated stream/whitespace bug
|
||||||
* Non-bugs
|
* Non-bugs
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <locale>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// This namespace provides safe integer conversion that detects
|
// This namespace provides safe integer conversion that detects
|
||||||
|
@ -67,6 +68,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||||
if (i > std::numeric_limits<To>::max())
|
if (i > std::numeric_limits<To>::max())
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "integer out of range converting " << i
|
msg << "integer out of range converting " << i
|
||||||
<< " from a "
|
<< " from a "
|
||||||
<< sizeof(From) << "-byte unsigned type to a "
|
<< sizeof(From) << "-byte unsigned type to a "
|
||||||
|
@ -88,6 +90,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||||
(i > std::numeric_limits<To>::max()))
|
(i > std::numeric_limits<To>::max()))
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "integer out of range converting " << i
|
msg << "integer out of range converting " << i
|
||||||
<< " from a "
|
<< " from a "
|
||||||
<< sizeof(From) << "-byte signed type to a "
|
<< sizeof(From) << "-byte signed type to a "
|
||||||
|
@ -111,6 +114,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||||
if ((i < 0) || (ii > std::numeric_limits<To>::max()))
|
if ((i < 0) || (ii > std::numeric_limits<To>::max()))
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "integer out of range converting " << i
|
msg << "integer out of range converting " << i
|
||||||
<< " from a "
|
<< " from a "
|
||||||
<< sizeof(From) << "-byte signed type to a "
|
<< sizeof(From) << "-byte signed type to a "
|
||||||
|
@ -134,6 +138,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||||
if (i > maxval)
|
if (i > maxval)
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "integer out of range converting " << i
|
msg << "integer out of range converting " << i
|
||||||
<< " from a "
|
<< " from a "
|
||||||
<< sizeof(From) << "-byte unsigned type to a "
|
<< sizeof(From) << "-byte unsigned type to a "
|
||||||
|
|
|
@ -108,6 +108,7 @@ BufferInputSource::range_check(qpdf_offset_t cur, qpdf_offset_t delta)
|
||||||
((std::numeric_limits<qpdf_offset_t>::max() - cur) < delta))
|
((std::numeric_limits<qpdf_offset_t>::max() - cur) < delta))
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "seeking forward from " << cur
|
msg << "seeking forward from " << cur
|
||||||
<< " by " << delta
|
<< " by " << delta
|
||||||
<< " would cause an overflow of the offset type";
|
<< " would cause an overflow of the offset type";
|
||||||
|
|
|
@ -47,6 +47,7 @@ OffsetInputSource::seek(qpdf_offset_t offset, int whence)
|
||||||
if (offset > this->max_safe_offset)
|
if (offset > this->max_safe_offset)
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "seeking to " << offset
|
msg << "seeking to " << offset
|
||||||
<< " offset by " << global_offset
|
<< " offset by " << global_offset
|
||||||
<< " would cause an overflow of the offset type";
|
<< " would cause an overflow of the offset type";
|
||||||
|
|
|
@ -1220,6 +1220,7 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
|
||||||
((std::numeric_limits<int>::max() - obj) < chunk_count))
|
((std::numeric_limits<int>::max() - obj) < chunk_count))
|
||||||
{
|
{
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
|
msg.imbue(std::locale::classic());
|
||||||
msg << "adding " << chunk_count << " to " << obj
|
msg << "adding " << chunk_count << " to " << obj
|
||||||
<< " while computing index in xref stream would cause"
|
<< " while computing index in xref stream would cause"
|
||||||
<< " an integer overflow";
|
<< " an integer overflow";
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <locale>
|
||||||
#ifndef QPDF_NO_WCHAR_T
|
#ifndef QPDF_NO_WCHAR_T
|
||||||
# include <cwchar>
|
# include <cwchar>
|
||||||
#endif
|
#endif
|
||||||
|
@ -267,6 +268,7 @@ int_to_string_base_internal(T num, int base, int length)
|
||||||
"int_to_string_base called with unsupported base");
|
"int_to_string_base called with unsupported base");
|
||||||
}
|
}
|
||||||
std::ostringstream buf;
|
std::ostringstream buf;
|
||||||
|
buf.imbue(std::locale::classic());
|
||||||
buf << std::setbase(base) << std::nouppercase << num;
|
buf << std::setbase(base) << std::nouppercase << num;
|
||||||
std::string result;
|
std::string result;
|
||||||
int str_length = QIntC::to_int(buf.str().length());
|
int str_length = QIntC::to_int(buf.str().length());
|
||||||
|
@ -318,6 +320,7 @@ QUtil::double_to_string(double num, int decimal_places)
|
||||||
decimal_places = 6;
|
decimal_places = 6;
|
||||||
}
|
}
|
||||||
std::ostringstream buf;
|
std::ostringstream buf;
|
||||||
|
buf.imbue(std::locale::classic());
|
||||||
buf << std::setprecision(decimal_places) << std::fixed << num;
|
buf << std::setprecision(decimal_places) << std::fixed << num;
|
||||||
return buf.str();
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
|
@ -80,8 +81,38 @@ void test_to_ull(char const* str, unsigned long long wanted, bool error)
|
||||||
test_to_number(str, wanted, error, QUtil::string_to_ull);
|
test_to_number(str, wanted, error, QUtil::string_to_ull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_locale()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// First try a locale known to put commas in numbers.
|
||||||
|
std::locale::global(std::locale("en_US.UTF-8"));
|
||||||
|
}
|
||||||
|
catch (std::runtime_error&)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If that fails, fall back to the user's default locale.
|
||||||
|
std::locale::global(std::locale(""));
|
||||||
|
}
|
||||||
|
catch (std::runtime_error& e)
|
||||||
|
{
|
||||||
|
// Ignore this error on Windows without MSVC. We get
|
||||||
|
// enough test coverage on other platforms, and mingw
|
||||||
|
// seems to have limited locale support (as of
|
||||||
|
// 2020-10).
|
||||||
|
#if ! defined(_WIN32) || defined(_MSC_VER)
|
||||||
|
throw e;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void string_conversion_test()
|
void string_conversion_test()
|
||||||
{
|
{
|
||||||
|
// Make sure the code produces consistent results even if we load
|
||||||
|
// a non-C locale.
|
||||||
|
set_locale();
|
||||||
std::cout << QUtil::int_to_string(16059) << std::endl
|
std::cout << QUtil::int_to_string(16059) << std::endl
|
||||||
<< QUtil::int_to_string(16059, 7) << std::endl
|
<< QUtil::int_to_string(16059, 7) << std::endl
|
||||||
<< QUtil::int_to_string(16059, -7) << std::endl
|
<< QUtil::int_to_string(16059, -7) << std::endl
|
||||||
|
|
Loading…
Reference in New Issue