CVE-2025-48542 AI-generated analysis
platform/frameworks/base, system_server AccountManagerService (services/core/java/com/android/server/accounts/AccountManagerService.java)
Adds per-account storage accounting to AccountManagerService. Once an account's estimated user-data and auth-token storage exceeds 30,000,000 divided by the number of accounts, new user-data writes are dropped silently and auth-token writes return false. Passwords longer than 1,000,000 characters are rejected on account add and on password change.
Root cause
AccountManagerService accepted user data (setUserdataInternal), auth tokens (the save path calling accounts.accountsDb.insertAuthToken) and passwords with no size limit. Only the account name was length-limited (existing 200-character check). Nothing tracked how much storage an account used in the accounts DB or in userDataCache/authTokenCache, so a caller able to write to an account could grow the database and the system_server caches without limit. The bulletin rates this as a High severity DoS.
How the fix works
- AccountManagerService.java: adds constants MAXIMUM_PASSWORD_LENGTH = 1000*1000 and STORAGE_LIMIT_PER_USER = 30*1000*1000, and a new per-UserAccounts map mCacheSizeForAccount (Account -> estimated size), documented as protected by cacheLock.
- AccountManagerService.java:computeEntrySize(key, value): returns key.length() + value.length() + 20 (a null key or value counts as 1) as the size estimate for one key/value entry.
- AccountManagerService.java:recomputeCacheSizeForAccountLocked(accounts, account): sums computeEntrySize over the account's entries in accounts.userDataCache and accounts.authTokenCache and stores the result in mCacheSizeForAccount.
- AccountManagerService.java:shouldBlockDatabaseWrite(accounts, account, key, value): adds the new entry's estimated size to the running total. It returns false early if the total is below STORAGE_LIMIT_PER_USER/100. Otherwise it counts the accounts in accounts.accountCache (minimum 1) and returns false if the total is below STORAGE_LIMIT_PER_USER/numberOfAccounts. If still over, it recomputes the size from the caches, adds the entry again and re-checks the same threshold. If the total is still over, it logs a warning and returns true.
- AccountManagerService.java:setUserdataInternal: inside dbLock, takes cacheLock. When value != null and shouldBlockDatabaseWrite returns true, it logs and returns without touching the DB (fail silently).
- AccountManagerService.java: auth-token save path (the method containing deleteAuthtokensByAccountIdAndType/insertAuthToken; its name is not shown in the hunk header): computes shouldBlockWrite under cacheLock before beginTransaction. After deleteAuthtokensByAccountIdAndType, if authToken != null and shouldBlockWrite is true, it logs and returns false before insertAuthToken, so setTransactionSuccessful is never reached.
- AccountManagerService.java: the account add path (the region with the existing 'Name longer than 200 chars' check) returns false when password != null and its length exceeds MAXIMUM_PASSWORD_LENGTH. The set-password path ('New password is too long' log) returns early for the same condition.
- AccountManagerService.java:writeUserDataIntoCacheLocked and writeAuthTokenIntoCacheLocked: when the per-account cache map has just been loaded from the DB (findUserExtrasForAccount / findAuthTokensByAccount), they call recomputeCacheSizeForAccountLocked. The lazy-load branches in readAuthTokenInternal and in the user-data read path do the same.
- AccountManagerService.java: the rename path calls recomputeCacheSizeForAccountLocked for renamedAccount. Two cache-cleanup sites remove the account from mCacheSizeForAccount alongside the other per-account caches; their method names are not shown in the hunk headers.
Completeness
- Covers the two key/value write paths (user data via setUserdataInternal, auth tokens via the insertAuthToken path) and limits password length on account add and on set-password.
- Password size is limited separately (1,000,000 characters) and is not included in the storage estimate. The account name was already limited to 200 characters by existing code.
- The estimate is approximate by design ('Estimation is not precise for updates to existing values'). The running total is incremented on every check, including overwrites, and is corrected by recomputeCacheSizeForAccountLocked only near the threshold. The recount uses only the in-memory caches.
- Deletions (value == null) are never blocked. Only userDataCache and authTokenCache entries are counted. Other per-account state (visibilityCache, previousNameCache, accountTokenCaches) is not counted by this change.
- The limit is per account (STORAGE_LIMIT_PER_USER / numberOfAccounts), and the fix does not cap the number of accounts. A code comment calls 100 the upper bound on accounts, but the material does not show where, or whether, that bound is enforced.
- In the auth-token path, the storage decision is made before the transaction under its own cacheLock acquisition, and both steps are inside dbLock.
Behavior changes
- User-data writes for an account whose estimated user data plus auth tokens exceed 30,000,000/numberOfAccounts (and at least 300,000) are dropped silently with a Log.w. No error is returned to the caller.
- Auth-token writes over the same limit return false from the save path, and setTransactionSuccessful is skipped. The pending deleteAuthtokensByAccountIdAndType is therefore not committed, assuming the transaction is ended in a finally block that the diff does not show.
- Account adds with a password longer than 1,000,000 characters fail (return false, Log.w). A set-password call with such a password is ignored (Log.w, early return).
- Writes near the limit add CPU work in system_server: a full recount of the account's cached entries and a count of all accounts in accountCache.
Per major
- Android 14: same fix: platform/frameworks/base 9a4d7193f893 in android-security-14.0.0_r22 (cherry-pick of 6b8138ca5279, Change-Id I88a0fef8e2e7bc232768bd5f7aa3f4bf87cb1c2c); 8/8 lines of the patched region present at android-security-14.0.0_r29.
- Android 15: same fix: platform/frameworks/base 7ed50e5b5d56 in android-security-15.0.0_r10. The hunks are the same; only the cancelNotification context line differs. 8/8 lines present at android-security-15.0.0_r17.
- Android 16: same fix: platform/frameworks/base 50ae729881da in android-security-16.0.0_r1. setUserdataInternal there takes an extra callingUid parameter, but the inserted block is the same. 8/8 lines present at android-security-16.0.0_r8.
- Android 17: not affected: not listed by the bulletin. The patched region is present (8/8 lines) at android-security-17.0.0_r1.
Unknowns
- The bulletin lists Android 13 as affected, but the material includes no Android 13 commit.
- The hunk headers (all 'public class AccountManagerService') do not name the enclosing methods of the auth-token save path, the account add path, the set-password path, the rename path or the cache-cleanup sites.
- Where, or whether, the comment-stated upper bound of 100 accounts per user is enforced is not shown.
- The material does not show which callers or permission levels can reach the affected write paths, or which resource is exhausted (DB size or system_server heap), beyond the bulletin's DoS classification. The commit was tested manually only.
- The finally block of the auth-token transaction is not shown, so the rollback of the preceding delete is inferred rather than visible.