[RESTRICT AUTOMERGE]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
This commit is contained in:
Jeff Chang
2021-10-27 17:59:52 +08:00
parent 20e71a637f
commit c668064c04
2 changed files with 89 additions and 32 deletions

View File

@@ -321,6 +321,11 @@ class Task extends TaskFragment {
*/ */
boolean mInResumeTopActivity = false; boolean mInResumeTopActivity = false;
/**
* Used to identify if the activity that is installed from device's system image.
*/
boolean mIsEffectivelySystemApp;
int mCurrentUser; int mCurrentUser;
String affinity; // The affinity name for this task, or null; may change identity. String affinity; // The affinity name for this task, or null; may change identity.
@@ -568,13 +573,24 @@ class Task extends TaskFragment {
if (r.finishing) return false; if (r.finishing) return false;
// Set this as the candidate root since it isn't finishing. if (mRoot == null || mRoot.finishing) {
mRoot = r; // Set this as the candidate root since it isn't finishing.
mRoot = r;
}
// Only end search if we are ignore relinquishing identity or we are not relinquishing. final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid;
return ignoreRelinquishIdentity if (ignoreRelinquishIdentity
|| mNeverRelinquishIdentity || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0
|| (r.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;
}
// Relinquish to next activity
mRoot = r;
return false;
} }
} }
@@ -999,7 +1015,15 @@ class Task extends TaskFragment {
* @param info The activity info which could be different from {@code r.info} if set. * @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) { 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; mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage; mCallingPackage = r.launchedFromPackage;
mCallingFeatureId = r.launchedFromFeatureId; mCallingFeatureId = r.launchedFromFeatureId;
@@ -1012,14 +1036,7 @@ class Task extends TaskFragment {
private void setIntent(Intent _intent, ActivityInfo info) { private void setIntent(Intent _intent, ActivityInfo info) {
if (!isLeafTask()) return; if (!isLeafTask()) return;
if (info.applicationInfo.uid == Process.SYSTEM_UID mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
|| 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; affinity = info.taskAffinity;
if (intent == null) { if (intent == null) {
// If this task already has an intent associated with it, don't set the root // If this task already has an intent associated with it, don't set the root
@@ -1028,6 +1045,7 @@ class Task extends TaskFragment {
rootAffinity = affinity; rootAffinity = affinity;
} }
effectiveUid = info.applicationInfo.uid; effectiveUid = info.applicationInfo.uid;
mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
stringName = null; stringName = null;
if (info.targetActivity == null) { if (info.targetActivity == null) {

View File

@@ -899,22 +899,21 @@ public class TaskTests extends WindowTestsBase {
/** /**
* Test that root activity index is reported correctly when looking for the 'effective root' in * 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 * case when bottom activities are relinquishing task identity or finishing.
* system activity even with the FLAG_RELINQUISH_TASK_IDENTITY.
*/ */
@Test @Test
public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { 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 // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
// one above as finishing. // one above as finishing.
final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.finishing = true; activity1.finishing = true;
new ActivityBuilder(mAtm).setTask(task).build(); new ActivityBuilder(mAtm).setTask(task).build();
assertEquals("The first non-finishing activity and non-relinquishing task identity " 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*/)); 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 * Test that the topmost activity index is reported correctly when looking for the
* 'effective root' for the case when all non-system activities have relinquishTaskIdentity set. * 'effective root' for the case when all activities have relinquishTaskIdentity set.
*/ */
@Test @Test
public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { 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 // Set relinquishTaskIdentity for all activities in the task
final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0), assertEquals("The topmost activity in the task must be reported.",
task.getRootActivity(false /*ignoreRelinquishIdentity*/, task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
true /*setToBottomIfNone*/)); false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
} }
/** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */ /** 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 * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
* activity that relinquishes task identity. * relinquishes task identity.
*/ */
@Test @Test
public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { 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 // Make the current root activity relinquish task identity
final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top - this will be the new root // Add an extra activity on top - this will be the new root
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1102,7 +1101,7 @@ public class TaskTests extends WindowTestsBase {
assertEquals(task.mTaskId, assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
@@ -1189,6 +1188,46 @@ public class TaskTests extends WindowTestsBase {
verify(task).setIntent(eq(activity0)); 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 @Test
public void testSaveLaunchingStateWhenConfigurationChanged() { public void testSaveLaunchingStateWhenConfigurationChanged() {
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;