Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c674d38c authored by Brandon Liu's avatar Brandon Liu
Browse files

Add additional check on float precision after parsing, only compile the

value to a float when the difference between float and double parsed
from same raw string is smaller than 1.

Bug: b/69347762
Test: Verified affected atests pass and added new atest
Change-Id: I25da0baccba580484db39aa2d0a1bb765706635d
parent e1e4129f
Loading
Loading
Loading
Loading
+45 −16
Original line number Diff line number Diff line
@@ -5436,37 +5436,66 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue)
    return U16StringToInt(s, len, outValue);
}

bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
{
    while (len > 0 && isspace16(*s)) {
        s++;
        len--;
template <typename T>
bool parseFloatingPoint(const char16_t* inBuf, size_t inLen, char* tempBuf,
                                  const char** outEnd, T& out){
    while (inLen > 0 && isspace16(*inBuf)) {
        inBuf++;
        inLen--;
    }

    if (len <= 0) {
    if (inLen <= 0) {
        return false;
    }

    char buf[128];
    int i=0;
    while (len > 0 && *s != 0 && i < 126) {
        if (*s > 255) {
    while (inLen > 0 && *inBuf != 0 && i < 126) {
        if (*inBuf > 255) {
            return false;
        }
        buf[i++] = *s++;
        len--;
        tempBuf[i++] = *inBuf++;
        inLen--;
    }

    if (len > 0) {
    if (inLen > 0) {
        return false;
    }
    if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
    if ((tempBuf[0] < '0' || tempBuf[0] > '9') && tempBuf[0] != '.' && tempBuf[0] != '-' && tempBuf[0] != '+') {
        return false;
    }

    buf[i] = 0;
    const char* end;
    float f = strtof(buf, (char**)&end);
    tempBuf[i] = 0;
    if constexpr(std::is_same_v<T, float>) {
        out = strtof(tempBuf, (char**)outEnd);
    } else {
        out = strtod(tempBuf, (char**)outEnd);
    }
    return true;
}

bool ResTable::stringToDouble(const char16_t* s, size_t len, double& d){
    char buf[128];
    const char* end = nullptr;
    if (!parseFloatingPoint(s, len, buf, &end, d)) {
        return false;
    }

    while (*end != 0 && isspace((unsigned char)*end)) {
        end++;
    }

    return *end == 0;
}

bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue)
{
    char buf[128];
    const char* end = nullptr;
    float f;

    if (!parseFloatingPoint(s, len, buf, &end, f)) {
        return false;
    }

    if (*end != 0 && !isspace((unsigned char)*end)) {
        // Might be a unit...
+1 −0
Original line number Diff line number Diff line
@@ -2162,6 +2162,7 @@ public:

    static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue);
    static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue);
    static bool stringToDouble(const char16_t* s, size_t len, double& outValue);

    // Used with stringToValue.
    class Accessor
+19 −1
Original line number Diff line number Diff line
@@ -670,10 +670,28 @@ std::unique_ptr<Item> TryParseItemForAttribute(
    // Try parsing this as a float.
    auto floating_point = TryParseFloat(value);
    if (floating_point) {
      // Only check if the parsed result lost precision when the parsed item is
      // android::Res_value::TYPE_FLOAT and there is other possible types saved in type_mask, like
      // ResTable_map::TYPE_INTEGER.
      if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
        const bool mayOnlyBeFloat = (type_mask & ~float_mask) == 0;
        const bool parsedAsFloat = floating_point->value.dataType == android::Res_value::TYPE_FLOAT;
        if (!mayOnlyBeFloat && parsedAsFloat) {
          float f = reinterpret_cast<float&>(floating_point->value.data);
          std::u16string str16 = android::util::Utf8ToUtf16(util::TrimWhitespace(value));
          double d;
          if (android::ResTable::stringToDouble(str16.data(), str16.size(), d)) {
            // Parse as a float only if the difference between float and double parsed from the
            // same string is smaller than 1, otherwise return as raw string.
            if (fabs(f - d) < 1) {
              return std::move(floating_point);
            }
          }
        } else {
          return std::move(floating_point);
        }
      }
    }
  }
  return {};
}
+20 −0
Original line number Diff line number Diff line
@@ -228,6 +228,26 @@ TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) {
              Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
}

TEST(ResourceUtilsTest, FloatAndBigIntegerParsedCorrectly) {
  const float expected_float = 0.125f;
  const uint32_t expected_float_flattened = *(uint32_t*)&expected_float;
  EXPECT_THAT(ResourceUtils::TryParseItemForAttribute("0.125", ResTable_map::TYPE_FLOAT),
              Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));

  const float special_float = 1.0f;
  const uint32_t special_float_flattened = *(uint32_t*)&special_float;
  EXPECT_THAT(ResourceUtils::TryParseItemForAttribute("1.0", ResTable_map::TYPE_FLOAT),
              Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, special_float_flattened))));

  EXPECT_EQ(ResourceUtils::TryParseItemForAttribute("1099511627776", ResTable_map::TYPE_INTEGER),
            std::unique_ptr<Item>(nullptr));

  const float big_float = 1099511627776.0f;
  const uint32_t big_flattened = *(uint32_t*)&big_float;
  EXPECT_THAT(ResourceUtils::TryParseItemForAttribute("1099511627776", ResTable_map::TYPE_FLOAT),
              Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, big_flattened))));
}

TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
  EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(std::optional<int>(10000)));
  EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q.fingerprint"), Eq(std::optional<int>(10000)));
+18 −1
Original line number Diff line number Diff line
@@ -439,6 +439,21 @@ static std::string ComplexToString(uint32_t complex_value, bool fraction) {
  return str;
}

// This function is designed to using different specifier to print different floats,
// which can print more accurate format rather than using %g only.
const char* BinaryPrimitive::DecideFormat(float f) {
  // if the float is either too big or too tiny, print it in scientific notation.
  // eg: "10995116277760000000000" to 1.099512e+22, "0.00000000001" to 1.000000e-11
  if (fabs(f) > std::numeric_limits<int64_t>::max() || fabs(f) < 1e-10) {
    return "%e";
    // Else if the number is an integer exactly, print it without trailing zeros.
    // eg: "1099511627776" to 1099511627776
  } else if (int64_t(f) == f) {
    return "%.0f";
  }
  return "%g";
}

void BinaryPrimitive::PrettyPrint(Printer* printer) const {
  using ::android::Res_value;
  switch (value.dataType) {
@@ -470,7 +485,9 @@ void BinaryPrimitive::PrettyPrint(Printer* printer) const {
      break;

    case Res_value::TYPE_FLOAT:
      printer->Print(StringPrintf("%g", *reinterpret_cast<const float*>(&value.data)));
      float f;
      f = *reinterpret_cast<const float*>(&value.data);
      printer->Print(StringPrintf(DecideFormat(f), f));
      break;

    case Res_value::TYPE_DIMENSION:
Loading