mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
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>
|
||||
|
||||
* 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
|
||||
distributions. This is a simple zip file that contains just the
|
||||
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.
|
||||
|
||||
* 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
|
||||
library/DLL. Use QPDF_DLL_CLASS for all classes whose type
|
||||
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
|
||||
* bugs
|
||||
* #473: zsh completion with directories
|
||||
* #459: locale-sensitivity
|
||||
* #449: internal error with case to reproduce (from pikepdf)
|
||||
* #444: concatenated stream/whitespace bug
|
||||
* Non-bugs
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <locale>
|
||||
#include <type_traits>
|
||||
|
||||
// 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())
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "integer out of range converting " << i
|
||||
<< " from a "
|
||||
<< sizeof(From) << "-byte unsigned type to a "
|
||||
@ -88,6 +90,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||
(i > std::numeric_limits<To>::max()))
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "integer out of range converting " << i
|
||||
<< " from 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()))
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "integer out of range converting " << i
|
||||
<< " from a "
|
||||
<< sizeof(From) << "-byte signed type to a "
|
||||
@ -134,6 +138,7 @@ namespace QIntC // QIntC = qpdf Integer Conversion
|
||||
if (i > maxval)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "integer out of range converting " << i
|
||||
<< " from 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::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "seeking forward from " << cur
|
||||
<< " by " << delta
|
||||
<< " 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)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "seeking to " << offset
|
||||
<< " offset by " << global_offset
|
||||
<< " 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::ostringstream msg;
|
||||
msg.imbue(std::locale::classic());
|
||||
msg << "adding " << chunk_count << " to " << obj
|
||||
<< " while computing index in xref stream would cause"
|
||||
<< " an integer overflow";
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <memory>
|
||||
#include <locale>
|
||||
#ifndef QPDF_NO_WCHAR_T
|
||||
# include <cwchar>
|
||||
#endif
|
||||
@ -267,6 +268,7 @@ int_to_string_base_internal(T num, int base, int length)
|
||||
"int_to_string_base called with unsupported base");
|
||||
}
|
||||
std::ostringstream buf;
|
||||
buf.imbue(std::locale::classic());
|
||||
buf << std::setbase(base) << std::nouppercase << num;
|
||||
std::string result;
|
||||
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;
|
||||
}
|
||||
std::ostringstream buf;
|
||||
buf.imbue(std::locale::classic());
|
||||
buf << std::setprecision(decimal_places) << std::fixed << num;
|
||||
return buf.str();
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
|
||||
#ifdef _WIN32
|
||||
# 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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// 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
|
||||
<< QUtil::int_to_string(16059, 7) << std::endl
|
||||
<< QUtil::int_to_string(16059, -7) << std::endl
|
||||
|
Loading…
Reference in New Issue
Block a user