diff options
author | Tom Sepez <tsepez@chromium.org> | 2018-09-13 21:40:49 +0000 |
---|---|---|
committer | Chromium commit bot <commit-bot@chromium.org> | 2018-09-13 21:40:49 +0000 |
commit | 61f1d62daa127097e4719575bd0ff652abcebad0 (patch) | |
tree | cc65e222aeaa6739972d9f0b07ca441c444eb6a1 /core/fxcrt | |
parent | 725f544776a6ee96a514a91dffd39aa32ecca3e6 (diff) | |
download | pdfium-61f1d62daa127097e4719575bd0ff652abcebad0.tar.xz |
Reland "Introduce FX_Number class as a replacement for FX_atonum()."
This reverts commit 29e180342e18873babf1c74f7c5c056f90a191b0.
Reason for revert: probably harmless
Original change's description:
> Revert "Introduce FX_Number class as a replacement for FX_atonum()."
>
> This reverts commit a5d7ad3aa8feb08a14b5cca173d673054c1ade23.
>
> Reason for revert: Speculative revert to get back before flake.
>
> Original change's description:
> > Introduce FX_Number class as a replacement for FX_atonum().
> >
> > The issue with FX_atonum() is that it doesn't return any information
> > about whether it range-checked its integer values as a signed or
> > unsigned type, even though it knows this as part of its processing.
> >
> > Rather than adding another out parameter to that function, create
> > a class to hold all this information together.
> >
> > This is the first place things went astray while diagnosing
> > bug 882959, in that a large positive value was cast to float as a
> > negative value. Unfortunately, this doesn't affect the related bug,
> > but is a step in the right direction.
> >
> > Change-Id: I0977ec8fccf85e2632a962507bdd30a1cbe6d33c
> > Reviewed-on: https://pdfium-review.googlesource.com/42353
> > Reviewed-by: Lei Zhang <thestig@chromium.org>
> > Commit-Queue: Tom Sepez <tsepez@chromium.org>
>
> TBR=thestig@chromium.org,tsepez@chromium.org
>
> Change-Id: Ia56270c3daa80408fc2b23eb4384a77f03f45b82
> Reviewed-on: https://pdfium-review.googlesource.com/42392
> Reviewed-by: Tom Sepez <tsepez@chromium.org>
> Commit-Queue: Tom Sepez <tsepez@chromium.org>
TBR=thestig@chromium.org,tsepez@chromium.org
Change-Id: I83c37aa3040a8890f2117753f19ab1d452d411e7
Reviewed-on: https://pdfium-review.googlesource.com/42410
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Diffstat (limited to 'core/fxcrt')
-rw-r--r-- | core/fxcrt/fx_number.cpp | 104 | ||||
-rw-r--r-- | core/fxcrt/fx_number.h | 39 | ||||
-rw-r--r-- | core/fxcrt/fx_number_unittest.cpp | 165 | ||||
-rw-r--r-- | core/fxcrt/fx_string.cpp | 63 | ||||
-rw-r--r-- | core/fxcrt/fx_string.h | 3 | ||||
-rw-r--r-- | core/fxcrt/fx_string_unittest.cpp | 47 |
6 files changed, 310 insertions, 111 deletions
diff --git a/core/fxcrt/fx_number.cpp b/core/fxcrt/fx_number.cpp new file mode 100644 index 0000000000..68d5bd9b32 --- /dev/null +++ b/core/fxcrt/fx_number.cpp @@ -0,0 +1,104 @@ +// Copyright 2018 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcrt/fx_number.h" + +#include <limits> + +#include "core/fxcrt/fx_extension.h" +#include "core/fxcrt/fx_string.h" + +FX_Number::FX_Number() + : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {} + +FX_Number::FX_Number(uint32_t value) + : m_bInteger(true), m_bSigned(false), m_UnsignedValue(value) {} + +FX_Number::FX_Number(int32_t value) + : m_bInteger(true), m_bSigned(true), m_SignedValue(value) {} + +FX_Number::FX_Number(float value) + : m_bInteger(false), m_bSigned(true), m_FloatValue(value) {} + +FX_Number::FX_Number(const ByteStringView& strc) + : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) { + if (strc.IsEmpty()) + return; + + if (strc.Contains('.')) { + m_bInteger = false; + m_bSigned = true; + m_FloatValue = FX_atof(strc); + return; + } + + // Note, numbers in PDF are typically of the form 123, -123, etc. But, + // for things like the Permissions on the encryption hash the number is + // actually an unsigned value. We use a uint32_t so we can deal with the + // unsigned and then check for overflow if the user actually signed the value. + // The Permissions flag is listed in Table 3.20 PDF 1.7 spec. + pdfium::base::CheckedNumeric<uint32_t> unsigned_val = 0; + bool bNegative = false; + size_t cc = 0; + if (strc[0] == '+') { + cc++; + m_bSigned = true; + } else if (strc[0] == '-') { + bNegative = true; + m_bSigned = true; + cc++; + } + + while (cc < strc.GetLength() && std::isdigit(strc[cc])) { + unsigned_val = unsigned_val * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc)); + if (!unsigned_val.IsValid()) + break; + cc++; + } + + uint32_t uValue = unsigned_val.ValueOrDefault(0); + if (!m_bSigned) { + m_UnsignedValue = uValue; + return; + } + + // We have a sign, so if the value was greater then the signed integer + // limits, then we've overflowed and must reset to the default value. + constexpr uint32_t uLimit = + static_cast<uint32_t>(std::numeric_limits<int>::max()); + + if (uValue > (bNegative ? uLimit + 1 : uLimit)) + uValue = 0; + + // Switch back to the int space so we can flip to a negative if we need. + int32_t value = static_cast<int32_t>(uValue); + if (bNegative) { + // |value| is usually positive, except in the corner case of "-2147483648", + // where |uValue| is 2147483648. When it gets casted to an int, |value| + // becomes -2147483648. For this case, avoid undefined behavior, because + // an int32_t cannot represent 2147483648. + static constexpr int kMinInt = std::numeric_limits<int>::min(); + m_SignedValue = LIKELY(value != kMinInt) ? -value : kMinInt; + } else { + m_SignedValue = value; + } +} + +uint32_t FX_Number::GetUnsigned() const { + return m_bInteger ? m_UnsignedValue : static_cast<uint32_t>(m_FloatValue); +} + +int32_t FX_Number::GetSigned() const { + return m_bInteger ? m_SignedValue : static_cast<int32_t>(m_FloatValue); +} + +float FX_Number::GetFloat() const { + if (!m_bInteger) + return m_FloatValue; + + return m_bSigned ? static_cast<float>(m_SignedValue) + : static_cast<float>(m_UnsignedValue); +} diff --git a/core/fxcrt/fx_number.h b/core/fxcrt/fx_number.h new file mode 100644 index 0000000000..4c789b4130 --- /dev/null +++ b/core/fxcrt/fx_number.h @@ -0,0 +1,39 @@ +// Copyright 2018 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCRT_FX_NUMBER_H_ +#define CORE_FXCRT_FX_NUMBER_H_ + +#include <stdint.h> + +#include "core/fxcrt/bytestring.h" + +class FX_Number { + public: + FX_Number(); + explicit FX_Number(uint32_t value); + explicit FX_Number(int32_t value); + explicit FX_Number(float value); + explicit FX_Number(const ByteStringView& str); + + bool IsInteger() const { return m_bInteger; } + bool IsSigned() const { return m_bSigned; } + + uint32_t GetUnsigned() const; // Underflow possible. + int32_t GetSigned() const; // Underflow/Overflow possible. + float GetFloat() const; + + private: + bool m_bInteger; // One of the two integers vs. float type. + bool m_bSigned; // Only valid if |m_bInteger|. + union { + uint32_t m_UnsignedValue; + int32_t m_SignedValue; + float m_FloatValue; + }; +}; + +#endif // CORE_FXCRT_FX_NUMBER_H_ diff --git a/core/fxcrt/fx_number_unittest.cpp b/core/fxcrt/fx_number_unittest.cpp new file mode 100644 index 0000000000..83702dadcf --- /dev/null +++ b/core/fxcrt/fx_number_unittest.cpp @@ -0,0 +1,165 @@ +// Copyright 2018 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <limits> + +#include "core/fxcrt/fx_number.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(fxnumber, Default) { + FX_Number number; + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(0u, number.GetUnsigned()); + EXPECT_EQ(0, number.GetSigned()); + EXPECT_FLOAT_EQ(0.0f, number.GetFloat()); +} + +TEST(fxnumber, FromUnsigned) { + FX_Number number(42u); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(42u, number.GetUnsigned()); + EXPECT_EQ(42, number.GetSigned()); + EXPECT_FLOAT_EQ(42.0f, number.GetFloat()); + + // Show that assignment works. + FX_Number number2 = number; + EXPECT_TRUE(number2.IsInteger()); + EXPECT_FALSE(number2.IsSigned()); + EXPECT_EQ(42u, number2.GetUnsigned()); + EXPECT_EQ(42, number2.GetSigned()); + EXPECT_FLOAT_EQ(42.0F, number2.GetFloat()); +} + +TEST(fxnumber, FromSigned) { + FX_Number number(-128); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(4294967168u, number.GetUnsigned()); + EXPECT_EQ(-128, number.GetSigned()); + EXPECT_FLOAT_EQ(-128.0f, number.GetFloat()); + + // Show that assignment works. + FX_Number number2 = number; + EXPECT_TRUE(number2.IsInteger()); + EXPECT_TRUE(number2.IsSigned()); + EXPECT_EQ(4294967168u, number2.GetUnsigned()); + EXPECT_EQ(-128, number2.GetSigned()); + EXPECT_FLOAT_EQ(-128.0f, number2.GetFloat()); +} + +TEST(fxnumber, FromFloat) { + FX_Number number(-100.001f); + EXPECT_FALSE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(4294967196u, number.GetUnsigned()); + EXPECT_EQ(-100, number.GetSigned()); + EXPECT_FLOAT_EQ(-100.001f, number.GetFloat()); + + // Show that assignment works. + FX_Number number2 = number; + EXPECT_FALSE(number2.IsInteger()); + EXPECT_TRUE(number2.IsSigned()); + EXPECT_EQ(4294967196u, number2.GetUnsigned()); + EXPECT_EQ(-100, number2.GetSigned()); + EXPECT_FLOAT_EQ(-100.001f, number2.GetFloat()); +} + +TEST(fxnumber, FromStringUnsigned) { + { + FX_Number number(""); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(0u, number.GetUnsigned()); + } + { + FX_Number number("0"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(0u, number.GetUnsigned()); + } + { + FX_Number number("10"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(10u, number.GetUnsigned()); + } + { + FX_Number number("4294967295"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(std::numeric_limits<uint32_t>::max(), number.GetUnsigned()); + } + { + // Value overflows. + FX_Number number("4223423494965252"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_FALSE(number.IsSigned()); + EXPECT_EQ(0u, number.GetUnsigned()); + } + { + // No explicit sign will allow the number to go negative if we retrieve + // it as a signed value. This is needed for things like the encryption + // Permissions flag (Table 3.20 PDF 1.7 spec) + FX_Number number("4294965252"); + EXPECT_EQ(-2044, number.GetSigned()); + } +} + +TEST(fxnumber, FromStringSigned) { + { + FX_Number number("-0"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(0, number.GetSigned()); + } + { + FX_Number number("+0"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(0, number.GetSigned()); + } + { + FX_Number number("-10"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(-10, number.GetSigned()); + } + { + FX_Number number("+10"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(10, number.GetSigned()); + } + { + FX_Number number("-2147483648"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(std::numeric_limits<int32_t>::min(), number.GetSigned()); + } + { + FX_Number number("+2147483647"); + EXPECT_TRUE(number.IsInteger()); + EXPECT_TRUE(number.IsSigned()); + EXPECT_EQ(std::numeric_limits<int32_t>::max(), number.GetSigned()); + } + { + // Value underflows. + FX_Number number("-2147483649"); + EXPECT_EQ(0u, number.GetUnsigned()); + EXPECT_EQ(0, number.GetSigned()); + } + { + // Value overflows. + FX_Number number("+2147483648"); + EXPECT_EQ(0u, number.GetUnsigned()); + EXPECT_EQ(0, number.GetSigned()); + } +} + +TEST(fxnumber, FromStringFloat) { + FX_Number number("3.24"); + EXPECT_FLOAT_EQ(3.24f, number.GetFloat()); +} diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp index c9993f9ab8..31eb8e917d 100644 --- a/core/fxcrt/fx_string.cpp +++ b/core/fxcrt/fx_string.cpp @@ -47,69 +47,6 @@ float FractionalScale(size_t scale_factor, int value) { } // namespace -bool FX_atonum(const ByteStringView& strc, void* pData) { - if (strc.Contains('.')) { - float* pFloat = static_cast<float*>(pData); - *pFloat = FX_atof(strc); - return false; - } - - // Note, numbers in PDF are typically of the form 123, -123, etc. But, - // for things like the Permissions on the encryption hash the number is - // actually an unsigned value. We use a uint32_t so we can deal with the - // unsigned and then check for overflow if the user actually signed the value. - // The Permissions flag is listed in Table 3.20 PDF 1.7 spec. - pdfium::base::CheckedNumeric<uint32_t> integer = 0; - bool bNegative = false; - bool bSigned = false; - size_t cc = 0; - if (strc[0] == '+') { - cc++; - bSigned = true; - } else if (strc[0] == '-') { - bNegative = true; - bSigned = true; - cc++; - } - - while (cc < strc.GetLength() && std::isdigit(strc[cc])) { - integer = integer * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc)); - if (!integer.IsValid()) - break; - cc++; - } - - // We have a sign, and the value was greater then a regular integer - // we've overflowed, reset to the default value. - if (bSigned) { - if (bNegative) { - if (integer.ValueOrDefault(0) > - static_cast<uint32_t>(std::numeric_limits<int>::max()) + 1) { - integer = 0; - } - } else if (integer.ValueOrDefault(0) > - static_cast<uint32_t>(std::numeric_limits<int>::max())) { - integer = 0; - } - } - - // Switch back to the int space so we can flip to a negative if we need. - uint32_t uValue = integer.ValueOrDefault(0); - int32_t value = static_cast<int>(uValue); - if (bNegative) { - // |value| is usually positive, except in the corner case of "-2147483648", - // where |uValue| is 2147483648. When it gets casted to an int, |value| - // becomes -2147483648. For this case, avoid undefined behavior, because an - // integer cannot represent 2147483648. - static constexpr int kMinInt = std::numeric_limits<int>::min(); - value = LIKELY(value != kMinInt) ? -value : kMinInt; - } - - int* pInt = static_cast<int*>(pData); - *pInt = value; - return true; -} - float FX_atof(const ByteStringView& strc) { if (strc.IsEmpty()) return 0.0; diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h index 2cf823738a..670aa4d4b7 100644 --- a/core/fxcrt/fx_string.h +++ b/core/fxcrt/fx_string.h @@ -7,6 +7,8 @@ #ifndef CORE_FXCRT_FX_STRING_H_ #define CORE_FXCRT_FX_STRING_H_ +#include <stdint.h> + #include "core/fxcrt/bytestring.h" #include "core/fxcrt/widestring.h" @@ -19,7 +21,6 @@ WideString FX_UTF8Decode(const ByteStringView& bsStr); float FX_atof(const ByteStringView& str); float FX_atof(const WideStringView& wsStr); -bool FX_atonum(const ByteStringView& str, void* pData); size_t FX_ftoa(float f, char* buf); #endif // CORE_FXCRT_FX_STRING_H_ diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp index 60e7f07523..3059de3e5c 100644 --- a/core/fxcrt/fx_string_unittest.cpp +++ b/core/fxcrt/fx_string_unittest.cpp @@ -2,56 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <limits> - #include "core/fxcrt/fx_string.h" #include "testing/gtest/include/gtest/gtest.h" -TEST(fxstring, FX_atonum) { - int i; - EXPECT_TRUE(FX_atonum("10", &i)); - EXPECT_EQ(10, i); - - EXPECT_TRUE(FX_atonum("-10", &i)); - EXPECT_EQ(-10, i); - - EXPECT_TRUE(FX_atonum("+10", &i)); - EXPECT_EQ(10, i); - - EXPECT_TRUE(FX_atonum("-2147483648", &i)); - EXPECT_EQ(std::numeric_limits<int>::min(), i); - - EXPECT_TRUE(FX_atonum("2147483647", &i)); - EXPECT_EQ(2147483647, i); - - // Value overflows. - EXPECT_TRUE(FX_atonum("-2147483649", &i)); - EXPECT_EQ(0, i); - - // Value overflows. - EXPECT_TRUE(FX_atonum("+2147483648", &i)); - EXPECT_EQ(0, i); - - // Value overflows. - EXPECT_TRUE(FX_atonum("4223423494965252", &i)); - EXPECT_EQ(0, i); - - // No explicit sign will allow the number to go negative. This is for things - // like the encryption Permissions flag (Table 3.20 PDF 1.7 spec) - EXPECT_TRUE(FX_atonum("4294965252", &i)); - EXPECT_EQ(-2044, i); - - EXPECT_TRUE(FX_atonum("-4294965252", &i)); - EXPECT_EQ(0, i); - - EXPECT_TRUE(FX_atonum("+4294965252", &i)); - EXPECT_EQ(0, i); - - float f; - EXPECT_FALSE(FX_atonum("3.24", &f)); - EXPECT_FLOAT_EQ(3.24f, f); -} - TEST(fxstring, FX_UTF8Encode) { EXPECT_EQ("", FX_UTF8Encode(WideStringView())); EXPECT_EQ( |