CVE-2024-0053 AI-generated analysis
platform/frameworks/base, print service (services/print, PrintManagerService)
PrintManagerService.getCustomPrinterIcon now checks URI-based custom printer icons returned by a print service and returns null for any icon whose URI encodes a user id whose profile parent is not getCurrentUserId().
Root cause
PrintManagerImpl.getCustomPrinterIcon returned userState.getCustomPrinterIcon(printerId) unmodified, inside a clearCallingIdentity block. Per the commit message, a third-party print plugin service can pass another user's User Icon URI. Because Settings holds INTERACT_ACROSS_USERS_FULL, this enables an exploit across the user boundary. For Icon.TYPE_URI and TYPE_URI_ADAPTIVE_BITMAP, the URI's encoded user info was not checked against the caller or the current profile group.
How the fix works
- services/print/java/com/android/server/print/PrintManagerService.java: PrintManagerImpl.getCustomPrinterIcon now stores the Icon from userState.getCustomPrinterIcon(printerId) and returns validateIconUserBoundary(icon). This is still inside the Binder.clearCallingIdentity/restoreCallingIdentity block.
- New private method PrintManagerImpl.validateIconUserBoundary(Icon): if the icon is non-null and of type Icon.TYPE_URI or Icon.TYPE_URI_ADAPTIVE_BITMAP, it reads icon.getUri().getEncodedUserInfo(). If that is null, the URI is treated as calling into the calling user's space and the icon is returned unchanged.
- If an encoded user is present, it is parsed with Integer.parseInt and passed through resolveCallingUserEnforcingPermissions(userId). Then, under mLock, resolveCallingProfileParentLocked(resolvedUserId) is compared to getCurrentUserId(). On mismatch the method returns null; otherwise it returns the original icon.
- Icons of other types are returned unchanged.
Completeness
- Only the getCustomPrinterIcon return path is validated. The icon stored in UserState is not sanitized when the print service reports it.
- Only the user-info component of the URI is checked; the authority and path are not restricted. The commit calls this a lightweight solution.
- Integer.parseInt(encodedUser) is not guarded, so non-numeric user info would throw NumberFormatException instead of being rejected.
- The check compares the profile parent of the URI's resolved user against getCurrentUserId(), not strictly against the calling user.
Behavior changes
- getCustomPrinterIcon returns null for URI-type icons whose encoded user's profile parent differs from getCurrentUserId(), so those printers show no custom icon.
- Non-integer user info in an icon URI makes the Binder call throw instead of returning the icon.
- resolveCallingUserEnforcingPermissions is now called on the URI-encoded user id, after clearCallingIdentity. Its effect in that context is not shown.
Per major
- Android 14: same fix: commit bf68f31ff7fa in android-security-14.0.0_r6 (cherry-pick of c58f2b7b262eee49130f47d72247615f07af4a81). At android-security-14.0.0_r29, 4/5 lines of the patched region are present.
- Android 15: not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-15.0.0_r17.
- Android 16: not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-16.0.0_r8.
- Android 17: not affected (not listed by the bulletin). 4/5 lines of the patched region are present at android-security-17.0.0_r1.
Unknowns
- Which line of the patched region differs on the latest security tags (14 r29, 15 r17, 16 r8, 17 r1) is not shown.
- The bodies of resolveCallingUserEnforcingPermissions, resolveCallingProfileParentLocked and getCurrentUserId are not in the material. So it cannot be determined whether the first can throw for the URI-encoded user id after clearCallingIdentity.
- How UserState.getCustomPrinterIcon obtains and caches the icon from the print service is not shown.
- Which component consumes and loads the returned icon URI is not shown beyond the commit message's mention of Settings.