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

Commit 7281fe0a authored by Ryan Mitchell's avatar Ryan Mitchell Committed by Android (Google) Code Review
Browse files

Merge "Check the size of the strings in the StringPool before flattening." into pi-dev

parents 71cba7d0 70414f22
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -225,7 +225,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
      }
    } else if (format_ == ApkFormat::kProto && path == kProtoResourceTablePath) {
      pb::ResourceTable pb_table;
      SerializeTableToPb(*split_table, &pb_table);
      SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics());
      if (!io::CopyProtoToArchive(context,
                                  &pb_table,
                                  path,
+78 −29
Original line number Diff line number Diff line
@@ -332,6 +332,25 @@ static T* EncodeLength(T* data, size_t length) {
  return data;
}

/**
 * Returns the maximum possible string length that can be successfully encoded
 * using 2 units of the specified T.
 *    EncodeLengthMax<char> -> maximum unit length of 0x7FFF
 *    EncodeLengthMax<char16_t> -> maximum unit length of 0x7FFFFFFF
 **/
template <typename T>
static size_t EncodeLengthMax() {
  static_assert(std::is_integral<T>::value, "wat.");

  constexpr size_t kMask = 1 << ((sizeof(T) * 8 * 2) - 1);
  constexpr size_t max = kMask - 1;
  return max;
}

/**
 * Returns the number of units (1 or 2) needed to encode the string length
 * before writing the string.
 */
template <typename T>
static size_t EncodedLengthUnits(size_t length) {
  static_assert(std::is_integral<T>::value, "wat.");
@@ -341,15 +360,30 @@ static size_t EncodedLengthUnits(size_t length) {
  return length > kMaxSize ? 2 : 1;
}

static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out) {
const std::string kStringTooLarge = "STRING_TOO_LARGE";

static bool EncodeString(const std::string& str, const bool utf8, BigBuffer* out,
                         IDiagnostics* diag) {
  if (utf8) {
    const std::string& encoded = str;
    const ssize_t utf16_length =
        utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size());
    const ssize_t utf16_length = utf8_to_utf16_length(
        reinterpret_cast<const uint8_t*>(encoded.data()), encoded.size());
    CHECK(utf16_length >= 0);

    const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
                              EncodedLengthUnits<char>(encoded.length()) + encoded.size() + 1;
    // Make sure the lengths to be encoded do not exceed the maximum length that
    // can be encoded using chars
    if ((((size_t)encoded.size()) > EncodeLengthMax<char>())
        || (((size_t)utf16_length) > EncodeLengthMax<char>())) {

      diag->Error(DiagMessage() << "string too large to encode using UTF-8 "
          << "written instead as '" << kStringTooLarge << "'");

      EncodeString(kStringTooLarge, utf8, out, diag);
      return false;
    }

    const size_t total_size = EncodedLengthUnits<char>(utf16_length)
        + EncodedLengthUnits<char>(encoded.size()) + encoded.size() + 1;

    char* data = out->NextBlock<char>(total_size);

@@ -357,15 +391,26 @@ static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out
    data = EncodeLength(data, utf16_length);

    // Now encode the size of the real UTF8 string.
    data = EncodeLength(data, encoded.length());
    data = EncodeLength(data, encoded.size());
    strncpy(data, encoded.data(), encoded.size());

  } else {
    const std::u16string encoded = util::Utf8ToUtf16(str);
    const ssize_t utf16_length = encoded.size();

    // Make sure the length to be encoded does not exceed the maximum possible
    // length that can be encoded
    if (((size_t)utf16_length) > EncodeLengthMax<char16_t>()) {
      diag->Error(DiagMessage() << "string too large to encode using UTF-16 "
          << "written instead as '" << kStringTooLarge << "'");

      EncodeString(kStringTooLarge, utf8, out, diag);
      return false;
    }

    // Total number of 16-bit words to write.
      const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1;
    const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length)
        + encoded.size() + 1;

    char16_t* data = out->NextBlock<char16_t>(total_size);

@@ -380,9 +425,13 @@ static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out
    // The null-terminating character is already here due to the block of data
    // being set to 0s on allocation.
  }

  return true;
}

bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8,
                         IDiagnostics* diag) {
  bool no_error = true;
  const size_t start_index = out->size();
  android::ResStringPool_header* header = out->NextBlock<android::ResStringPool_header>();
  header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE);
@@ -403,12 +452,12 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
  // Styles always come first.
  for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
    *indices++ = out->size() - before_strings_index;
    EncodeString(entry->value, utf8, out);
    no_error = EncodeString(entry->value, utf8, out, diag) && no_error;
  }

  for (const std::unique_ptr<Entry>& entry : pool.strings_) {
    *indices++ = out->size() - before_strings_index;
    EncodeString(entry->value, utf8, out);
    no_error = EncodeString(entry->value, utf8, out, diag) && no_error;
  }

  out->Align4();
@@ -446,15 +495,15 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
    out->Align4();
  }
  header->header.size = util::HostToDevice32(out->size() - start_index);
  return true;
  return no_error;
}

bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool) {
  return Flatten(out, pool, true);
bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) {
  return Flatten(out, pool, true, diag);
}

bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool) {
  return Flatten(out, pool, false);
bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) {
  return Flatten(out, pool, false, diag);
}

}  // namespace aapt
+4 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include "androidfw/StringPiece.h"

#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "util/BigBuffer.h"

namespace aapt {
@@ -152,8 +153,8 @@ class StringPool {
    int ref_;
  };

  static bool FlattenUtf8(BigBuffer* out, const StringPool& pool);
  static bool FlattenUtf16(BigBuffer* out, const StringPool& pool);
  static bool FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag);
  static bool FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag);

  StringPool() = default;
  StringPool(StringPool&&) = default;
@@ -207,7 +208,7 @@ class StringPool {
 private:
  DISALLOW_COPY_AND_ASSIGN(StringPool);

  static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
  static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);

  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
  void ReAssignIndices();
+57 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#include "androidfw/StringPiece.h"

#include "Diagnostics.h"
#include "test/Test.h"
#include "util/Util.h"

@@ -188,10 +189,11 @@ TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) {

TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
  using namespace android;  // For NO_ERROR on Windows.
  StdErrDiagnostics diag;

  StringPool pool;
  BigBuffer buffer(1024);
  StringPool::FlattenUtf8(&buffer, pool);
  StringPool::FlattenUtf8(&buffer, pool, &diag);

  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
  ResStringPool test;
@@ -200,11 +202,12 @@ TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {

TEST(StringPoolTest, FlattenOddCharactersUtf16) {
  using namespace android;  // For NO_ERROR on Windows.
  StdErrDiagnostics diag;

  StringPool pool;
  pool.MakeRef("\u093f");
  BigBuffer buffer(1024);
  StringPool::FlattenUtf16(&buffer, pool);
  StringPool::FlattenUtf16(&buffer, pool, &diag);

  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
  ResStringPool test;
@@ -225,6 +228,7 @@ constexpr const char* sLongString =

TEST(StringPoolTest, Flatten) {
  using namespace android;  // For NO_ERROR on Windows.
  StdErrDiagnostics diag;

  StringPool pool;

@@ -244,8 +248,8 @@ TEST(StringPoolTest, Flatten) {
  EXPECT_THAT(ref_d.index(), Eq(4u));

  BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};
  StringPool::FlattenUtf8(&buffers[0], pool);
  StringPool::FlattenUtf16(&buffers[1], pool);
  StringPool::FlattenUtf8(&buffers[0], pool, &diag);
  StringPool::FlattenUtf16(&buffers[1], pool, &diag);

  // Test both UTF-8 and UTF-16 buffers.
  for (const BigBuffer& buffer : buffers) {
@@ -288,4 +292,53 @@ TEST(StringPoolTest, Flatten) {
  }
}


TEST(StringPoolTest, MaxEncodingLength) {
  StdErrDiagnostics diag;
  using namespace android;  // For NO_ERROR on Windows.
  ResStringPool test;

  StringPool pool;
  pool.MakeRef("aaaaaaaaaa");
  BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)};

  // Make sure a UTF-8 string under the maximum length does not produce an error
  EXPECT_THAT(StringPool::FlattenUtf8(&buffers[0], pool, &diag), Eq(true));
  std::unique_ptr<uint8_t[]> data = util::Copy(buffers[0]);
  test.setTo(data.get(), buffers[0].size());
  EXPECT_THAT(util::GetString(test, 0), Eq("aaaaaaaaaa"));

  // Make sure a UTF-16 string under the maximum length does not produce an error
  EXPECT_THAT(StringPool::FlattenUtf16(&buffers[1], pool, &diag), Eq(true));
  data = util::Copy(buffers[1]);
  test.setTo(data.get(), buffers[1].size());
  EXPECT_THAT(util::GetString16(test, 0), Eq(u"aaaaaaaaaa"));

  StringPool pool2;
  std::string longStr(50000, 'a');
  pool2.MakeRef("this fits1");
  pool2.MakeRef(longStr);
  pool2.MakeRef("this fits2");
  BigBuffer buffers2[2] = {BigBuffer(1024), BigBuffer(1024)};

  // Make sure a string that exceeds the maximum length of UTF-8 produces an
  // error and writes a shorter error string instead
  EXPECT_THAT(StringPool::FlattenUtf8(&buffers2[0], pool2, &diag), Eq(false));
  data = util::Copy(buffers2[0]);
  test.setTo(data.get(), buffers2[0].size());
  EXPECT_THAT(util::GetString(test, 0), "this fits1");
  EXPECT_THAT(util::GetString(test, 1), "STRING_TOO_LARGE");
  EXPECT_THAT(util::GetString(test, 2), "this fits2");

  // Make sure a string that a string that exceeds the maximum length of UTF-8
  // but not UTF-16 does not error for UTF-16
  StringPool pool3;
  std::u16string longStr16(50000, 'a');
  pool3.MakeRef(longStr);
  EXPECT_THAT(StringPool::FlattenUtf16(&buffers2[1], pool3, &diag), Eq(true));
  data = util::Copy(buffers2[1]);
  test.setTo(data.get(), buffers2[1].size());
  EXPECT_THAT(util::GetString16(test, 0), Eq(longStr16));
}

}  // namespace aapt
+1 −1
Original line number Diff line number Diff line
@@ -258,7 +258,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
    ContainerWriter container_writer(&copying_adaptor, 1u);

    pb::ResourceTable pb_table;
    SerializeTableToPb(table, &pb_table);
    SerializeTableToPb(table, &pb_table, context->GetDiagnostics());
    if (!container_writer.AddResTableEntry(pb_table)) {
      context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
      return false;
Loading