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"