DO NOT MERGE SetMode: Don't call into PM with AppOps lock held
In the setmode paths do not call into package manager with the app-ops
lock held. Otherwise we might get dead-locks then someone calls into
app-ops manager with the package manager lock held.
Test: Booted and saw no errors from the changed code
Bug: 124731615
Bug: 146463528
Bug: 146590200
Change-Id: If074bed1bd246a81791a7d9fd656f42f1a755495
(cherry picked from commit ec142a52fe6da0a18a1274249ae452cc5a8669fa)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 314e04c..8261eb3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1110,8 +1110,8 @@
return Collections.emptyList();
}
synchronized (this) {
- Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
- false /* uidMismatchExpected */);
+ Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */,
+ false /* edit */);
if (pkgOps == null) {
return null;
}
@@ -1208,8 +1208,7 @@
private void pruneOp(Op op, int uid, String packageName) {
if (!op.hasAnyTime()) {
- Ops ops = getOpsRawLocked(uid, packageName, false /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
@@ -1409,11 +1408,6 @@
}
}
- @Override
- public void setMode(int code, int uid, String packageName, int mode) {
- setMode(code, uid, packageName, mode, true, false);
- }
-
/**
* Sets the mode for a certain op and uid.
*
@@ -1421,19 +1415,25 @@
* @param uid The UID for which to set
* @param packageName The package for which to set
* @param mode The new mode to set
- * @param verifyUid Iff {@code true}, check that the package name belongs to the uid
- * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid ==
- * false})
*/
- private void setMode(int code, int uid, @NonNull String packageName, int mode,
- boolean verifyUid, boolean isPrivileged) {
+ @Override
+ public void setMode(int code, int uid, @NonNull String packageName, int mode) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
+
+ boolean isPrivileged;
+ try {
+ isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot setMode", e);
+ return;
+ }
+
synchronized (this) {
UidState uidState = getUidStateLocked(uid, false);
- Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged);
+ Op op = getOpLocked(code, uid, packageName, isPrivileged, true);
if (op != null) {
if (op.mode != mode) {
op.mode = mode;
@@ -1822,11 +1822,19 @@
if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
return AppOpsManager.MODE_IGNORED;
}
- synchronized (this) {
+
+ boolean isPrivileged;
+ try {
+ isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+ } catch (Exception e) {
if (verify) {
- checkPackage(uid, packageName);
+ throw e;
}
- if (isOpRestrictedLocked(uid, code, packageName)) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+
+ synchronized (this) {
+ if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -1836,7 +1844,7 @@
final int rawMode = uidState.opModes.get(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
- Op op = getOpLocked(code, uid, packageName, false, verify, false);
+ Op op = getOpLocked(code, uid, packageName, false, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -1941,14 +1949,12 @@
@Override
public int checkPackage(int uid, String packageName) {
Preconditions.checkNotNull(packageName);
- synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- true /* uidMismatchExpected */);
- if (ops != null) {
- return AppOpsManager.MODE_ALLOWED;
- } else {
- return AppOpsManager.MODE_ERRORED;
- }
+ try {
+ verifyAndGetIsPrivileged(uid, packageName);
+
+ return AppOpsManager.MODE_ALLOWED;
+ } catch (SecurityException ignored) {
+ return AppOpsManager.MODE_ERRORED;
}
}
@@ -2011,9 +2017,16 @@
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName, @OpFlags int flags) {
+ boolean isPrivileged;
+ try {
+ isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot startOperation", e);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
- final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- false /* uidMismatchExpected */);
+ final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */);
if (ops == null) {
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED);
@@ -2022,7 +2035,7 @@
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, true);
- if (isOpRestrictedLocked(uid, code, packageName)) {
+ if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
@@ -2181,16 +2194,25 @@
return AppOpsManager.MODE_IGNORED;
}
ClientState client = (ClientState)token;
+
+ boolean isPrivileged;
+ try {
+ isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot startOperation", e);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
synchronized (this) {
- final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
- false /* uidMismatchExpected */);
+ final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged,
+ true /* edit */);
if (ops == null) {
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + resolvedPackageName);
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, true);
- if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+ if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -2262,8 +2284,17 @@
return;
}
ClientState client = (ClientState) token;
+
+ boolean isPrivileged;
+ try {
+ isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot finishOperation", e);
+ return;
+ }
+
synchronized (this) {
- Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false);
+ Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true);
if (op == null) {
return;
}
@@ -2513,8 +2544,76 @@
uidState.pendingStateCommitTime = 0;
}
- private Ops getOpsRawLocked(int uid, String packageName, boolean edit,
- boolean uidMismatchExpected) {
+ /**
+ * Verify that package belongs to uid and return whether the package is privileged.
+ *
+ * @param uid The uid the package belongs to
+ * @param packageName The package the might belong to the uid
+ *
+ * @return {@code true} iff the package is privileged
+ */
+ private boolean verifyAndGetIsPrivileged(int uid, String packageName) {
+ if (uid == Process.ROOT_UID) {
+ // For backwards compatibility, don't check package name for root UID.
+ return false;
+ }
+
+ // Do not check if uid/packageName is already known
+ synchronized (this) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState != null && uidState.pkgOps != null) {
+ Ops ops = uidState.pkgOps.get(packageName);
+
+ if (ops != null) {
+ return ops.isPrivileged;
+ }
+ }
+ }
+
+ boolean isPrivileged = false;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ int pkgUid;
+
+ ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class)
+ .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_INSTANT,
+ Process.SYSTEM_UID, UserHandle.getUserId(uid));
+ if (appInfo != null) {
+ pkgUid = appInfo.uid;
+ isPrivileged = (appInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ } else {
+ pkgUid = resolveUid(packageName);
+ if (pkgUid >= 0) {
+ isPrivileged = false;
+ }
+ }
+ if (pkgUid != uid) {
+ throw new SecurityException("Specified package " + packageName + " under uid " + uid
+ + " but it is really " + pkgUid);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return isPrivileged;
+ }
+
+ /**
+ * Get (and potentially create) ops.
+ *
+ * @param uid The uid the package belongs to
+ * @param packageName The name of the package
+ * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false)
+ * @param edit If an ops does not exist, create the ops?
+
+ * @return
+ */
+ private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
return null;
@@ -2532,47 +2631,6 @@
if (!edit) {
return null;
}
- boolean isPrivileged = false;
- // This is the first time we have seen this package name under this uid,
- // so let's make sure it is valid.
- if (uid != 0) {
- final long ident = Binder.clearCallingIdentity();
- try {
- int pkgUid = -1;
- try {
- ApplicationInfo appInfo = ActivityThread.getPackageManager()
- .getApplicationInfo(packageName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(uid));
- if (appInfo != null) {
- pkgUid = appInfo.uid;
- isPrivileged = (appInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- } else {
- pkgUid = resolveUid(packageName);
- if (pkgUid >= 0) {
- isPrivileged = false;
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Could not contact PackageManager", e);
- }
- if (pkgUid != uid) {
- // Oops! The package name is not valid for the uid they are calling
- // under. Abort.
- if (!uidMismatchExpected) {
- RuntimeException ex = new RuntimeException("here");
- ex.fillInStackTrace();
- Slog.w(TAG, "Bad call: specified package " + packageName
- + " under uid " + uid + " but it is really " + pkgUid, ex);
- }
- return null;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
ops = new Ops(packageName, uidState, isPrivileged);
uidState.pkgOps.put(packageName, ops);
}
@@ -2580,7 +2638,7 @@
}
/**
- * Get the state of all ops for a package, <b>don't verify that package belongs to uid</b>.
+ * Get the state of all ops for a package.
*
* <p>Usually callers should use {@link #getOpLocked} and not call this directly.
*
@@ -2638,23 +2696,15 @@
* @param code The code of the op
* @param uid The uid the of the package
* @param packageName The package name for which to get the state for
+ * @param isPrivileged Whether the package is privileged or not (only used if {@code edit
+ * == true})
* @param edit Iff {@code true} create the {@link Op} object if not yet created
- * @param verifyUid Iff {@code true} check that the package belongs to the uid
- * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid
- * == false})
*
* @return The {@link Op state} of the op
*/
- private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit,
- boolean verifyUid, boolean isPrivileged) {
- Ops ops;
-
- if (verifyUid) {
- ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */);
- } else {
- ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
- }
-
+ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
+ boolean isPrivileged, boolean edit) {
+ Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
if (ops == null) {
return null;
}
@@ -2684,7 +2734,8 @@
return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
}
- private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
+ private boolean isOpRestrictedLocked(int uid, int code, String packageName,
+ boolean isPrivileged) {
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
@@ -2696,8 +2747,8 @@
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
// If we are the system, bypass user restrictions for certain codes
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
- false /* uidMismatchExpected */);
+ Ops ops = getOpsRawLocked(uid, packageName, isPrivileged,
+ true /* edit */);
if ((ops != null) && ops.isPrivileged) {
return false;
}
@@ -3068,7 +3119,7 @@
out.attribute(null, "n", Integer.toString(pkg.getUid()));
synchronized (this) {
Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(),
- false /* edit */, false /* uidMismatchExpected */);
+ false /* isPrivileged */, false /* edit */);
// Should always be present as the list of PackageOps is generated
// from Ops.
if (ops != null) {