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

Commit 58a3327f authored by Jeff Chang's avatar Jeff Chang
Browse files

Only allow system and same app to apply relinquishTaskIdentity

Any malicious application could hijack tasks by
android:relinquishTaskIdentity. This vulnerability can perform UI
spoofing or spy on user’s activities.

This CL limit the usage which only allow system and same app to apply
relinquishTaskIdentity.

Bug: 185810717
Test: atest IntentTests
      atest testActivityWithRelinquishTaskIdentity
      atest com.android.server.wm.TaskTests
Change-Id: I55fe8938cd9a0dd7c0268e1cfec89d4e95eee049
parent 97ada9bc
Loading
Loading
Loading
Loading
+33 −15
Original line number Diff line number Diff line
@@ -319,6 +319,11 @@ class Task extends TaskFragment {
     */
    boolean mInResumeTopActivity = false;

    /**
     * Used to identify if the activity that is installed from device's system image.
     */
    boolean mIsEffectivelySystemApp;

    int mCurrentUser;

    String affinity;        // The affinity name for this task, or null; may change identity.
@@ -554,13 +559,24 @@ class Task extends TaskFragment {

            if (r.finishing) return false;

            if (mRoot == null || mRoot.finishing) {
                // Set this as the candidate root since it isn't finishing.
                mRoot = r;
            }

            final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid;
            if (mIgnoreRelinquishIdentity
                    || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0
                    || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID
                    && !mRoot.info.applicationInfo.isSystemApp()
                    && mRoot.info.applicationInfo.uid != uid)) {
                // No need to relinquish identity, end search.
                return true;
            }

            // Only end search if we are ignore relinquishing identity or we are not relinquishing.
            return mIgnoreRelinquishIdentity
                    || mNeverRelinquishIdentity
                    || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
            // Relinquish to next activity
            mRoot = r;
            return false;
        }
    }

@@ -985,7 +1001,15 @@ class Task extends TaskFragment {
     * @param info The activity info which could be different from {@code r.info} if set.
     */
    void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) {
        if (this.intent == null || !mNeverRelinquishIdentity) {
        boolean updateIdentity = false;
        if (this.intent == null) {
            updateIdentity = true;
        } else if (!mNeverRelinquishIdentity) {
            final ActivityInfo activityInfo = info != null ? info : r.info;
            updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp
                    || effectiveUid == activityInfo.applicationInfo.uid);
        }
        if (updateIdentity) {
            mCallingUid = r.launchedFromUid;
            mCallingPackage = r.launchedFromPackage;
            mCallingFeatureId = r.launchedFromFeatureId;
@@ -998,14 +1022,7 @@ class Task extends TaskFragment {
    private void setIntent(Intent _intent, ActivityInfo info) {
        if (!isLeafTask()) return;

        if (info.applicationInfo.uid == Process.SYSTEM_UID
                || info.applicationInfo.isSystemApp()) {
            // Only allow the apps that pre-installed on the system image to apply
            // relinquishTaskIdentity
        mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
        } else {
            mNeverRelinquishIdentity = true;
        }
        affinity = info.taskAffinity;
        if (intent == null) {
            // If this task already has an intent associated with it, don't set the root
@@ -1014,6 +1031,7 @@ class Task extends TaskFragment {
            rootAffinity = affinity;
        }
        effectiveUid = info.applicationInfo.uid;
        mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
        stringName = null;

        if (info.targetActivity == null) {
+56 −17
Original line number Diff line number Diff line
@@ -899,22 +899,21 @@ public class TaskTests extends WindowTestsBase {

    /**
     * Test that root activity index is reported correctly when looking for the 'effective root' in
     * case when bottom activity is finishing. Ignore the relinquishing task identity if it's not a
     * system activity even with the FLAG_RELINQUISH_TASK_IDENTITY.
     * case when bottom activities are relinquishing task identity or finishing.
     */
    @Test
    public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
        final Task task = getTestTask();
        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final Task task = activity0.getTask();
        // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
        // one above as finishing.
        final ActivityRecord activity0 = task.getBottomMostActivity();
        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
        activity1.finishing = true;
        new ActivityBuilder(mAtm).setTask(task).build();

        assertEquals("The first non-finishing activity and non-relinquishing task identity "
                + "must be reported.", task.getChildAt(0), task.getRootActivity(
                + "must be reported.", task.getChildAt(2), task.getRootActivity(
                false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
    }

@@ -934,21 +933,21 @@ public class TaskTests extends WindowTestsBase {
    }

    /**
     * Test that the root activity index is reported correctly when looking for the
     * 'effective root' for the case when all non-system activities have relinquishTaskIdentity set.
     * Test that the topmost activity index is reported correctly when looking for the
     * 'effective root' for the case when all activities have relinquishTaskIdentity set.
     */
    @Test
    public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
        final Task task = getTestTask();
        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final Task task = activity0.getTask();
        // Set relinquishTaskIdentity for all activities in the task
        final ActivityRecord activity0 = task.getBottomMostActivity();
        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;

        assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0),
                task.getRootActivity(false /*ignoreRelinquishIdentity*/,
                        true /*setToBottomIfNone*/));
        assertEquals("The topmost activity in the task must be reported.",
                task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
    }

    /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -1086,14 +1085,14 @@ public class TaskTests extends WindowTestsBase {
    }

    /**
     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with non-system
     * activity that relinquishes task identity.
     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
     * relinquishes task identity.
     */
    @Test
    public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
        final Task task = getTestTask();
        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final Task task = activity0.getTask();
        // Make the current root activity relinquish task identity
        final ActivityRecord activity0 = task.getBottomMostActivity();
        activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
        // Add an extra activity on top - this will be the new root
        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1102,7 +1101,7 @@ public class TaskTests extends WindowTestsBase {

        assertEquals(task.mTaskId,
                ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */));
        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
        assertEquals(task.mTaskId,
                ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */));
        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
                ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */));
@@ -1189,6 +1188,46 @@ public class TaskTests extends WindowTestsBase {
        verify(task).setIntent(eq(activity0));
    }

    /**
     * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but
     * another with different uid. This should make the task use the root activity when updating the
     * intent.
     */
    @Test
    public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() {
        final ActivityRecord activity0 = new ActivityBuilder(mAtm)
                .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
        final Task task = activity0.getTask();

        // Add an extra activity on top
        new ActivityBuilder(mAtm).setUid(11).setTask(task).build();

        spyOn(task);
        task.updateEffectiveIntent();
        verify(task).setIntent(eq(activity0));
    }

    /**
     * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity.
     * This should make the task use the topmost activity when updating the intent.
     */
    @Test
    public void testUpdateEffectiveIntent_relinquishingMultipleActivities() {
        final ActivityRecord activity0 = new ActivityBuilder(mAtm)
                .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
        final Task task = activity0.getTask();
        // Add an extra activity on top
        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;

        // Add an extra activity on top
        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();

        spyOn(task);
        task.updateEffectiveIntent();
        verify(task).setIntent(eq(activity2));
    }

    @Test
    public void testSaveLaunchingStateWhenConfigurationChanged() {
        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;