{"uuid": "bd7829e9-c814-4e46-9774-b94eef05c002", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2024-31317", "type": "seen", "source": "https://gist.github.com/H3XDaemon/47b44057c0d70721380fbf33f2c4d496", "content": "# Technical Advisory: System Settings Persistence Failure via 64KB UTF-8 Serialization Overflow in OxygenOS / ColorOS\n\n**Component:** `frameworks/base/packages/SettingsProvider` / `com.android.providers.settings.SettingsState`  \n**Affected Platforms:** OxygenOS 13.x / ColorOS 13.x (Android 13, Snapdragon 865 / SM8250 platform tested)  \n**Impact:** Complete breakdown of `Settings.Secure` disk persistence; all user-configured settings revert upon system reboot.\n\n---\n\n## 1. Executive Summary\n\nOn certain OxygenOS/ColorOS firmware releases, modifications made to system settings (including `timepower_config`, display timeout, accessibility, and security preferences) revert to default values after a device reboot. \n\nInitial triage often misattributes this issue to third-party Xposed/LSPosed hooks, corrupt SQLite databases, or read-only filesystem mounts. Empirical tracing reveals the true root cause: an uncaught `java.io.UTFDataFormatException` within `SettingsState.doWriteState()`, triggered when a single OEM setting entry exceeds the 65,535-byte serialization limit enforced by Java's `FastDataOutput.writeUTF()` implementation.\n\nThis document details the serialization failure mechanism, traces the specific OEM string leakage key (`op_sysui_qs_tiles_hide`), and provides a non-destructive remediation procedure.\n\n---\n\n## 2. Technical Root Cause Analysis\n\n### 2.1 Java 64 KB UTF-8 Serialization Boundary Violation\nAndroid's `SettingsProvider` maintains in-memory setting tables (`system`, `secure`, `global`) that are periodically flushed to disk at `/data/system/users/0/settings_*.xml`.\n\nDuring XML binary/text serialization, `com.android.internal.util.FastDataOutput.writeUTF()` serializes string attributes. Java's Modified UTF-8 specification encodes string length as an unsigned 16-bit integer (`u2`), capping the maximum string length at **65,535 bytes**:\n\n$$\\text{Max UTF-8 Length} = 2^{16} - 1 = 65,535 \\text{ bytes}$$\n\nIf a single setting string exceeds 65,535 bytes, the length calculation overflows to a negative integer value. `FastDataOutput.writeUTF()` throws a `UTFDataFormatException`:\n\n```text\nE SettingsState: Failed to write settings, restoring backup\nE SettingsState: java.io.IOException: Modified UTF-8 length too large: -66142\n    at com.android.internal.util.FastDataOutput.writeUTF(FastDataOutput.java:167)\n    at com.android.internal.util.BinaryXmlSerializer.attribute(BinaryXmlSerializer.java:209)\n    at com.android.providers.settings.SettingsState.setValueAttribute(SettingsState.java:991)\n    at com.android.providers.settings.SettingsState.writeSingleSetting(SettingsState.java:968)\n    at com.android.providers.settings.SettingsState.doWriteState(SettingsState.java:853)\n```\n\n### 2.2 OEM SystemUI String Concatenation Memory Leak\nInspection of `Settings.Secure` revealed an OEM-specific key:\n- **Key:** `op_sysui_qs_tiles_hide`\n- **Measured String Length:** 66,165 characters\n- **Target Package:** `/system_ext/priv-app/SystemUI/SystemUI.apk` (`classes.dex`)\n- **Disassembled Class:** `Lcom/android/systemui/qs/QSTileHost$OperatorCustom$HideTileBroadcastReceiver;` (Class Def #5093, Bytecode Offsets: `0x51d9a2`, `0x51dd12`)\n\n**Mechanism:** OxygenOS/ColorOS SystemUI dynamically updates `op_sysui_qs_tiles_hide` when tile visibility changes (e.g., Wi-Fi Calling status updates, SIM card state transitions, or Quick Settings customization). Within `QSTileHost$OperatorCustom$HideTileBroadcastReceiver.onReceive()`, string updates lack a deduplication check (`.contains()`). Tile package strings are repeatedly appended:\n\n$$\\text{Key Value} = \\text{Key Value}_{\\text{existing}} + \\text{\",\"} + \\text{Tile Package Name}$$\n\nOver extended device usage, the string size eventually passes the 65,535-byte threshold.\n\n### 2.3 Transaction Abort and Disk Persistence Lockout\nWhen any key in `Settings.Secure` is modified (via the Settings GUI or ADB), `SettingsState` schedules a batch write to disk. \n\nWhen serialization reaches the oversized `op_sysui_qs_tiles_hide` entry, `FastDataOutput.writeUTF()` throws `UTFDataFormatException`. `SettingsState` catches the exception, aborts the write transaction, and restores `/data/system/users/0/settings_secure.xml` from the backup file on disk.\n\nConsequently:\n1. In-memory changes persist only for the duration of the current boot session.\n2. The physical disk file `/data/system/users/0/settings_secure.xml` is never updated.\n3. Upon reboot, `SettingsProvider` reloads the stale XML file from disk, discarding all user modifications.\n\n### 2.4 Secondary Hardware Alarm State Desynchronization\nFor scheduled power configurations (`timepower_config`), OxygenOS maintains a system property:\n- `persist.sys.poweralarm.time` (Unix epoch timestamp in milliseconds).\n\nBecause `SettingsState` fails to commit the updated `timepower_config` to disk, `persist.sys.poweralarm.time` retains a legacy timestamp (e.g., `1785020404000`, corresponding to 07:00 AM). On system boot, `init` reloads `persist.sys.poweralarm.time`, and `com.qualcomm.qti.poweroffalarm` re-injects the 07:00 AM power-on alarm into `SettingsProvider`, enforcing a persistent state loop.\n\n---\n\n## 3. Diagnostic Protocol\n\n### 3.1 Logcat Inspection\nFilter system logs for serialization failures within `SettingsState`:\n\n```bash\nadb shell \"su -c 'logcat -d | grep -i SettingsState'\"\n```\n\nLook for the following signature:\n```text\nE SettingsState: Failed to write settings, restoring backup : Modified UTF-8 length too large\n```\n\n### 3.2 Key Length Inspection\nIdentify keys exceeding byte limits using Python over ADB:\n\n```bash\npython -c \"import subprocess; out=subprocess.check_output(['adb', 'shell', 'settings list secure']).decode('utf-8', errors='ignore'); [print(line[:80], '-&gt; len=', len(line)) for line in out.splitlines() if len(line) &gt; 1000]\"\n```\n\n---\n\n## 4. Remediation Procedure\n\nExecute the following commands in an elevated root shell (`su`) to remove the corrupted entry and clear hardware property locks.\n\n### Step 1: Remove the Oversized Setting Entry\n```bash\nadb shell \"su -c 'settings delete secure op_sysui_qs_tiles_hide'\"\n```\n\n### Step 2: Clear Stale Hardware Alarm Properties\n```bash\nadb shell \"su -c 'setprop persist.sys.poweralarm.time 0'\"\nadb shell \"su -c 'setprop sys.power_off_alarm 0'\"\n```\n\n### Step 3: Write Target Settings Entry\n```bash\nadb shell \"su -c 'settings put secure timepower_config 070000127230000127'\"\n```\n\n---\n\n## 5. Verification\n\n### 5.1 Pre-Reboot Disk State Verification\nConfirm that `SettingsState` successfully serializes to disk without throwing exceptions:\n\n```bash\nadb shell \"su -c 'strings /data/system/users/0/settings_secure.xml | grep -A 2 -B 2 timepower_config'\"\n```\n\n*Expected Output:* The target string `070000127230000127` is present in `settings_secure.xml`.\n\n### 5.2 Post-Reboot Verification\nReboot the device:\n\n```bash\nadb reboot\n```\n\nAfter boot completion, query the setting and property states:\n\n```bash\nadb shell settings get secure timepower_config\n# Expected: 070000127230000127\n\nadb shell getprop persist.sys.poweralarm.time\n# Expected: 0\n```\n\n---\n\n## 6. References &amp; Related Research\n\n1. **AOSP Issue Tracker &amp; FastDataOutput Specification**:\n   `com.android.internal.util.FastDataOutput.writeUTF()` implementation details and unsigned 16-bit string length boundaries.\n2. **CVE-2024-31317 Research / PoC**:\n   `SettingsState` denial-of-service vector triggered via intentionally oversized string attributes in `SettingsProvider`.\n   Reference Gist: `https://gist.github.com/rabits/eef4fad0bd024786a3afde2bc1f32b7e`\n", "creation_timestamp": "2026-07-25T12:26:12.152179Z"}