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

Commit 0bba8d5f authored by Philip P. Moltmann's avatar Philip P. Moltmann Committed by Narayan Kamath
Browse files

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
Bug: 147649036
Change-Id: If074bed1bd246a81791a7d9fd656f42f1a755495
(cherry picked from commit ec142a52)
parent b43b52a2
Loading
Loading
Loading
Loading
+146 −95
Original line number Original line Diff line number Diff line
@@ -1105,8 +1105,8 @@ public class AppOpsService extends IAppOpsService.Stub {
            return Collections.emptyList();
            return Collections.emptyList();
        }
        }
        synchronized (this) {
        synchronized (this) {
            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */,
                    false /* uidMismatchExpected */);
                    false /* edit */);
            if (pkgOps == null) {
            if (pkgOps == null) {
                return null;
                return null;
            }
            }
@@ -1203,8 +1203,7 @@ public class AppOpsService extends IAppOpsService.Stub {


    private void pruneOp(Op op, int uid, String packageName) {
    private void pruneOp(Op op, int uid, String packageName) {
        if (!op.hasAnyTime()) {
        if (!op.hasAnyTime()) {
            Ops ops = getOpsRawLocked(uid, packageName, false /* edit */,
            Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */);
                    false /* uidMismatchExpected */);
            if (ops != null) {
            if (ops != null) {
                ops.remove(op.op);
                ops.remove(op.op);
                if (ops.size() <= 0) {
                if (ops.size() <= 0) {
@@ -1404,11 +1403,6 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
        }
    }
    }


    @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.
     * Sets the mode for a certain op and uid.
     *
     *
@@ -1416,19 +1410,25 @@ public class AppOpsService extends IAppOpsService.Stub {
     * @param uid The UID for which to set
     * @param uid The UID for which to set
     * @param packageName The package for which to set
     * @param packageName The package for which to set
     * @param mode The new mode 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,
    @Override
            boolean verifyUid, boolean isPrivileged) {
    public void setMode(int code, int uid, @NonNull String packageName, int mode) {
        enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
        enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
        verifyIncomingOp(code);
        verifyIncomingOp(code);
        ArraySet<ModeCallback> repCbs = null;
        ArraySet<ModeCallback> repCbs = null;
        code = AppOpsManager.opToSwitch(code);
        code = AppOpsManager.opToSwitch(code);

        boolean isPrivileged;
        try {
            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
        } catch (SecurityException e) {
            Slog.e(TAG, "Cannot setMode", e);
            return;
        }

        synchronized (this) {
        synchronized (this) {
            UidState uidState = getUidStateLocked(uid, false);
            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 != null) {
                if (op.mode != mode) {
                if (op.mode != mode) {
                    op.mode = mode;
                    op.mode = mode;
@@ -1817,11 +1817,19 @@ public class AppOpsService extends IAppOpsService.Stub {
        if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
        if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
            return AppOpsManager.MODE_IGNORED;
            return AppOpsManager.MODE_IGNORED;
        }
        }
        synchronized (this) {

        boolean isPrivileged;
        try {
            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
        } catch (Exception e) {
            if (verify) {
            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;
                return AppOpsManager.MODE_IGNORED;
            }
            }
            code = AppOpsManager.opToSwitch(code);
            code = AppOpsManager.opToSwitch(code);
@@ -1831,7 +1839,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                final int rawMode = uidState.opModes.get(code);
                final int rawMode = uidState.opModes.get(code);
                return raw ? rawMode : uidState.evalMode(code, rawMode);
                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) {
            if (op == null) {
                return AppOpsManager.opToDefaultMode(code);
                return AppOpsManager.opToDefaultMode(code);
            }
            }
@@ -1936,16 +1944,14 @@ public class AppOpsService extends IAppOpsService.Stub {
    @Override
    @Override
    public int checkPackage(int uid, String packageName) {
    public int checkPackage(int uid, String packageName) {
        Preconditions.checkNotNull(packageName);
        Preconditions.checkNotNull(packageName);
        synchronized (this) {
        try {
            Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
            verifyAndGetIsPrivileged(uid, packageName);
                    true /* uidMismatchExpected */);

            if (ops != null) {
            return AppOpsManager.MODE_ALLOWED;
            return AppOpsManager.MODE_ALLOWED;
            } else {
        } catch (SecurityException ignored) {
            return AppOpsManager.MODE_ERRORED;
            return AppOpsManager.MODE_ERRORED;
        }
        }
    }
    }
    }


    @Override
    @Override
    public int noteProxyOperation(int code, int proxyUid,
    public int noteProxyOperation(int code, int proxyUid,
@@ -2006,9 +2012,16 @@ public class AppOpsService extends IAppOpsService.Stub {


    private int noteOperationUnchecked(int code, int uid, String packageName,
    private int noteOperationUnchecked(int code, int uid, String packageName,
            int proxyUid, String proxyPackageName, @OpFlags int flags) {
            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) {
        synchronized (this) {
            final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
            final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */);
                    false /* uidMismatchExpected */);
            if (ops == null) {
            if (ops == null) {
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                        AppOpsManager.MODE_IGNORED);
                        AppOpsManager.MODE_IGNORED);
@@ -2017,7 +2030,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                return AppOpsManager.MODE_ERRORED;
                return AppOpsManager.MODE_ERRORED;
            }
            }
            final Op op = getOpLocked(ops, code, true);
            final Op op = getOpLocked(ops, code, true);
            if (isOpRestrictedLocked(uid, code, packageName)) {
            if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                        AppOpsManager.MODE_IGNORED);
                        AppOpsManager.MODE_IGNORED);
                return AppOpsManager.MODE_IGNORED;
                return AppOpsManager.MODE_IGNORED;
@@ -2176,16 +2189,25 @@ public class AppOpsService extends IAppOpsService.Stub {
            return  AppOpsManager.MODE_IGNORED;
            return  AppOpsManager.MODE_IGNORED;
        }
        }
        ClientState client = (ClientState)token;
        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) {
        synchronized (this) {
            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged,
                    false /* uidMismatchExpected */);
                    true /* edit */);
            if (ops == null) {
            if (ops == null) {
                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                        + " package " + resolvedPackageName);
                        + " package " + resolvedPackageName);
                return AppOpsManager.MODE_ERRORED;
                return AppOpsManager.MODE_ERRORED;
            }
            }
            final Op op = getOpLocked(ops, code, true);
            final Op op = getOpLocked(ops, code, true);
            if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
            if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) {
                return AppOpsManager.MODE_IGNORED;
                return AppOpsManager.MODE_IGNORED;
            }
            }
            final int switchCode = AppOpsManager.opToSwitch(code);
            final int switchCode = AppOpsManager.opToSwitch(code);
@@ -2257,8 +2279,17 @@ public class AppOpsService extends IAppOpsService.Stub {
            return;
            return;
        }
        }
        ClientState client = (ClientState) token;
        ClientState client = (ClientState) token;

        boolean isPrivileged;
        try {
            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
        } catch (SecurityException e) {
            Slog.e(TAG, "Cannot finishOperation", e);
            return;
        }

        synchronized (this) {
        synchronized (this) {
            Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false);
            Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true);
            if (op == null) {
            if (op == null) {
                return;
                return;
            }
            }
@@ -2508,38 +2539,44 @@ public class AppOpsService extends IAppOpsService.Stub {
        uidState.pendingStateCommitTime = 0;
        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.
        UidState uidState = getUidStateLocked(uid, edit);
     *
        if (uidState == null) {
     * @param uid The uid the package belongs to
            return null;
     * @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;
        }
        }


        if (uidState.pkgOps == null) {
        // Do not check if uid/packageName is already known
            if (!edit) {
        synchronized (this) {
                return null;
            UidState uidState = mUidStates.get(uid);
            if (uidState != null && uidState.pkgOps != null) {
                Ops ops = uidState.pkgOps.get(packageName);

                if (ops != null) {
                    return ops.isPrivileged;
                }
                }
            uidState.pkgOps = new ArrayMap<>();
            }
            }

        Ops ops = uidState.pkgOps.get(packageName);
        if (ops == null) {
            if (!edit) {
                return null;
        }
        }

        boolean isPrivileged = false;
        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();
        final long ident = Binder.clearCallingIdentity();
        try {
        try {
                    int pkgUid = -1;
            int pkgUid;
                    try {

                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
            ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class)
                                .getApplicationInfo(packageName,
                    .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE
                                        PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
                                        UserHandle.getUserId(uid));
                                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
                                    | PackageManager.MATCH_INSTANT,
                            Process.SYSTEM_UID, UserHandle.getUserId(uid));
            if (appInfo != null) {
            if (appInfo != null) {
                pkgUid = appInfo.uid;
                pkgUid = appInfo.uid;
                isPrivileged = (appInfo.privateFlags
                isPrivileged = (appInfo.privateFlags
@@ -2550,23 +2587,44 @@ public class AppOpsService extends IAppOpsService.Stub {
                    isPrivileged = false;
                    isPrivileged = false;
                }
                }
            }
            }
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Could not contact PackageManager", e);
                    }
            if (pkgUid != uid) {
            if (pkgUid != uid) {
                        // Oops!  The package name is not valid for the uid they are calling
                throw new SecurityException("Specified package " + packageName + " under uid " + uid
                        // under.  Abort.
                        + " but it is really " + pkgUid);
                        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 {
        } finally {
            Binder.restoreCallingIdentity(ident);
            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;
        }

        if (uidState.pkgOps == null) {
            if (!edit) {
                return null;
            }
            uidState.pkgOps = new ArrayMap<>();
        }

        Ops ops = uidState.pkgOps.get(packageName);
        if (ops == null) {
            if (!edit) {
                return null;
            }
            }
            ops = new Ops(packageName, uidState, isPrivileged);
            ops = new Ops(packageName, uidState, isPrivileged);
            uidState.pkgOps.put(packageName, ops);
            uidState.pkgOps.put(packageName, ops);
@@ -2575,7 +2633,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    }
    }


    /**
    /**
     * 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.
     * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
     *
     *
@@ -2633,23 +2691,15 @@ public class AppOpsService extends IAppOpsService.Stub {
     * @param code The code of the op
     * @param code The code of the op
     * @param uid The uid the of the package
     * @param uid The uid the of the package
     * @param packageName The package name for which to get the state for
     * @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 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
     * @return The {@link Op state} of the op
     */
     */
    private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit,
    private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
            boolean verifyUid, boolean isPrivileged) {
            boolean isPrivileged, boolean edit) {
        Ops ops;
        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);

        if (verifyUid) {
            ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */);
        }  else {
            ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
        }

        if (ops == null) {
        if (ops == null) {
            return null;
            return null;
        }
        }
@@ -2679,7 +2729,8 @@ public class AppOpsService extends IAppOpsService.Stub {
        return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
        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);
        int userHandle = UserHandle.getUserId(uid);
        final int restrictionSetCount = mOpUserRestrictions.size();
        final int restrictionSetCount = mOpUserRestrictions.size();


@@ -2691,8 +2742,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                    // If we are the system, bypass user restrictions for certain codes
                    // If we are the system, bypass user restrictions for certain codes
                    synchronized (this) {
                    synchronized (this) {
                        Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                        Ops ops = getOpsRawLocked(uid, packageName, isPrivileged,
                                false /* uidMismatchExpected */);
                                true /* edit */);
                        if ((ops != null) && ops.isPrivileged) {
                        if ((ops != null) && ops.isPrivileged) {
                            return false;
                            return false;
                        }
                        }
@@ -3063,7 +3114,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
                        synchronized (this) {
                        synchronized (this) {
                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(),
                            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
                            // Should always be present as the list of PackageOps is generated
                            // from Ops.
                            // from Ops.
                            if (ops != null) {
                            if (ops != null) {