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

Commit 2c434422 authored by Rhed Jao's avatar Rhed Jao
Browse files

aapt2: Limit length of package name and shared user id

Package name and shared user id could be used as part of filename
as prefix by other modules. Limits the length to 223 and reserves
32 for the OS.

Bug: 118768971
Test: atest aapt2_tests
Test: aapt2 link -I android.jar --manifest ManifestLongPackageName.xml
Test: aapt2 link -I android.jar --manifest ManifestLongSharedUserId.xml
Change-Id: Ic4b5b4647b9e253b79b663f4d7a9050f43bb8cf0
parent ce2de700
Loading
Loading
Loading
Loading
+80 −27
Original line number Diff line number Diff line
@@ -1580,6 +1580,22 @@ class Linker {
    return true;
  }

  ResourceEntry* ResolveTableEntry(LinkContext* context, ResourceTable* table,
                                   Reference* reference) {
    if (!reference || !reference->name) {
      return nullptr;
    }
    auto name_ref = ResourceNameRef(reference->name.value());
    if (name_ref.package.empty()) {
      name_ref.package = context->GetCompilationPackage();
    }
    const auto search_result = table->FindResource(name_ref);
    if (!search_result) {
      return nullptr;
    }
    return search_result.value().entry;
  }

  void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
    const xml::Element* application = manifest->root->FindChild("", "application");
    if (!application) {
@@ -1594,22 +1610,13 @@ class Linker {

    // Find the icon resource defined within the application.
    const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
    if (!icon_reference || !icon_reference->name) {
      return;
    }

    auto icon_name = ResourceNameRef(icon_reference->name.value());
    if (icon_name.package.empty()) {
      icon_name.package = context_->GetCompilationPackage();
    }

    const auto icon_entry_result = table->FindResource(icon_name);
    if (!icon_entry_result) {
    const auto icon_entry = ResolveTableEntry(context_, table, icon_reference);
    if (!icon_entry) {
      return;
    }

    int icon_max_sdk = 0;
    for (auto& config_value : icon_entry_result.value().entry->values) {
    for (auto& config_value : icon_entry->values) {
      icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
          ? config_value->config.sdkVersion : icon_max_sdk;
    }
@@ -1620,22 +1627,13 @@ class Linker {

    // Find the roundIcon resource defined within the application.
    const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
    if (!round_icon_reference || !round_icon_reference->name) {
      return;
    }

    auto round_icon_name = ResourceNameRef(round_icon_reference->name.value());
    if (round_icon_name.package.empty()) {
      round_icon_name.package = context_->GetCompilationPackage();
    }

    const auto round_icon_entry_result = table->FindResource(round_icon_name);
    if (!round_icon_entry_result) {
    const auto round_icon_entry = ResolveTableEntry(context_, table, round_icon_reference);
    if (!round_icon_entry) {
      return;
    }

    int round_icon_max_sdk = 0;
    for (auto& config_value : round_icon_entry_result.value().entry->values) {
    for (auto& config_value : round_icon_entry->values) {
      round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
                     ? config_value->config.sdkVersion : round_icon_max_sdk;
    }
@@ -1646,7 +1644,7 @@ class Linker {
    }

    // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
    for (auto& config_value : icon_entry_result.value().entry->values) {
    for (auto& config_value : icon_entry->values) {
      if (config_value->config.sdkVersion < SDK_O) {
        continue;
      }
@@ -1657,12 +1655,62 @@ class Linker {
                                                     << "\" for round icon compatibility");

      auto value = icon_reference->Clone(&table->string_pool);
      auto round_config_value = round_icon_entry_result.value().entry->FindOrCreateValue(
          config_value->config, config_value->product);
      auto round_config_value =
          round_icon_entry->FindOrCreateValue(config_value->config, config_value->product);
      round_config_value->value.reset(value);
    }
  }

  bool VerifySharedUserId(xml::XmlResource* manifest, ResourceTable* table) {
    const xml::Element* manifest_el = xml::FindRootElement(manifest->root.get());
    if (manifest_el == nullptr) {
      return true;
    }
    if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
      return true;
    }
    const xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "sharedUserId");
    if (!attr) {
      return true;
    }
    const auto validate = [&](const std::string& shared_user_id) -> bool {
      if (util::IsAndroidSharedUserId(context_->GetCompilationPackage(), shared_user_id)) {
        return true;
      }
      DiagMessage error_msg(manifest_el->line_number);
      error_msg << "attribute 'sharedUserId' in <manifest> tag is not a valid shared user id: '"
                << shared_user_id << "'";
      if (options_.manifest_fixer_options.warn_validation) {
        // Treat the error only as a warning.
        context_->GetDiagnostics()->Warn(error_msg);
        return true;
      }
      context_->GetDiagnostics()->Error(error_msg);
      return false;
    };
    // If attr->compiled_value is not null, check if it is a ref
    if (attr->compiled_value) {
      const auto ref = ValueCast<Reference>(attr->compiled_value.get());
      if (ref == nullptr) {
        return true;
      }
      const auto shared_user_id_entry = ResolveTableEntry(context_, table, ref);
      if (!shared_user_id_entry) {
        return true;
      }
      for (const auto& value : shared_user_id_entry->values) {
        const auto str_value = ValueCast<String>(value->value.get());
        if (str_value != nullptr && !validate(*str_value->value)) {
          return false;
        }
      }
      return true;
    }

    // Fallback to checking the raw value
    return validate(attr->value);
  }

  // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
  // to the IArchiveWriter.
  bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
@@ -1684,6 +1732,11 @@ class Linker {
    // See (b/34829129)
    AliasAdaptiveIcon(manifest, table);

    // Verify the shared user id here to handle the case of reference value.
    if (!VerifySharedUserId(manifest, table)) {
      return false;
    }

    ResourceFileFlattenerOptions file_flattener_options;
    file_flattener_options.keep_raw_values = keep_raw_values;
    file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
+12 −5
Original line number Diff line number Diff line
@@ -142,7 +142,8 @@ static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics*
  return true;
}

static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy,
                           SourcePathDiagnostics* diag) {
  xml::Attribute* attr = el->FindAttribute({}, "package");
  if (!attr) {
    diag->Error(DiagMessage(el->line_number)
@@ -153,11 +154,17 @@ static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
                << "attribute 'package' in <manifest> tag must not be a reference");
    return false;
  } else if (!util::IsAndroidPackageName(attr->value)) {
    diag->Error(DiagMessage(el->line_number)
                << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
                << attr->value << "'");
    DiagMessage error_msg(el->line_number);
    error_msg << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
              << attr->value << "'";
    if (policy == xml::XmlActionExecutorPolicy::kAllowListWarning) {
      // Treat the error only as a warning.
      diag->Warn(error_msg);
    } else {
      diag->Error(error_msg);
      return false;
    }
  }

  attr = el->FindAttribute({}, "split");
  if (attr) {
+17 −0
Original line number Diff line number Diff line
@@ -38,6 +38,11 @@ using ::android::StringPiece16;
namespace aapt {
namespace util {

// Package name and shared user id would be used as a part of the file name.
// Limits size to 223 and reserves 32 for the OS.
// See frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
constexpr static const size_t kMaxPackageNameSize = 223;

static std::vector<std::string> SplitAndTransform(
    const StringPiece& str, char sep, const std::function<char(char)>& f) {
  std::vector<std::string> parts;
@@ -169,9 +174,21 @@ static int IsAndroidNameImpl(const StringPiece& str) {
}

bool IsAndroidPackageName(const StringPiece& str) {
  if (str.size() > kMaxPackageNameSize) {
    return false;
  }
  return IsAndroidNameImpl(str) > 1 || str == "android";
}

bool IsAndroidSharedUserId(const android::StringPiece& package_name,
                           const android::StringPiece& shared_user_id) {
  if (shared_user_id.size() > kMaxPackageNameSize) {
    return false;
  }
  return shared_user_id.empty() || IsAndroidNameImpl(shared_user_id) > 1 ||
         package_name == "android";
}

bool IsAndroidSplitName(const StringPiece& str) {
  return IsAndroidNameImpl(str) > 0;
}
+10 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ bool IsJavaPackageName(const android::StringPiece& str);
// - First character of each component (separated by '.') must be an ASCII letter.
// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
// - Package must contain at least two components, unless it is 'android'.
// - The maximum package name length is 223.
bool IsAndroidPackageName(const android::StringPiece& str);

// Tests that the string is a valid Android split name.
@@ -88,6 +89,15 @@ bool IsAndroidPackageName(const android::StringPiece& str);
// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
bool IsAndroidSplitName(const android::StringPiece& str);

// Tests that the string is a valid Android shared user id.
// - First character of each component (separated by '.') must be an ASCII letter.
// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
// - Must contain at least two components, unless package name is 'android'.
// - The maximum shared user id length is 223.
// - Treat empty string as valid, it's the case of no shared user id.
bool IsAndroidSharedUserId(const android::StringPiece& package_name,
                           const android::StringPiece& shared_user_id);

// Converts the class name to a fully qualified class name from the given
// `package`. Ex:
//
+33 −0
Original line number Diff line number Diff line
@@ -27,6 +27,17 @@ using ::testing::SizeIs;

namespace aapt {

// Test that a max package name size 223 is valid.
static const std::string kMaxPackageName =
    "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQux"
    "SoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O"
    "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6l";
// Test that a long package name size 224 is invalid.
static const std::string kLongPackageName =
    "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQu"
    "xSoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O"
    "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6le";

TEST(UtilTest, TrimOnlyWhitespace) {
  const StringPiece trimmed = util::TrimWhitespace("\n        ");
  EXPECT_TRUE(trimmed.empty());
@@ -108,6 +119,7 @@ TEST(UtilTest, IsAndroidPackageName) {
  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing"));
  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_"));
  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_"));
  EXPECT_TRUE(util::IsAndroidPackageName(kMaxPackageName));

  EXPECT_FALSE(util::IsAndroidPackageName("android._test"));
  EXPECT_FALSE(util::IsAndroidPackageName("com"));
@@ -116,6 +128,27 @@ TEST(UtilTest, IsAndroidPackageName) {
  EXPECT_FALSE(util::IsAndroidPackageName(".android"));
  EXPECT_FALSE(util::IsAndroidPackageName(".."));
  EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo"));
  EXPECT_FALSE(util::IsAndroidPackageName(kLongPackageName));
}

TEST(UtilTest, IsAndroidSharedUserId) {
  EXPECT_TRUE(util::IsAndroidSharedUserId("android", "foo"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "android.test"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_thing"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.testing_thing_"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_99_"));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", ""));
  EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", kMaxPackageName));

  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android._test"));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "com"));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "_android"));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android."));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", ".android"));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", ".."));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "cøm.foo"));
  EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", kLongPackageName));
}

TEST(UtilTest, FullyQualifiedClassName) {
Loading