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

Commit 9f6bb5b7 authored by Mathew Inwood's avatar Mathew Inwood
Browse files

Fail gracefully if we get a bad API whitelist.

If we send a bad API whitelist to the Zygote, it causes it to close the
socket. If we take no further action in AMS, it results in the same list
of exceptions being sent when we re-open the socket, resulting in it again
being closed. This results in no longer fork/start any new processes.
Since the list is persisted, this would result in the device entering a
boot loop upon reboot. Since no apps could be started, we cannot recover.

So in the case that the exemptions list causes problems, clear out the
list so we don't try to send it again next time. This means we will see
a single failure, but future attempts will succeed (obviously without
any whitelist). The device should not enter a boot loop.

Note, the test below relies on the fact that we can send at most 1024
arguments in a command to the Zygote (MAX_ZYGOTE_ARGC), and that each
item on the list is a separate argument.

Test: adb shell settings put global hidden_api_blacklist_exemptions \
Test:    $(for i in {1..1025}; do echo -n $i,; done)
Bug: 64382372
Change-Id: Ie47095d516c247ff6a8d667a2ac9b7be45f1acda
parent 8182e354
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -475,11 +475,14 @@ public class ZygoteProcess {
     * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
     *        whitelisted/public APIs (i.e. allowed, no logging of usage).
     */
    public void setApiBlacklistExemptions(List<String> exemptions) {
    public boolean setApiBlacklistExemptions(List<String> exemptions) {
        synchronized (mLock) {
            mApiBlacklistExemptions = exemptions;
            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
            boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true);
            if (ok) {
                ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
            }
            return ok;
        }
    }

@@ -499,12 +502,13 @@ public class ZygoteProcess {
    }

    @GuardedBy("mLock")
    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
    private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
        if (state == null || state.isClosed()) {
            return;
            Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection");
            return false;
        }
        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
            return;
            return true;
        }
        try {
            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
@@ -520,8 +524,11 @@ public class ZygoteProcess {
            if (status != 0) {
                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
            }
            return true;
        } catch (IOException ioe) {
            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
            mApiBlacklistExemptions = Collections.emptyList();
            return false;
        }
    }

+5 −1
Original line number Diff line number Diff line
@@ -2946,7 +2946,11 @@ public class ActivityManagerService extends IActivityManager.Stub
                            ? Collections.emptyList()
                            : Arrays.asList(exemptions.split(","));
                }
                zygoteProcess.setApiBlacklistExemptions(mExemptions);
                if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
                  Slog.e(TAG, "Failed to set API blacklist exemptions!");
                  // leave mExemptionsStr as is, so we don't try to send the same list again.
                  mExemptions = Collections.emptyList();
                }
            }
            int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);