CVE-2024-31317 AI-generated analysis
platform/frameworks/base, core/java/android/os/ZygoteProcess.java (system_server side of the zygote command protocol, hidden API exemption and process start paths)
Adds character validation in core/java/android/os/ZygoteProcess.java so that hidden API denylist exemption strings containing '\n', '\r' or '\u0000' are rejected (list cleared, nothing written) before reaching the zygote command socket, and process start arguments containing '\u0000' now throw ZygoteStartFailedEx alongside the existing newline and carriage return checks.
Root cause
The zygote wire format is a count followed by newline-separated argument strings (see the comment in zygoteSendArgsAndGetResult referencing com.android.internal.os.ZygoteArguments.parseArgs()). The method in ZygoteProcess.java that writes mApiDenylistExemptions to state.mZygoteOutputWriter (HiddenApiSettings.update() per the commit message) did not check the exemption strings for embedded '\n', '\r' or NUL before writing the count (size + 1) and the list. Because of this, embedded line breaks could break the newline-based framing. Separately, zygoteSendArgsAndGetResult rejected embedded '\n' and '\r' in process start args but not '\u0000'.
How the fix works
- core/java/android/os/ZygoteProcess.java:zygoteSendArgsAndGetResult: adds a third branch to the per-argument loop so that any arg with arg.indexOf('\u0000') >= 0 throws ZygoteStartFailedEx("Embedded nulls not allowed"), before msgStr is built and before either the USAP or the zygote socket path is used.
- core/java/android/os/ZygoteProcess.java (method writing mApiDenylistExemptions, HiddenApiSettings.update() per commit message): before writing the count to state.mZygoteOutputWriter, iterates over mApiDenylistExemptions and if any string contains '\n', '\r' or '\u0000' it logs Slog.e(LOG_TAG, "Failed to set API denylist exemptions: Bad character"), replaces mApiDenylistExemptions with Collections.emptyList() and returns false, so nothing is written to the zygote for that call and the bad list is not kept.
Completeness
- Both places named in the commit message are covered: the exemption write path and the process start argument path.
- The exemption validation is all-or-nothing: one bad string discards the entire exemption list rather than filtering the offending entry; the method returns false.
- Because the list is replaced with an empty list on failure, a bad list is not re-sent on later calls.
- The process start check covers both the USAP path (attemptUsapSendArgsAndGetResult uses the same msgStr) and the regular zygote path, since the loop runs before msgStr is built.
- Only '\n', '\r' and '\u0000' are rejected. The material does not show whether the zygote-side parser (com.android.internal.os.ZygoteArguments.parseArgs) validates input independently. It also does not show whether other writers to the zygote socket (e.g. the command around line 394 that calls writer.newLine()/writer.flush()) take caller-controlled strings.
Behavior changes
- Process start requests whose argument strings contain a NUL character now fail early with ZygoteStartFailedEx("Embedded nulls not allowed") instead of being sent to the zygote.
- Setting hidden API denylist exemptions containing '\n', '\r' or '\u0000' now logs "Failed to set API denylist exemptions: Bad character", clears the stored exemption list to Collections.emptyList(), and the update method returns false; no exemptions are sent to the zygote in that case.
Per major
- Android 14: same fix: commit c0c2c1ef78f2 (cherry-pick of 7ba059e2cf0a) in android-security-14.0.0_r9; all 5 patched lines present at android-security-14.0.0_r29.
- Android 15: not affected per the bulletin; the patched region (5/5 lines) is present at android-security-15.0.0_r17.
- Android 16: not affected per the bulletin; the patched region (5/5 lines) is present at android-security-16.0.0_r8.
- Android 17: not affected per the bulletin; the patched region (5/5 lines) is present at android-security-17.0.0_r1.
Unknowns
- The exact name of the method containing the second hunk is not shown in the diff hunk header; the commit message calls it HiddenApiSettings.update().
- Which callers (and with what privilege) can set the hidden API denylist exemptions or supply process start arguments is not shown.
- Whether the zygote-side parser (com.android.internal.os.ZygoteArguments.parseArgs) validates input independently is not shown.
- How the exemption strings are written after the count, and the concrete impact of the unvalidated characters, are not shown.
- Where mApiDenylistExemptions is set is not shown.
- How the fix was applied to Android 12, 12L and 13, which the bulletin lists as affected.