Skip to content

Commit 5320eb8

Browse files
author
Dianne Hackborn
committed
Fix activity resolver, issues #6519130 and #6507239
6519130: Starting ResolverActivity with no arguments crashes system_server 6507239: ResolverActivity may bypass signature permissions Change-Id: I64534f781bc6b7eb45e85dbe3a55d351ee28e85c
1 parent 787c9ec commit 5320eb8

File tree

6 files changed

+135
-36
lines changed

6 files changed

+135
-36
lines changed

core/java/android/app/Activity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4999,7 +4999,8 @@ final void attach(Context context, ActivityThread aThread,
49994999
mCurrentConfig = config;
50005000
}
50015001

5002-
final IBinder getActivityToken() {
5002+
/** @hide */
5003+
public final IBinder getActivityToken() {
50035004
return mParent != null ? mParent.getActivityToken() : mToken;
50045005
}
50055006

core/java/android/app/ActivityManager.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import android.content.pm.ApplicationInfo;
2727
import android.content.pm.ConfigurationInfo;
2828
import android.content.pm.IPackageDataObserver;
29-
import android.content.res.Configuration;
29+
import android.content.pm.PackageManager;
3030
import android.content.res.Resources;
3131
import android.graphics.Bitmap;
3232
import android.graphics.Point;
@@ -36,16 +36,17 @@
3636
import android.os.Handler;
3737
import android.os.Parcel;
3838
import android.os.Parcelable;
39+
import android.os.Process;
3940
import android.os.RemoteException;
4041
import android.os.ServiceManager;
4142
import android.os.SystemProperties;
43+
import android.os.UserId;
4244
import android.text.TextUtils;
4345
import android.util.DisplayMetrics;
4446
import android.util.Log;
4547
import android.util.Slog;
4648
import android.view.Display;
4749

48-
import java.util.ArrayList;
4950
import java.util.HashMap;
5051
import java.util.List;
5152
import java.util.Map;
@@ -1798,6 +1799,40 @@ public Map<String, Integer> getAllPackageLaunchCounts() {
17981799
}
17991800
}
18001801

1802+
/** @hide */
1803+
public static int checkComponentPermission(String permission, int uid,
1804+
int owningUid, boolean exported) {
1805+
// Root, system server get to do everything.
1806+
if (uid == 0 || uid == Process.SYSTEM_UID) {
1807+
return PackageManager.PERMISSION_GRANTED;
1808+
}
1809+
// Isolated processes don't get any permissions.
1810+
if (UserId.isIsolated(uid)) {
1811+
return PackageManager.PERMISSION_DENIED;
1812+
}
1813+
// If there is a uid that owns whatever is being accessed, it has
1814+
// blanket access to it regardless of the permissions it requires.
1815+
if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
1816+
return PackageManager.PERMISSION_GRANTED;
1817+
}
1818+
// If the target is not exported, then nobody else can get to it.
1819+
if (!exported) {
1820+
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
1821+
return PackageManager.PERMISSION_DENIED;
1822+
}
1823+
if (permission == null) {
1824+
return PackageManager.PERMISSION_GRANTED;
1825+
}
1826+
try {
1827+
return AppGlobals.getPackageManager()
1828+
.checkUidPermission(permission, uid);
1829+
} catch (RemoteException e) {
1830+
// Should never happen, but if it does... deny!
1831+
Slog.e(TAG, "PackageManager is dead?!?", e);
1832+
}
1833+
return PackageManager.PERMISSION_DENIED;
1834+
}
1835+
18011836
/**
18021837
* Returns the usage statistics of each installed package.
18031838
*

core/java/android/app/ActivityManagerNative.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,15 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
16561656
return true;
16571657
}
16581658

1659+
case GET_LAUNCHED_FROM_UID_TRANSACTION: {
1660+
data.enforceInterface(IActivityManager.descriptor);
1661+
IBinder token = data.readStrongBinder();
1662+
int res = getLaunchedFromUid(token);
1663+
reply.writeNoException();
1664+
reply.writeInt(res);
1665+
return true;
1666+
}
1667+
16591668
}
16601669

16611670
return super.onTransact(code, data, reply, flags);
@@ -3785,5 +3794,18 @@ public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent
37853794
return result;
37863795
}
37873796

3797+
public int getLaunchedFromUid(IBinder activityToken) throws RemoteException {
3798+
Parcel data = Parcel.obtain();
3799+
Parcel reply = Parcel.obtain();
3800+
data.writeInterfaceToken(IActivityManager.descriptor);
3801+
data.writeStrongBinder(activityToken);
3802+
mRemote.transact(GET_LAUNCHED_FROM_UID_TRANSACTION, data, reply, 0);
3803+
reply.readException();
3804+
int result = reply.readInt();
3805+
data.recycle();
3806+
reply.recycle();
3807+
return result;
3808+
}
3809+
37883810
private IBinder mRemote;
37893811
}

core/java/android/app/IActivityManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ public boolean targetTaskAffinityMatchesActivity(IBinder token, String destAffin
350350
public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent resultData)
351351
throws RemoteException;
352352

353+
// This is not public because you need to be very careful in how you
354+
// manage your activity to make sure it is always the uid you expect.
355+
public int getLaunchedFromUid(IBinder activityToken) throws RemoteException;
356+
353357
/*
354358
* Private non-Binder interfaces
355359
*/
@@ -592,4 +596,5 @@ private WaitResult(Parcel source) {
592596
int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146;
593597
int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
594598
int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
599+
int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
595600
}

core/java/com/android/internal/app/ResolverActivity.java

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.android.internal.content.PackageMonitor;
2121

2222
import android.app.ActivityManager;
23+
import android.app.ActivityManagerNative;
2324
import android.content.ComponentName;
2425
import android.content.Context;
2526
import android.content.Intent;
@@ -34,6 +35,9 @@
3435
import android.net.Uri;
3536
import android.os.Bundle;
3637
import android.os.PatternMatcher;
38+
import android.os.Process;
39+
import android.os.RemoteException;
40+
import android.os.UserId;
3741
import android.util.Log;
3842
import android.view.LayoutInflater;
3943
import android.view.View;
@@ -61,6 +65,7 @@
6165
public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {
6266
private static final String TAG = "ResolverActivity";
6367

68+
private int mLaunchedFromUid;
6469
private ResolveListAdapter mAdapter;
6570
private PackageManager mPm;
6671
private boolean mAlwaysUseOption;
@@ -102,6 +107,12 @@ protected void onCreate(Bundle savedInstanceState, Intent intent,
102107
boolean alwaysUseOption) {
103108
setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
104109
super.onCreate(savedInstanceState);
110+
try {
111+
mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
112+
getActivityToken());
113+
} catch (RemoteException e) {
114+
mLaunchedFromUid = -1;
115+
}
105116
mPm = getPackageManager();
106117
mAlwaysUseOption = alwaysUseOption;
107118
mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
@@ -118,9 +129,14 @@ protected void onCreate(Bundle savedInstanceState, Intent intent,
118129
mIconDpi = am.getLauncherLargeIconDensity();
119130
mIconSize = am.getLauncherLargeIconSize();
120131

121-
mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList);
132+
mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
133+
mLaunchedFromUid);
122134
int count = mAdapter.getCount();
123-
if (count > 1) {
135+
if (mLaunchedFromUid < 0 || UserId.isIsolated(mLaunchedFromUid)) {
136+
// Gulp!
137+
finish();
138+
return;
139+
} else if (count > 1) {
124140
ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
125141
mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
126142
mGrid.setAdapter(mAdapter);
@@ -146,9 +162,13 @@ protected void onCreate(Bundle savedInstanceState, Intent intent,
146162

147163
if (alwaysUseOption) {
148164
final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
149-
buttonLayout.setVisibility(View.VISIBLE);
150-
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
151-
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
165+
if (buttonLayout != null) {
166+
buttonLayout.setVisibility(View.VISIBLE);
167+
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
168+
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
169+
} else {
170+
mAlwaysUseOption = false;
171+
}
152172
}
153173
}
154174

@@ -207,6 +227,18 @@ protected void onStop() {
207227
mPackageMonitor.unregister();
208228
mRegistered = false;
209229
}
230+
if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
231+
// This resolver is in the unusual situation where it has been
232+
// launched at the top of a new task. We don't let it be added
233+
// to the recent tasks shown to the user, and we need to make sure
234+
// that each time we are launched we get the correct launching
235+
// uid (not re-using the same resolver from an old launching uid),
236+
// so we will now finish ourself since being no longer visible,
237+
// the user probably can't get back to us.
238+
if (!isChangingConfigurations()) {
239+
finish();
240+
}
241+
}
210242
}
211243

212244
@Override
@@ -363,17 +395,19 @@ private final class ResolveListAdapter extends BaseAdapter {
363395
private final Intent[] mInitialIntents;
364396
private final List<ResolveInfo> mBaseResolveList;
365397
private final Intent mIntent;
398+
private final int mLaunchedFromUid;
366399
private final LayoutInflater mInflater;
367400

368401
private List<ResolveInfo> mCurrentResolveList;
369402
private List<DisplayResolveInfo> mList;
370403

371404
public ResolveListAdapter(Context context, Intent intent,
372-
Intent[] initialIntents, List<ResolveInfo> rList) {
405+
Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
373406
mIntent = new Intent(intent);
374407
mIntent.setComponent(null);
375408
mInitialIntents = initialIntents;
376409
mBaseResolveList = rList;
410+
mLaunchedFromUid = launchedFromUid;
377411
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
378412
rebuildList();
379413
}
@@ -400,6 +434,23 @@ private void rebuildList() {
400434
mCurrentResolveList = mPm.queryIntentActivities(
401435
mIntent, PackageManager.MATCH_DEFAULT_ONLY
402436
| (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
437+
// Filter out any activities that the launched uid does not
438+
// have permission for. We don't do this when we have an explicit
439+
// list of resolved activities, because that only happens when
440+
// we are being subclassed, so we can safely launch whatever
441+
// they gave us.
442+
if (mCurrentResolveList != null) {
443+
for (int i=mCurrentResolveList.size()-1; i >= 0; i--) {
444+
ActivityInfo ai = mCurrentResolveList.get(i).activityInfo;
445+
int granted = ActivityManager.checkComponentPermission(
446+
ai.permission, mLaunchedFromUid,
447+
ai.applicationInfo.uid, ai.exported);
448+
if (granted != PackageManager.PERMISSION_GRANTED) {
449+
// Access not allowed!
450+
mCurrentResolveList.remove(i);
451+
}
452+
}
453+
}
403454
}
404455
int N;
405456
if ((mCurrentResolveList != null) && ((N = mCurrentResolveList.size()) > 0)) {

services/java/com/android/server/am/ActivityManagerService.java

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,35 +4639,12 @@ int checkComponentPermission(String permission, int pid, int uid,
46394639
pid = tlsIdentity.pid;
46404640
}
46414641

4642-
// Root, system server and our own process get to do everything.
4643-
if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
4644-
return PackageManager.PERMISSION_GRANTED;
4645-
}
4646-
// Isolated processes don't get any permissions.
4647-
if (UserId.isIsolated(uid)) {
4648-
return PackageManager.PERMISSION_DENIED;
4649-
}
4650-
// If there is a uid that owns whatever is being accessed, it has
4651-
// blanket access to it regardless of the permissions it requires.
4652-
if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
4653-
return PackageManager.PERMISSION_GRANTED;
4654-
}
4655-
// If the target is not exported, then nobody else can get to it.
4656-
if (!exported) {
4657-
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
4658-
return PackageManager.PERMISSION_DENIED;
4659-
}
4660-
if (permission == null) {
4642+
if (pid == MY_PID) {
46614643
return PackageManager.PERMISSION_GRANTED;
46624644
}
4663-
try {
4664-
return AppGlobals.getPackageManager()
4665-
.checkUidPermission(permission, uid);
4666-
} catch (RemoteException e) {
4667-
// Should never happen, but if it does... deny!
4668-
Slog.e(TAG, "PackageManager is dead?!?", e);
4669-
}
4670-
return PackageManager.PERMISSION_DENIED;
4645+
4646+
return ActivityManager.checkComponentPermission(permission, uid,
4647+
owningUid, exported);
46714648
}
46724649

46734650
/**
@@ -13544,6 +13521,14 @@ public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
1354413521
}
1354513522
}
1354613523

13524+
public int getLaunchedFromUid(IBinder activityToken) {
13525+
ActivityRecord srec = ActivityRecord.forToken(activityToken);
13526+
if (srec == null) {
13527+
return -1;
13528+
}
13529+
return srec.launchedFromUid;
13530+
}
13531+
1354713532
// =========================================================
1354813533
// LIFETIME MANAGEMENT
1354913534
// =========================================================

0 commit comments

Comments
 (0)