You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
JsonReader silently accepts \uXXXX escapes containing lone surrogates in string values and field names #3113
JsonReader.readEscapeCharacter() (JsonReader.java:1855) reads 4 hex digits, casts to (char), and returns unchecked at line 1882. No Character.isSurrogate / 0xD800..0xDFFF gate anywhere. Callers at line 1244 (string-value) and line 1340 (skip-string) consume the raw char with no validation. Both setLenient(false) and Strictness.STRICT accept the malformed input.
The specs
RFC 8259 §8.2: "behavior of software that receives JSON texts containing such values is unpredictable; for example, implementations might return different values for the length of a string value or even suffer fatal runtime exceptions."
RFC 7493 §2.1 (I-JSON): "Object member names, and string values in arrays and object members, MUST NOT include code points that identify Surrogates or Noncharacters as defined by [UNICODE]... \uDEAD is invalid because it is an unpaired surrogate."
RFC 8785 §3.2.2.2 (JCS): "occurrences of such data MUST cause a compliant JCS implementation to terminate with an appropriate error."
ACCEPT means the parser returned successfully (no exception).
name.char0 is the hex value of the first char of the field name.
val.char0 is the hex value of the first char of the value.
Row 1: field name is "k" (0x6b), value's first char is 0xd800 — a lone high surrogate returned as-is in the Java String.
Row 2: same field name, value's first char is 0xdc00 — a lone low surrogate returned as-is.
Row 3: field NAME itself starts with 0xdc00 (a lone low surrogate); the value is just the digit 1 (0x31).
All three should have rejected under Strictness.STRICT per the RFC 7493 / RFC 8785 requirements above.
Suggested fix
The return at JsonReader.java:1882 is the site to add the guard. Rejecting under Strictness.STRICT preserves current behavior in Strictness.LEGACY_STRICT / Strictness.LENIENT — no Android-compat break.
Activity