mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2026-01-26 20:26:56 +00:00
Issue #1385 demonstrates how a large 'precision' value can cause buffer overflows. Originally, the buffer was designed to fit any scientific notation. But the precision changes at which point large floating point numbers are displayed in scientific notation or default notation. In case of the default notation many extra zeros have to be printed, this was not reflected in the output_buffer and an overflow could occur. This PR computes the number of zero that do not fit into the static buffer and appends them at the end of the function triggering potential a second dynamic allocation. (The first allocation is the std::string allocation).
248 lines
10 KiB
C++
248 lines
10 KiB
C++
#include "yaml-cpp/fptostring.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace YAML {
|
|
namespace {
|
|
|
|
/**
|
|
* Helper function, that converts double to string as std::stringstream would do
|
|
*/
|
|
template <typename T>
|
|
static std::string convert_with_stringstream(T v, size_t precision = 0) {
|
|
std::stringstream ss;
|
|
ss.imbue(std::locale::classic());
|
|
if (precision > 0) {
|
|
ss << std::setprecision(precision);
|
|
}
|
|
ss << v;
|
|
return ss.str();
|
|
}
|
|
|
|
// Caution: Test involving 'convert_with_stringstream' are based on std::stringstream
|
|
// having certain printing behavior, if these changes, the unit test might fail.
|
|
// This is not a fault of FpToString just a weakness of the way these
|
|
// tests are constructed
|
|
|
|
TEST(FpToStringTest, conversion_double) {
|
|
// Issue motivating FpToString function,
|
|
// https://github.com/jbeder/yaml-cpp/issues/1289
|
|
// Original problem at hand:
|
|
EXPECT_EQ("34.34", FpToString(34.34));
|
|
EXPECT_EQ("56.56", FpToString(56.56));
|
|
EXPECT_EQ("12.12", FpToString(12.12));
|
|
EXPECT_EQ("78.78", FpToString(78.78));
|
|
|
|
// Special challenge with rounding
|
|
// https://github.com/jbeder/yaml-cpp/issues/1289#issuecomment-2211705536
|
|
EXPECT_EQ("1.54743e+26", FpToString(1.5474250491e+26f));
|
|
EXPECT_EQ(convert_with_stringstream(1.5474250491e+26f), FpToString(1.5474250491e+26f));
|
|
EXPECT_EQ("1.5474251e+26", FpToString(1.5474250491e+26f, 8));
|
|
|
|
// prints the same way as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
|
|
EXPECT_EQ(convert_with_stringstream(1e0), FpToString(1e0));
|
|
EXPECT_EQ(convert_with_stringstream(1e1), FpToString(1e1));
|
|
EXPECT_EQ(convert_with_stringstream(1e2), FpToString(1e2));
|
|
EXPECT_EQ(convert_with_stringstream(1e3), FpToString(1e3));
|
|
EXPECT_EQ(convert_with_stringstream(1e4), FpToString(1e4));
|
|
EXPECT_EQ(convert_with_stringstream(1e5), FpToString(1e5));
|
|
EXPECT_EQ(convert_with_stringstream(1e6), FpToString(1e6));
|
|
EXPECT_EQ(convert_with_stringstream(1e7), FpToString(1e7));
|
|
EXPECT_EQ(convert_with_stringstream(1e8), FpToString(1e8));
|
|
EXPECT_EQ(convert_with_stringstream(1e9), FpToString(1e9));
|
|
|
|
// Print by default values below 1e6 without scientific notation
|
|
EXPECT_EQ("1", FpToString(1.));
|
|
EXPECT_EQ("1", FpToString(1e0));
|
|
EXPECT_EQ("10", FpToString(1e1));
|
|
EXPECT_EQ("100", FpToString(1e2));
|
|
EXPECT_EQ("1000", FpToString(1e3));
|
|
EXPECT_EQ("10000", FpToString(1e4));
|
|
EXPECT_EQ("100000", FpToString(1e5));
|
|
EXPECT_EQ("1e+06", FpToString(1e6));
|
|
EXPECT_EQ("1e+07", FpToString(1e7));
|
|
EXPECT_EQ("1e+08", FpToString(1e8));
|
|
EXPECT_EQ("1e+09", FpToString(1e9));
|
|
|
|
// prints the same way as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
|
|
EXPECT_EQ(convert_with_stringstream(1e-0), FpToString(1e-0));
|
|
EXPECT_EQ(convert_with_stringstream(1e-1), FpToString(1e-1));
|
|
EXPECT_EQ(convert_with_stringstream(1e-2), FpToString(1e-2));
|
|
EXPECT_EQ(convert_with_stringstream(1e-3), FpToString(1e-3));
|
|
EXPECT_EQ(convert_with_stringstream(1e-4), FpToString(1e-4));
|
|
EXPECT_EQ(convert_with_stringstream(1e-5), FpToString(1e-5));
|
|
EXPECT_EQ(convert_with_stringstream(1e-6), FpToString(1e-6));
|
|
EXPECT_EQ(convert_with_stringstream(1e-7), FpToString(1e-7));
|
|
EXPECT_EQ(convert_with_stringstream(1e-8), FpToString(1e-8));
|
|
EXPECT_EQ(convert_with_stringstream(1e-9), FpToString(1e-9));
|
|
|
|
// Print by default values above 1e-5 without scientific notation
|
|
EXPECT_EQ("1", FpToString(1.));
|
|
EXPECT_EQ("1", FpToString(1e-0));
|
|
EXPECT_EQ("0.1", FpToString(1e-1));
|
|
EXPECT_EQ("0.01", FpToString(1e-2));
|
|
EXPECT_EQ("0.001", FpToString(1e-3));
|
|
EXPECT_EQ("0.0001", FpToString(1e-4));
|
|
EXPECT_EQ("1e-05", FpToString(1e-5));
|
|
EXPECT_EQ("1e-06", FpToString(1e-6));
|
|
EXPECT_EQ("1e-07", FpToString(1e-7));
|
|
EXPECT_EQ("1e-08", FpToString(1e-8));
|
|
EXPECT_EQ("1e-09", FpToString(1e-9));
|
|
|
|
// changing precision has the same effect as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(123., 1), FpToString(123., 1));
|
|
EXPECT_EQ(convert_with_stringstream(1234567., 7), FpToString(1234567., 7));
|
|
EXPECT_EQ(convert_with_stringstream(12345.67, 7), FpToString(12345.67, 7));
|
|
EXPECT_EQ(convert_with_stringstream(1234567e-9, 7), FpToString(1234567e-9, 7));
|
|
EXPECT_EQ(convert_with_stringstream(1234567e-9, 1), FpToString(1234567e-9, 1));
|
|
|
|
// known example that is difficult to round
|
|
EXPECT_EQ("1", FpToString(0.9999, 2));
|
|
EXPECT_EQ("-1", FpToString(-0.9999, 2));
|
|
|
|
// some more random tests
|
|
EXPECT_EQ("1.25", FpToString(1.25));
|
|
EXPECT_EQ("34.34", FpToString(34.34));
|
|
EXPECT_EQ("1e+20", FpToString(1e+20));
|
|
EXPECT_EQ("1.1e+20", FpToString(1.1e+20));
|
|
EXPECT_EQ("1e-20", FpToString(1e-20));
|
|
EXPECT_EQ("1.1e-20", FpToString(1.1e-20));
|
|
EXPECT_EQ("1e-20", FpToString(0.1e-19));
|
|
EXPECT_EQ("1.1e-20", FpToString(0.11e-19));
|
|
|
|
EXPECT_EQ("19", FpToString(18.9, 2));
|
|
EXPECT_EQ("20", FpToString(19.9, 2));
|
|
EXPECT_EQ("2e+01", FpToString(19.9, 1));
|
|
EXPECT_EQ("1.2e-05", FpToString(1.234e-5, 2));
|
|
EXPECT_EQ("1.3e-05", FpToString(1.299e-5, 2));
|
|
|
|
EXPECT_EQ("-1", FpToString(-1.));
|
|
EXPECT_EQ("-1.25", FpToString(-1.25));
|
|
EXPECT_EQ("-34.34", FpToString(-34.34));
|
|
EXPECT_EQ("-1e+20", FpToString(-1e+20));
|
|
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20));
|
|
EXPECT_EQ("-1e-20", FpToString(-1e-20));
|
|
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20));
|
|
EXPECT_EQ("-1e-20", FpToString(-0.1e-19));
|
|
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19));
|
|
|
|
EXPECT_EQ("-19", FpToString(-18.9, 2));
|
|
EXPECT_EQ("-20", FpToString(-19.9, 2));
|
|
EXPECT_EQ("-2e+01", FpToString(-19.9, 1));
|
|
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5, 2));
|
|
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5, 2));
|
|
}
|
|
|
|
TEST(FpToStringTest, conversion_float) {
|
|
// Issue motivating FpToString function,
|
|
// https://github.com/jbeder/yaml-cpp/issues/1289
|
|
// Original problem at hand:
|
|
EXPECT_EQ("34.34", FpToString(34.34f));
|
|
EXPECT_EQ("56.56", FpToString(56.56f));
|
|
EXPECT_EQ("12.12", FpToString(12.12f));
|
|
EXPECT_EQ("78.78", FpToString(78.78f));
|
|
|
|
// prints the same way as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
|
|
EXPECT_EQ(convert_with_stringstream(1e0f), FpToString(1e0f));
|
|
EXPECT_EQ(convert_with_stringstream(1e1f), FpToString(1e1f));
|
|
EXPECT_EQ(convert_with_stringstream(1e2f), FpToString(1e2f));
|
|
EXPECT_EQ(convert_with_stringstream(1e3f), FpToString(1e3f));
|
|
EXPECT_EQ(convert_with_stringstream(1e4f), FpToString(1e4f));
|
|
EXPECT_EQ(convert_with_stringstream(1e5f), FpToString(1e5f));
|
|
EXPECT_EQ(convert_with_stringstream(1e6f), FpToString(1e6f));
|
|
EXPECT_EQ(convert_with_stringstream(1e7f), FpToString(1e7f));
|
|
EXPECT_EQ(convert_with_stringstream(1e8f), FpToString(1e8f));
|
|
EXPECT_EQ(convert_with_stringstream(1e9f), FpToString(1e9f));
|
|
|
|
// Print by default values below 1e6 without scientific notation
|
|
EXPECT_EQ("1", FpToString(1.f));
|
|
EXPECT_EQ("1", FpToString(1e0f));
|
|
EXPECT_EQ("10", FpToString(1e1f));
|
|
EXPECT_EQ("100", FpToString(1e2f));
|
|
EXPECT_EQ("1000", FpToString(1e3f));
|
|
EXPECT_EQ("10000", FpToString(1e4f));
|
|
EXPECT_EQ("100000", FpToString(1e5f));
|
|
EXPECT_EQ("1e+06", FpToString(1e6f));
|
|
EXPECT_EQ("1e+07", FpToString(1e7f));
|
|
EXPECT_EQ("1e+08", FpToString(1e8f));
|
|
EXPECT_EQ("1e+09", FpToString(1e9f));
|
|
|
|
// prints the same way as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-0f), FpToString(1e-0f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-1f), FpToString(1e-1f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-2f), FpToString(1e-2f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-3f), FpToString(1e-3f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-4f), FpToString(1e-4f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-5f), FpToString(1e-5f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-6f), FpToString(1e-6f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-7f), FpToString(1e-7f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-8f), FpToString(1e-8f));
|
|
EXPECT_EQ(convert_with_stringstream(1e-9f), FpToString(1e-9f));
|
|
|
|
// Print by default values above 1e-5 without scientific notation
|
|
EXPECT_EQ("1", FpToString(1.f));
|
|
EXPECT_EQ("1", FpToString(1e-0f));
|
|
EXPECT_EQ("0.1", FpToString(1e-1f));
|
|
EXPECT_EQ("0.01", FpToString(1e-2f));
|
|
EXPECT_EQ("0.001", FpToString(1e-3f));
|
|
EXPECT_EQ("0.0001", FpToString(1e-4f));
|
|
EXPECT_EQ("1e-05", FpToString(1e-5f));
|
|
EXPECT_EQ("1e-06", FpToString(1e-6f));
|
|
EXPECT_EQ("1e-07", FpToString(1e-7f));
|
|
EXPECT_EQ("1e-08", FpToString(1e-8f));
|
|
EXPECT_EQ("1e-09", FpToString(1e-9f));
|
|
|
|
// changing precision has the same effect as std::stringstream
|
|
EXPECT_EQ(convert_with_stringstream(123.f, 1), FpToString(123.f, 1));
|
|
EXPECT_EQ(convert_with_stringstream(1234567.f, 7), FpToString(1234567.f, 7));
|
|
EXPECT_EQ(convert_with_stringstream(12345.67f, 7), FpToString(12345.67f, 7));
|
|
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 7), FpToString(1234567e-9f, 7));
|
|
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 1), FpToString(1234567e-9f, 1));
|
|
|
|
// known example that is difficult to round
|
|
EXPECT_EQ("1", FpToString(0.9999f, 2));
|
|
EXPECT_EQ("-1", FpToString(-0.9999f, 2));
|
|
|
|
// some more random tests
|
|
EXPECT_EQ("1.25", FpToString(1.25f));
|
|
EXPECT_EQ("34.34", FpToString(34.34f));
|
|
EXPECT_EQ("1e+20", FpToString(1e+20f));
|
|
EXPECT_EQ("1.1e+20", FpToString(1.1e+20f));
|
|
EXPECT_EQ("1e-20", FpToString(1e-20f));
|
|
EXPECT_EQ("1.1e-20", FpToString(1.1e-20f));
|
|
EXPECT_EQ("1e-20", FpToString(0.1e-19f));
|
|
EXPECT_EQ("1.1e-20", FpToString(0.11e-19f));
|
|
|
|
EXPECT_EQ("19", FpToString(18.9f, 2));
|
|
EXPECT_EQ("20", FpToString(19.9f, 2));
|
|
EXPECT_EQ("2e+01", FpToString(19.9f, 1));
|
|
EXPECT_EQ("1.2e-05", FpToString(1.234e-5f, 2));
|
|
EXPECT_EQ("1.3e-05", FpToString(1.299e-5f, 2));
|
|
|
|
EXPECT_EQ("-1", FpToString(-1.f));
|
|
EXPECT_EQ("-1.25", FpToString(-1.25f));
|
|
EXPECT_EQ("-34.34", FpToString(-34.34f));
|
|
EXPECT_EQ("-1e+20", FpToString(-1e+20f));
|
|
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20f));
|
|
EXPECT_EQ("-1e-20", FpToString(-1e-20f));
|
|
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20f));
|
|
EXPECT_EQ("-1e-20", FpToString(-0.1e-19f));
|
|
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19f));
|
|
|
|
EXPECT_EQ("-19", FpToString(-18.9f, 2));
|
|
EXPECT_EQ("-20", FpToString(-19.9f, 2));
|
|
EXPECT_EQ("-2e+01", FpToString(-19.9f, 1));
|
|
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5f, 2));
|
|
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5f, 2));
|
|
}
|
|
|
|
TEST(FpToStringTest, vulnerability_stack_buffer_overflow) {
|
|
EXPECT_EQ(FpToString(1.0e100, 200), "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace YAML
|