Fix segfault with sparse encoding
The native code that underlies `Resources#getIdentifier()` did not take
sparse encoding into account when calculating offsets, resulting in
either garbage or SEGV_MAPERR whenever a resource is first encountered
in a sparsely encoded configuration.
Bug: 197976367
Test: atest libandroidfw_tests --host
Change-Id: Ib7550fe2e05005550f59129a06be5712b74bc9c8
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index d17c328..446e580 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -384,7 +384,16 @@
return base::unexpected(IOError::PAGES_MISSING);
}
- auto offset = dtohl(entry_offset_ptr.value());
+ uint32_t offset;
+ uint16_t res_idx;
+ if (type->flags & ResTable_type::FLAG_SPARSE) {
+ auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+ offset = dtohs(sparse_entry->offset) * 4u;
+ res_idx = dtohs(sparse_entry->idx);
+ } else {
+ offset = dtohl(entry_offset_ptr.value());
+ res_idx = entry_idx;
+ }
if (offset != ResTable_type::NO_ENTRY) {
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
if (!entry) {
@@ -394,7 +403,7 @@
if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
- return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
+ return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
}
}
}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f356c8130..d214e2d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -95,6 +95,38 @@
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
}
+TEST(LoadedArscTest, FindSparseEntryApp) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
+ &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26));
+ ASSERT_THAT(package, NotNull());
+
+ const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1;
+ const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+ // Ensure that AAPT2 sparsely encoded the v26 config as expected.
+ auto type_entry = std::find_if(
+ type_spec->type_entries.begin(), type_spec->type_entries.end(),
+ [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; });
+ ASSERT_NE(type_entry, type_spec->type_entries.end());
+ ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+ // Test fetching a resource with only sparsely encoded configs by name.
+ auto id = package->FindEntryByName(u"string", u"only_v26");
+ ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0));
+}
+
TEST(LoadedArscTest, LoadSharedLibrary) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h
index 243e74f..2492dbf 100644
--- a/libs/androidfw/tests/data/sparse/R.h
+++ b/libs/androidfw/tests/data/sparse/R.h
@@ -27,21 +27,22 @@
struct integer {
enum : uint32_t {
foo_0 = 0x7f010000,
- foo_1 = 0x7f010000,
- foo_2 = 0x7f010000,
- foo_3 = 0x7f010000,
- foo_4 = 0x7f010000,
- foo_5 = 0x7f010000,
- foo_6 = 0x7f010000,
- foo_7 = 0x7f010000,
- foo_8 = 0x7f010000,
- foo_9 = 0x7f010000,
+ foo_1 = 0x7f010001,
+ foo_2 = 0x7f010002,
+ foo_3 = 0x7f010003,
+ foo_4 = 0x7f010004,
+ foo_5 = 0x7f010005,
+ foo_6 = 0x7f010006,
+ foo_7 = 0x7f010007,
+ foo_8 = 0x7f010008,
+ foo_9 = 0x7f010009,
};
};
struct string {
enum : uint32_t {
foo_999 = 0x7f0203e7,
+ only_v26 = 0x7f0203e8
};
};
};
diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh
index e7e1d60..4ea5468 100755
--- a/libs/androidfw/tests/data/sparse/gen_strings.sh
+++ b/libs/androidfw/tests/data/sparse/gen_strings.sh
@@ -14,5 +14,7 @@
fi
done
echo "</resources>" >> $OUTPUT_default
+
+echo " <string name=\"only_v26\">only v26</string>" >> $OUTPUT_v26
echo "</resources>" >> $OUTPUT_v26
diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk
index 599a370..b08a621 100644
--- a/libs/androidfw/tests/data/sparse/not_sparse.apk
+++ b/libs/androidfw/tests/data/sparse/not_sparse.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
index b6f8299..d116087e 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
@@ -333,4 +333,5 @@
<string name="foo_993">9930</string>
<string name="foo_996">9960</string>
<string name="foo_999">9990</string>
+ <string name="only_v26">only v26</string>
</resources>
diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk
index 1f9bba3..9fd01fb 100644
--- a/libs/androidfw/tests/data/sparse/sparse.apk
+++ b/libs/androidfw/tests/data/sparse/sparse.apk
Binary files differ