Merge "Limit max meta data string values per component." into main
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 1667896..38dbec5 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -38,9 +38,11 @@
private static final int MAX_ATTR_LEN_PERMISSION_GROUP = 256;
private static final int MAX_ATTR_LEN_PACKAGE = 256;
private static final int MAX_ATTR_LEN_MIMETYPE = 512;
- public static final int MAX_ATTR_LEN_NAME = 1024;
- public static final int MAX_ATTR_LEN_PATH = 4000;
- public static final int MAX_ATTR_LEN_VALUE = 32_768;
+ private static final int MAX_ATTR_LEN_NAME = 1024;
+ private static final int MAX_ATTR_LEN_PATH = 4000;
+ private static final int MAX_ATTR_LEN_VALUE = 32_768;
+
+ private static final int MAX_TOTAL_META_DATA_SIZE = 262_144;
private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?%^*|/\\";
@@ -157,6 +159,7 @@
}
private long mChildTagMask = 0;
+ private int mTotalComponentMetadataSize = 0;
private static int getCounterIdx(String tag) {
switch(tag) {
@@ -283,6 +286,7 @@
private void init(String tag) {
this.mTag = tag;
mChildTagMask = 0;
+ mTotalComponentMetadataSize = 0;
switch (tag) {
case TAG_ACTIVITY:
initializeCounter(TAG_LAYOUT, 1000);
@@ -820,6 +824,12 @@
}
}
+ void validateComponentMetadata(String value) {
+ mTotalComponentMetadataSize += value.length();
+ if (mTotalComponentMetadataSize > MAX_TOTAL_META_DATA_SIZE) {
+ throw new SecurityException("Max total meta data size limit exceeded for " + mTag);
+ }
+ }
void seen(@NonNull Element element) {
TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
index cae353b..3b68452 100644
--- a/core/java/android/content/res/Validator.java
+++ b/core/java/android/content/res/Validator.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.StyleableRes;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,6 +86,9 @@
return;
}
mElements.peek().validateResStrAttr(index, stringValue);
+ if (index == R.styleable.AndroidManifestMetaData_value) {
+ validateComponentMetadata(stringValue.toString());
+ }
}
/**
@@ -94,5 +99,20 @@
return;
}
mElements.peek().validateStrAttr(attrName, attrValue);
+ if (attrName.equals(Element.TAG_ATTR_VALUE)) {
+ validateComponentMetadata(attrValue);
+ }
+ }
+
+ private void validateComponentMetadata(String attrValue) {
+ Element element = mElements.peek();
+ // Meta-data values are evaluated on the parent element which is the next element in the
+ // mElements stack after the meta-data element. The top of the stack is always the current
+ // element being validated so check that the top element is meta-data.
+ if (element.mTag.equals(Element.TAG_META_DATA) && mElements.size() > 1) {
+ element = mElements.pop();
+ mElements.peek().validateComponentMetadata(attrValue);
+ mElements.push(element);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index cf2b748..ee23a00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -492,6 +492,40 @@
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
}
+ @Test
+ fun totalMetadataValuesExceedMax_shouldFail() {
+ val value = "x".repeat(8192)
+ var tags = ""
+ repeat(32) { index ->
+ tags += "<meta-data name=\"name$index\" value=\"$value\" />"
+ }
+ var xml = "<application>$tags</application>"
+ try {
+ parseXmlForMetadata(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ try {
+ parseXmlForMetadataRes(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ tags += "<meta-data name=\"last\" value=\"x\" />"
+ xml = "<application>$tags</application>"
+ var e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadata(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadataRes(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ }
+
private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
for (name in passNames) {
@@ -664,6 +698,38 @@
} while (type != XmlPullParser.END_DOCUMENT)
}
+ fun parseXmlForMetadata(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateStrAttr(pullParser, "value", value)
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
+ fun parseXmlForMetadataRes(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateResStrAttr(
+ pullParser,
+ R.styleable.AndroidManifestMetaData_value,
+ value
+ )
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
fun expectedCountErrorMsg(tag: String, parentTag: String) =
"The number of child $tag elements exceeded the max allowed in $parentTag"