diff options
author | 2023-11-15 22:02:54 +0000 | |
---|---|---|
committer | 2023-11-15 22:02:54 +0000 | |
commit | 9e79ad8b01c80448121ddde60d9d596b704bad1e (patch) | |
tree | 02635ea2f6c6fecd395cec015a560d7197204920 | |
parent | 177a544dde4e0a5f619d74182d651cc37dd6606c (diff) | |
parent | 21c90e0c9342b93f2a47fb460b2d2306625ea88a (diff) |
Merge "Limit max meta data string values per component." into main
3 files changed, 99 insertions, 3 deletions
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java index 1667896930c2..38dbec5a5529 100644 --- a/core/java/android/content/res/Element.java +++ b/core/java/android/content/res/Element.java @@ -38,9 +38,11 @@ public class Element { 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 @@ public class Element { } private long mChildTagMask = 0; + private int mTotalComponentMetadataSize = 0; private static int getCounterIdx(String tag) { switch(tag) { @@ -283,6 +286,7 @@ public class Element { 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 @@ public class Element { } } + 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 cae353b3bd5a..3b68452b1a5c 100644 --- a/core/java/android/content/res/Validator.java +++ b/core/java/android/content/res/Validator.java @@ -19,6 +19,8 @@ package android.content.res; 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 @@ public class Validator { return; } mElements.peek().validateResStrAttr(index, stringValue); + if (index == R.styleable.AndroidManifestMetaData_value) { + validateComponentMetadata(stringValue.toString()); + } } /** @@ -94,5 +99,20 @@ public class Validator { 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 cf2b748f7752..ee23a00f27d7 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 @@ class AndroidPackageParsingValidationTest { 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 @@ class AndroidPackageParsingValidationTest { } 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" |