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

Commit 7848d124 authored by William Loh's avatar William Loh
Browse files

Add check to validate component names

Only allow valid Java class names in component names when parsing the
manifest. This validation does not check for Java reserved words in the name.

Bug: 259946410
Test: atest AndroidPackageParsingValidationTest
Change-Id: I14514e6d4dbf4eff9230368aeaf4819b2ffba9ed
parent 86e546f9
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.os.SystemProperties.PROP_VALUE_MAX;

import android.annotation.NonNull;
import android.util.Pools.SimplePool;
import android.util.Slog;

import androidx.annotation.StyleableRes;

@@ -41,6 +42,7 @@ public class Element {
    public static final int MAX_ATTR_LEN_PATH = 4000;
    public static final int MAX_ATTR_LEN_DATA_VALUE = 4000;

    private static final String TAG = "PackageParsing";
    protected static final String TAG_ACTION = "action";
    protected static final String TAG_ACTIVITY = "activity";
    protected static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
@@ -715,22 +717,125 @@ public class Element {
        mChildTagMask |= 1 << idx;
    }

    private boolean isComponentNameAttr(String name) {
        switch (mTag) {
            case TAG_ACTIVITY:
                switch (name) {
                    case TAG_ATTR_NAME:
                    case TAG_ATTR_PARENT_ACTIVITY_NAME:
                        return true;
                    default:
                        return false;
                }
            case TAG_ACTIVITY_ALIAS:
                switch (name) {
                    case TAG_ATTR_TARGET_ACTIVITY:
                        return true;
                    default:
                        return false;
                }
            case TAG_APPLICATION:
                switch (name) {
                    case TAG_ATTR_BACKUP_AGENT:
                    case TAG_ATTR_NAME:
                        return true;
                    default:
                        return false;
                }
            case TAG_INSTRUMENTATION:
            case TAG_PROVIDER:
            case TAG_RECEIVER:
            case TAG_SERVICE:
            case TAG_USES_LIBRARY:
                switch (name) {
                    case TAG_ATTR_NAME:
                        return true;
                    default:
                        return false;
                }
            default:
                return false;
        }
    }

    private boolean isComponentNameAttr(@StyleableRes int index) {
        switch (mTag) {
            case TAG_ACTIVITY:
                return index == R.styleable.AndroidManifestActivity_name
                        || index == R.styleable.AndroidManifestActivity_parentActivityName;
            case TAG_ACTIVITY_ALIAS:
                return index == R.styleable.AndroidManifestActivityAlias_targetActivity;
            case TAG_APPLICATION:
                return index == R.styleable.AndroidManifestApplication_backupAgent
                        || index == R.styleable.AndroidManifestApplication_name;
            case TAG_INSTRUMENTATION:
                return index ==  R.styleable.AndroidManifestInstrumentation_name;
            case TAG_PROVIDER:
                return index ==  R.styleable.AndroidManifestProvider_name;
            case TAG_RECEIVER:
                return index ==  R.styleable.AndroidManifestReceiver_name;
            case TAG_SERVICE:
                return index ==  R.styleable.AndroidManifestService_name;
            case TAG_USES_LIBRARY:
                return index ==  R.styleable.AndroidManifestUsesLibrary_name;
            default:
                return false;
        }
    }

    boolean hasChild(String tag) {
        return (mChildTagMask & (1 << getCounterIdx(tag))) != 0;
    }

    void validateComponentName(CharSequence name) {
        int i = 0;
        if (name.charAt(0) == '.') {
            i = 1;
        }
        boolean isStart = true;
        for (; i < name.length(); i++) {
            if (name.charAt(i) == '.') {
                if (isStart) {
                    break;
                }
                isStart = true;
            } else {
                if (isStart) {
                    if (Character.isJavaIdentifierStart(name.charAt(i))) {
                        isStart = false;
                    } else {
                        break;
                    }
                } else if (!Character.isJavaIdentifierPart(name.charAt(i))) {
                    break;
                }
            }
        }
        if ((i < name.length()) || (name.charAt(name.length() - 1) == '.')) {
            Slog.e(TAG, name + " is not a valid Java class name");
            throw new SecurityException(name + " is not a valid Java class name");
        }
    }

    void validateStrAttr(String attrName, String attrValue) {
        if (attrValue != null && attrValue.length() > getAttrStrMaxLen(attrName)) {
            throw new SecurityException("String length limit exceeded for attribute " + attrName
                    + " in " + mTag);
        }
        if (isComponentNameAttr(attrName)) {
            validateComponentName(attrValue);
        }
    }

    void validateResStrAttr(@StyleableRes int index, CharSequence stringValue) {
        if (stringValue != null && stringValue.length() > getResStrMaxLen(index)) {
            throw new SecurityException("String length limit exceeded for attribute in " + mTag);
        }
        if (isComponentNameAttr(index)) {
            validateComponentName(stringValue);
        }
    }


    void seen(@NonNull Element element) {
        TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
+59 −1
Original line number Diff line number Diff line
@@ -52,7 +52,6 @@ class AndroidPackageParsingValidationTest {
            factory.isNamespaceAware = true
            factory.newPullParser()
        }
        private val ns = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
    }

    @Test
@@ -102,9 +101,13 @@ class AndroidPackageParsingValidationTest {
        val tag = "application"
        validateTagAttr(tag, "backupAgent",
            R.styleable.AndroidManifestApplication_backupAgent, 1024)
        validateTagAttrComponentName(tag, "backupAgent",
            R.styleable.AndroidManifestApplication_backupAgent)
        validateTagAttr(tag, "manageSpaceActivity",
            R.styleable.AndroidManifestApplication_manageSpaceActivity, 1024)
        validateTagAttr(tag, "name", R.styleable.AndroidManifestApplication_name, 1024)
        validateTagAttrComponentName(tag, "name",
            R.styleable.AndroidManifestApplication_name)
        validateTagAttr(tag, "permission", R.styleable.AndroidManifestApplication_permission, 1024)
        validateTagAttr(tag, "process", R.styleable.AndroidManifestApplication_process, 1024)
        validateTagAttr(tag, "requiredAccountType",
@@ -134,6 +137,7 @@ class AndroidPackageParsingValidationTest {
    fun parseReceiverTag() {
        val tag = "receiver"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestReceiver_name, 1024)
        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestReceiver_name)
        validateTagAttr(tag, "permission", R.styleable.AndroidManifestReceiver_permission, 1024)
        validateTagAttr(tag, "process", R.styleable.AndroidManifestReceiver_process, 1024)
        validateTagCount("meta-data", 1000, tag)
@@ -144,6 +148,7 @@ class AndroidPackageParsingValidationTest {
    fun parseServiceTag() {
        val tag = "service"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestService_name, 1024)
        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestService_name)
        validateTagAttr(tag, "permission", R.styleable.AndroidManifestService_permission, 1024)
        validateTagAttr(tag, "process", R.styleable.AndroidManifestService_process, 1024)
        validateTagCount("meta-data", 1000, tag)
@@ -158,6 +163,8 @@ class AndroidPackageParsingValidationTest {
            R.styleable.AndroidManifestActivityAlias_permission, 1024)
        validateTagAttr(tag, "targetActivity",
            R.styleable.AndroidManifestActivityAlias_targetActivity, 1024)
        validateTagAttrComponentName(tag, "targetActivity",
            R.styleable.AndroidManifestActivityAlias_targetActivity)
        validateTagCount("meta-data", 1000, tag)
        validateTagCount("intent-filter", 20000, tag)
    }
@@ -166,14 +173,18 @@ class AndroidPackageParsingValidationTest {
    fun parseUsesLibraryTag() {
        val tag = "uses-library"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesLibrary_name, 1024)
        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestUsesLibrary_name)
    }

    @Test
    fun parseActivityTag() {
        val tag = "activity"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestActivity_name, 1024)
        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestActivity_name)
        validateTagAttr(tag, "parentActivityName",
            R.styleable.AndroidManifestActivity_parentActivityName, 1024)
        validateTagAttrComponentName(tag, "parentActivityName",
            R.styleable.AndroidManifestActivity_parentActivityName)
        validateTagAttr(tag, "permission", R.styleable.AndroidManifestActivity_permission, 1024)
        validateTagAttr(tag, "process", R.styleable.AndroidManifestActivity_process, 1024)
        validateTagAttr(tag, "taskAffinity", R.styleable.AndroidManifestActivity_taskAffinity, 1024)
@@ -200,6 +211,8 @@ class AndroidPackageParsingValidationTest {
    fun parseInstrumentationTag() {
        val tag = "instrumentation"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestInstrumentation_name, 1024)
        validateTagAttrComponentName(tag, "name",
            R.styleable.AndroidManifestInstrumentation_name)
        validateTagAttr(tag, "targetPackage",
            R.styleable.AndroidManifestInstrumentation_targetPackage, 256)
        validateTagAttr(tag, "targetProcesses",
@@ -262,6 +275,7 @@ class AndroidPackageParsingValidationTest {
    fun parseProviderTag() {
        val tag = "provider"
        validateTagAttr(tag, "name", R.styleable.AndroidManifestProvider_name, 1024)
        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestProvider_name)
        validateTagAttr(tag, "permission", R.styleable.AndroidManifestProvider_permission, 1024)
        validateTagAttr(tag, "process", R.styleable.AndroidManifestProvider_process, 1024)
        validateTagAttr(tag, "readPermission",
@@ -361,6 +375,48 @@ class AndroidPackageParsingValidationTest {
        validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
    }

    private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
        val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
        for (name in passNames) {
            val xml = "<$tag $attr=\"$name\" />"
            pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
            val validator = Validator()
            pullParser.nextTag()
            validator.validate(pullParser)
            try {
                validator.validateStrAttr(pullParser, attr, name)
            } catch (e: SecurityException) {
                fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
                        " ${e.message}")
            }
            try {
                validator.validateResStrAttr(pullParser, index, name)
            } catch (e: SecurityException) {
                fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
                        " ${e.message}")
            }
        }

        val failNames = arrayOf("com.android.TestClass:", "-TestClass", "TestClass.", ".", "..")
        for (name in failNames) {
            val xml = "<$tag $attr=\"$name\" />"
            pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
            val validator = Validator()
            pullParser.nextTag()
            validator.validate(pullParser)
            val e1 = assertThrows("$name is not valid Java class name",
                SecurityException::class.java) {
                validator.validateStrAttr(pullParser, attr, name)
            }
            assertEquals(expectedAttrComponentNameErrorMsg(name), e1.message)
            val e2 = assertThrows("$name is not valid Java class name",
                SecurityException::class.java) {
                validator.validateResStrAttr(pullParser, index, name)
            }
            assertEquals(expectedAttrComponentNameErrorMsg(name), e2.message)
        }
    }

    private fun validateTagAttr(tag: String, name: String, index: Int?, maxLen: Int) {
        validateTagAttr_shouldPass(tag, name, index, maxLen)
        validateTagAttr_shouldFail(tag, name, index, maxLen)
@@ -468,4 +524,6 @@ class AndroidPackageParsingValidationTest {

    fun expectedResAttrLengthErrorMsg(tag: String) =
            "String length limit exceeded for attribute in $tag"

    fun expectedAttrComponentNameErrorMsg(name: String) = "$name is not a valid Java class name"
}