Android Security Bulletin Index (July 2023)

2023-07-01 security patch level vulnerability details

Framework

CVE-2023-21254
  • getUidProcessState这个接口的AMS对外服务版本和AMInternal进程内版本的实现不一致,对外版本中如果满足mPendingStartActivityUids.isPendingTopUid(uid)的条件,就直接返回PROCESS_STATE_TOP,在这种场景下会导致一次性权限无法被正常清除。
  • ActivityManagerService#getUidProcessState的实现:
    @Override
    public int getUidProcessState(int uid, String callingPackage) {
    if (!hasUsageStatsPermission(callingPackage)) {
        enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
                "getUidProcessState");
    }
    // In case the caller is requesting processState of an app in a different user,
    // then verify the caller has INTERACT_ACROSS_USERS_FULL permission
    mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
            UserHandle.getUserId(uid), false /* allowAll */, ALLOW_FULL_ONLY,
            "getUidProcessState", callingPackage); // Ignore return value
    synchronized (mProcLock) {
        if (mPendingStartActivityUids.isPendingTopUid(uid)) {
            return PROCESS_STATE_TOP;
        }
        return mProcessList.getUidProcStateLOSP(uid);
    }
    }
  • ActivityManagerInternal#getUidProcessState的实现:
    // NOTE: this is an internal method used by the OnShellCommand implementation only and should
    // be guarded by permission checking.
    int getUidState(int uid) {
    synchronized (mProcLock) {
        return mProcessList.getUidProcStateLOSP(uid);
    }
    }
  • 很明显的除了权限检查之外,就差了一个mPendingStartActivityUids.isPendingTopUid(uid)的条件判断。想让判断成立需要走PendingStartActivityUids#add
    /** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */
    synchronized boolean add(int uid, int pid) {
    if (mPendingUids.get(uid) == null) {
        mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime()));
        return true;
    }
    return false;
    }
  • 只有一个地方可以实现,即ActivityManagerService#addPendingTopUid
    @Override
    public void addPendingTopUid(int uid, int pid, @Nullable IApplicationThread thread) {
    final boolean isNewPending = mPendingStartActivityUids.add(uid, pid);
    // If the next top activity is in cached and frozen mode, WM should raise its priority
    // to unfreeze it. This is done by calling AMS.updateOomAdj that will lower its oom adj.
    // However, WM cannot hold the AMS clock here so the updateOomAdj operation is performed
    // in a separate thread asynchronously. Therefore WM can't guarantee AMS will unfreeze
    // next top activity on time. This race will fail the following binder transactions WM
    // sends to the activity. After this race issue between WM/ATMS and AMS is solved, this
    // workaround can be removed. (b/213288355)
    if (isNewPending) {
        mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid,
                OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
    }
    // We need to update the network rules for the app coming to the top state so that
    // it can access network when the device or the app is in a restricted state
    // (e.g. battery/data saver) but since waiting for updateOomAdj to complete and then
    // informing NetworkPolicyManager might get delayed, informing the state change as soon
    // as we know app is going to come to the top state.
    if (isNewPending && mNetworkPolicyUidObserver != null) {
        try {
            final long procStateSeq = mProcessList.getNextProcStateSeq();
            mNetworkPolicyUidObserver.onUidStateChanged(uid, PROCESS_STATE_TOP,
                    procStateSeq, PROCESS_CAPABILITY_ALL);
            if (thread != null && shouldWaitForNetworkRulesUpdate(uid)) {
                thread.setNetworkBlockSeq(procStateSeq);
            }
        } catch (RemoteException e) {
            Slog.d(TAG, "Error calling setNetworkBlockSeq", e);
        }
    }
    }
  • 这个接口不是IPC接口,在system_server内部调用他的只有这里:
    void onStartActivity(int topProcessState, ActivityInfo info) {
    String packageName = null;
    if ((info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
            || !"android".equals(info.packageName)) {
        // Don't add this if it is a platform component that is marked to run in multiple
        // processes, because this is actually part of the framework so doesn't make sense
        // to track as a separate apk in the process.
        packageName = info.packageName;
    }
    // update ActivityManagerService.PendingStartActivityUids list.
    if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
        mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
    }
    prepareOomAdjustment();
    // Posting the message at the front of queue so WM lock isn't held when we call into AM,
    // and the process state of starting activity can be updated quicker which will give it a
    // higher scheduling group.
    final Message m = PooledLambda.obtainMessage(WindowProcessListener::onStartActivity,
            mListener, topProcessState, shouldSetProfileProc(), packageName,
            info.applicationInfo.longVersionCode);
    mAtm.mH.sendMessageAtFrontOfQueue(m);
    }
  • 所以就是刚启动Activity的时候就处于这个状态,想保持这个状态需要启动Activity之后马上退出,正如修复补丁中所说:

    Install test app that requests permission and will exit immediately on granting, observe permission is no longer indefinitely held.