From ad48bb99fddfb4e020673a1ef73789a3fe18218e Mon Sep 17 00:00:00 2001 From: "Alam, Sahibex" Date: Sat, 1 Mar 2025 07:37:40 +0000 Subject: [PATCH] ASB MAR 2025 Security Patches integration Integrating Google Android Security Bulletin Patches. Test done: STS r36 All TCs Passed except 3 TCs failed due to Bluetooth & Kernel LTS Update required, Test: KernelLts Test#testRequiredKernelLts_ENFORCING KernelLts Test#testRequiredKernelLts_WARN android.security.cts.CVE_2024_43084#testCVE_2024_43084 Update in upcoming ASB WIP. Tracked-On: OAM-130583 Signed-off-by: Alam, Sahibex --- ...4-Update-security_patch_level-string.patch | 2 +- ...anTables-when-decoding-jpeg.bulletin.patch | 32 ++ ..._0304-Restrict-access-to-directories.patch | 77 +++++ ...e-Android-data-obb-sanbox-on-shared-.patch | 106 ++++++ ...count-user-icon-validation-.bulletin.patch | 88 +++++ ...stractAccountAuthenticator-.bulletin.patch | 41 +++ ...le-issue-in-UninstallerActi.bulletin.patch | 71 ++++ ...nsistent-flags-for-self-reg.bulletin.patch | 32 ++ ...uri-belongs-to-current-user.bulletin.patch | 315 ++++++++++++++++++ ...sions-for-not-bonded-device.bulletin.patch | 51 +++ ...nloadprovider-insert-method.bulletin.patch | 82 +++++ ...count-user-icon-validation-.bulletin.patch | 85 +++++ ...revent-use-after-free-of-HID-reports.patch | 70 ++++ ...or-avdtp-and-avctp-channels.bulletin.patch | 113 +++++++ 14 files changed, 1164 insertions(+), 1 deletion(-) create mode 100644 aosp_diff/preliminary/external/dng_sdk/0001-Catch-null-HuffmanTables-when-decoding-jpeg.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0304-Restrict-access-to-directories.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0305-DO-NOT-MERGE-Hide-Android-data-obb-sanbox-on-shared-.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0306-Resolve-cross-account-user-icon-validation-.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0308-Check-account-type-returned-by-AbstractAccountAuthenticator-.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0309-RESTRICT-AUTOMERGE-PM-Fix-the-profile-issue-in-UninstallerActi.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/native/24_00240001-servicemanager-set-consistent-flags-for-self-reg.bulletin.patch create mode 100644 aosp_diff/preliminary/packages/apps/Bluetooth/07_0007-backport-opp-validate-that-content-uri-belongs-to-current-user.bulletin.patch create mode 100644 aosp_diff/preliminary/packages/apps/Bluetooth/08_0008-Reset-permissions-for-not-bonded-device.bulletin.patch create mode 100644 aosp_diff/preliminary/packages/providers/DownloadProvider/0004-Ensure-ownership-validation-in-downloadprovider-insert-method.bulletin.patch create mode 100644 aosp_diff/preliminary/packages/services/Telecomm/16_0016-Resolve-cross-account-user-icon-validation-.bulletin.patch create mode 100644 aosp_diff/preliminary/system/bt/68_0068-Prevent-use-after-free-of-HID-reports.patch create mode 100644 aosp_diff/preliminary/system/bt/69_0069-Use-encrypted-link-for-avdtp-and-avctp-channels.bulletin.patch diff --git a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch index 19399c2305..572560c795 100644 --- a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch +++ b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch @@ -20,7 +20,7 @@ index 47bb92c142..2d0ac256a4 100644 # It must match one of the Android Security Patch Level strings of the Public Security Bulletins. # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty. - PLATFORM_SECURITY_PATCH := 2022-02-05 -+ PLATFORM_SECURITY_PATCH := 2025-02-01 ++ PLATFORM_SECURITY_PATCH := 2025-03-01 endif .KATI_READONLY := PLATFORM_SECURITY_PATCH diff --git a/aosp_diff/preliminary/external/dng_sdk/0001-Catch-null-HuffmanTables-when-decoding-jpeg.bulletin.patch b/aosp_diff/preliminary/external/dng_sdk/0001-Catch-null-HuffmanTables-when-decoding-jpeg.bulletin.patch new file mode 100644 index 0000000000..d32c0ad836 --- /dev/null +++ b/aosp_diff/preliminary/external/dng_sdk/0001-Catch-null-HuffmanTables-when-decoding-jpeg.bulletin.patch @@ -0,0 +1,32 @@ +From 60d501a8adb5118aa57733a527185a626f0b745d Mon Sep 17 00:00:00 2001 +From: Nick Chusid +Date: Tue, 10 Dec 2024 22:46:28 +0000 +Subject: [PATCH] Catch null HuffmanTables when decoding jpeg + +Bug: 347735428 +Test: TreeHugger +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:caa282a34ca505eef317c3f3eb7cac83771f357c) +Merged-In: Iaf36c076467d18e4e31d8436a6d199c3276a8786 +Change-Id: Iaf36c076467d18e4e31d8436a6d199c3276a8786 +--- + source/dng_lossless_jpeg.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/source/dng_lossless_jpeg.cpp b/source/dng_lossless_jpeg.cpp +index 9d0d01a..8802f32 100644 +--- a/source/dng_lossless_jpeg.cpp ++++ b/source/dng_lossless_jpeg.cpp +@@ -1616,6 +1616,10 @@ inline int32 dng_lossless_decoder::get_bit () + inline int32 dng_lossless_decoder::HuffDecode (HuffmanTable *htbl) + { + ++ if (htbl == nullptr) { ++ ThrowBadFormat (); ++ } ++ + // If the huffman code is less than 8 bits, we can use the fast + // table lookup to get its value. It's more than 8 bits about + // 3-4% of the time. +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0304-Restrict-access-to-directories.patch b/aosp_diff/preliminary/frameworks/base/99_0304-Restrict-access-to-directories.patch new file mode 100644 index 0000000000..e21ef43798 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0304-Restrict-access-to-directories.patch @@ -0,0 +1,77 @@ +From 7f93e424f477596dc356a020b0bd12ec5a283da5 Mon Sep 17 00:00:00 2001 +From: Dipankar Bhardwaj +Date: Wed, 21 Aug 2024 14:26:50 +0000 +Subject: [PATCH] Restrict access to directories + +Restricted access to Android/data, Android/obb and Android/sandbox +directories and its sub-directories. Replacing path's pattern match +check with file equality check. + +Test: atest DocumentsClientTest +Bug: 341680936 +Flag: EXEMPT bug fix +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:22ea85377ad49594e46c57b398fb477d3d12c668) +Merged-In: I8879900e57e1702d11797b81e86d0cc3f55bac22 +Change-Id: I8879900e57e1702d11797b81e86d0cc3f55bac22 +--- + .../ExternalStorageProvider.java | 18 +++--------------- + 1 file changed, 3 insertions(+), 15 deletions(-) + +diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +index 8f31248d9039..faef38b33429 100644 +--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java ++++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +@@ -16,8 +16,6 @@ + + package com.android.externalstorage; + +-import static java.util.regex.Pattern.CASE_INSENSITIVE; +- + import android.annotation.NonNull; + import android.annotation.Nullable; + import android.app.usage.StorageStatsManager; +@@ -69,7 +67,6 @@ import java.util.List; + import java.util.Locale; + import java.util.Objects; + import java.util.UUID; +-import java.util.regex.Pattern; + import java.util.stream.Collectors; + + /** +@@ -97,13 +94,6 @@ public class ExternalStorageProvider extends FileSystemProvider { + + private static final String STORAGE_PATH = "/storage/"; + +- /** +- * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and +- * {@code /Android/sandbox/} along with all their subdirectories and content. +- */ +- private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES = +- Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE); +- + private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, + Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, Root.COLUMN_QUERY_ARGS +@@ -315,16 +305,14 @@ public class ExternalStorageProvider extends FileSystemProvider { + if (isOnRemovableUsbStorage(documentId)) { + return false; + } +- final String path = getPathFromDocId(documentId); +- return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches(); +- +- /*try { ++ ++ try { + final RootInfo root = getRootFromDocId(documentId); + final String canonicalPath = getPathFromDocId(documentId); + return isRestrictedPath(root.rootId, canonicalPath); + } catch (Exception e) { + return true; +- }*/ ++ } + } + + /** +-- +2.46.0 + diff --git a/aosp_diff/preliminary/frameworks/base/99_0305-DO-NOT-MERGE-Hide-Android-data-obb-sanbox-on-shared-.patch b/aosp_diff/preliminary/frameworks/base/99_0305-DO-NOT-MERGE-Hide-Android-data-obb-sanbox-on-shared-.patch new file mode 100644 index 0000000000..b911e02f0d --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0305-DO-NOT-MERGE-Hide-Android-data-obb-sanbox-on-shared-.patch @@ -0,0 +1,106 @@ +From c0f01a95c6748e5c75223ea355cd08a7c5a07717 Mon Sep 17 00:00:00 2001 +From: Sergey Nikolaienkov +Date: Sat, 1 Jul 2023 16:03:56 +0200 +Subject: [PATCH] DO NOT MERGE: "Hide" /Android/data|obb|sanbox/ on shared + storage + +Implement shouldHideDocument() in the ExternalStorageProvider so that it +resitcts access to 'Android/data/', 'Android/obb/' and 'Android/sandbox' +on the integrated shared storage along with all their content and +subdirectories. + +Clean up the abstract FileSystemProvider, specifically all variants of +queryChildDocuments(). + +Bug: 200034476 +Bug: 220066255 +Bug: 283962634 +Test: make & flash systemimage, run manually +Test: atest ExternalStorageProviderTests +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9a9602a68d6d7acb8e9bd6fa37ca93f11d6dd213) +Merged-In: I48c2ce7ff2d7fc067961ea2af0ea63818316f086 +Change-Id: I48c2ce7ff2d7fc067961ea2af0ea63818316f086 +--- + .../ExternalStorageProvider.java | 27 +++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +index fdeccfa7b9d0..8f31248d9039 100644 +--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java ++++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +@@ -16,6 +16,8 @@ + + package com.android.externalstorage; + ++import static java.util.regex.Pattern.CASE_INSENSITIVE; ++ + import android.annotation.NonNull; + import android.annotation.Nullable; + import android.app.usage.StorageStatsManager; +@@ -67,6 +69,7 @@ import java.util.List; + import java.util.Locale; + import java.util.Objects; + import java.util.UUID; ++import java.util.regex.Pattern; + import java.util.stream.Collectors; + + /** +@@ -93,6 +96,13 @@ public class ExternalStorageProvider extends FileSystemProvider { + private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/"; + + private static final String STORAGE_PATH = "/storage/"; ++ ++ /** ++ * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and ++ * {@code /Android/sandbox/} along with all their subdirectories and content. ++ */ ++ private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES = ++ Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE); + + private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, +@@ -305,14 +315,16 @@ public class ExternalStorageProvider extends FileSystemProvider { + if (isOnRemovableUsbStorage(documentId)) { + return false; + } ++ final String path = getPathFromDocId(documentId); ++ return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches(); + +- try { ++ /*try { + final RootInfo root = getRootFromDocId(documentId); + final String canonicalPath = getPathFromDocId(documentId); + return isRestrictedPath(root.rootId, canonicalPath); + } catch (Exception e) { + return true; +- } ++ }*/ + } + + /** +@@ -628,6 +640,13 @@ public class ExternalStorageProvider extends FileSystemProvider { + return result; + } + ++ /** ++ * Print the state into the given stream. ++ * Gets invoked when you run: ++ *
++     * adb shell dumpsys activity provider com.android.externalstorage/.ExternalStorageProvider
++     * 
++ */ + @Override + public Path findDocumentPath(@Nullable String parentDocId, String childDocId) + throws FileNotFoundException { +@@ -829,4 +848,8 @@ public class ExternalStorageProvider extends FileSystemProvider { + private static boolean equalIgnoringCase(@NonNull String a, @NonNull String b) { + return TextUtils.equals(a.toLowerCase(Locale.ROOT), b.toLowerCase(Locale.ROOT)); + } ++ ++ private static boolean equalIgnoringCase(@NonNull String a, @NonNull String b) { ++ return TextUtils.equals(a.toLowerCase(Locale.ROOT), b.toLowerCase(Locale.ROOT)); ++ } + } +-- +2.46.0 + diff --git a/aosp_diff/preliminary/frameworks/base/99_0306-Resolve-cross-account-user-icon-validation-.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0306-Resolve-cross-account-user-icon-validation-.bulletin.patch new file mode 100644 index 0000000000..5185bd1408 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0306-Resolve-cross-account-user-icon-validation-.bulletin.patch @@ -0,0 +1,88 @@ +From 9654f5726275407dc30fddcaea3a08f8c8b7362e Mon Sep 17 00:00:00 2001 +From: Pranav Madapurmath +Date: Thu, 2 Jan 2025 14:58:50 -0800 +Subject: [PATCH] Resolve cross account user icon validation. + +Resolves a vulnerability found with the cross account user icon +validation in StatusHint and TelecomServiceImpl (when registering a +phone account). The reporter found that an uri formatted as `userId%` +isn't parsed properly with the existing reference to Uri.encodedUserInfo. + +Bug: 376461551 +Bug: 376259166 +Flag: EXEMPT bugfix +Test: atest TelecomServiceImplTest +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c28c4deeb27275c4fc68ea723202ed9faea9c265) +Merged-In: I25614ead889501f4553ed2b42b366e09a47b0c9f +Change-Id: I25614ead889501f4553ed2b42b366e09a47b0c9f +--- + .../java/android/telecom/StatusHints.java | 34 +++++++++++++++---- + 1 file changed, 28 insertions(+), 6 deletions(-) + +diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java +index b7346331dc60..907a4b2a7000 100644 +--- a/telecomm/java/android/telecom/StatusHints.java ++++ b/telecomm/java/android/telecom/StatusHints.java +@@ -27,6 +27,7 @@ import android.os.Bundle; + import android.os.Parcel; + import android.os.Parcelable; + import android.os.UserHandle; ++import android.util.Log; + + import com.android.internal.annotations.VisibleForTesting; + +@@ -40,6 +41,7 @@ public final class StatusHints implements Parcelable { + private final CharSequence mLabel; + private Icon mIcon; + private final Bundle mExtras; ++ private static final String TAG = StatusHints.class.getSimpleName(); + + /** + * @hide +@@ -150,17 +152,37 @@ public final class StatusHints implements Parcelable { + // incompatible types. + if (icon != null && (icon.getType() == Icon.TYPE_URI + || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { +- String encodedUser = icon.getUri().getEncodedUserInfo(); +- // If there is no encoded user, the URI is calling into the calling user space +- if (encodedUser != null) { +- int userId = Integer.parseInt(encodedUser); +- // Do not try to save the icon if the user id isn't in the calling user space. +- if (userId != callingUserHandle.getIdentifier()) return null; ++ int callingUserId = callingUserHandle.getIdentifier(); ++ int requestingUserId = getUserIdFromAuthority( ++ icon.getUri().getAuthority(), callingUserId); ++ if (callingUserId != requestingUserId) { ++ return null; + } ++ + } + return icon; + } + ++ /** ++ * Derives the user id from the authority or the default user id if none could be found. ++ * @param auth ++ * @param defaultUserId ++ * @return The user id from the given authority. ++ * @hide ++ */ ++ public static int getUserIdFromAuthority(String auth, int defaultUserId) { ++ if (auth == null) return defaultUserId; ++ int end = auth.lastIndexOf('@'); ++ if (end == -1) return defaultUserId; ++ String userIdString = auth.substring(0, end); ++ try { ++ return Integer.parseInt(userIdString); ++ } catch (NumberFormatException e) { ++ Log.w(TAG, "Error parsing userId." + e); ++ return UserHandle.USER_NULL; ++ } ++ } ++ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeCharSequence(mLabel); +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0308-Check-account-type-returned-by-AbstractAccountAuthenticator-.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0308-Check-account-type-returned-by-AbstractAccountAuthenticator-.bulletin.patch new file mode 100644 index 0000000000..87dff63b15 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0308-Check-account-type-returned-by-AbstractAccountAuthenticator-.bulletin.patch @@ -0,0 +1,41 @@ +From 4b99ccbe8fe9b7ad8f187a3175e0b38976f8b0c8 Mon Sep 17 00:00:00 2001 +From: Dmitry Dementyev +Date: Thu, 19 Dec 2024 11:02:42 -0800 +Subject: [PATCH] Check account type returned by AbstractAccountAuthenticator. + +AccountManagerService already knows which account is used during +AbstractAccountAuthenticator.getAuthToken. + +KEY_ACCOUNT_NAME and KEY_ACCOUNT_TYPE in the response look unnecessary, +but we can't change API at this moment. + +Bug: 364269936 +Test: manual +Flag: EXEMPT bugfix +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:7b3d13643fdbad661f7a388188b1b7e74dffe03f) +Merged-In: Ifc62866f4feaca43abc32bc542b97f3741953f56 +Change-Id: Ifc62866f4feaca43abc32bc542b97f3741953f56 +--- + .../com/android/server/accounts/AccountManagerService.java | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java +index 5f434ee267d4..5b83a02de8b2 100644 +--- a/services/core/java/com/android/server/accounts/AccountManagerService.java ++++ b/services/core/java/com/android/server/accounts/AccountManagerService.java +@@ -3070,6 +3070,12 @@ public class AccountManagerService + "the type and name should not be empty"); + return; + } ++ if (!type.equals(mAccountType)) { ++ onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, ++ "incorrect account type"); ++ return; ++ } ++ + Account resultAccount = new Account(name, type); + if (!customTokens) { + saveAuthTokenToDatabase( +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0309-RESTRICT-AUTOMERGE-PM-Fix-the-profile-issue-in-UninstallerActi.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0309-RESTRICT-AUTOMERGE-PM-Fix-the-profile-issue-in-UninstallerActi.bulletin.patch new file mode 100644 index 0000000000..e1e5718173 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0309-RESTRICT-AUTOMERGE-PM-Fix-the-profile-issue-in-UninstallerActi.bulletin.patch @@ -0,0 +1,71 @@ +From ba69150dc9e950fb0b4ec0fe5288f96b3abfae7f Mon Sep 17 00:00:00 2001 +From: Ivan Chiang +Date: Tue, 24 Dec 2024 08:22:39 +0000 +Subject: [PATCH] RESTRICT AUTOMERGE [PM] Fix the profile issue in + UninstallerActivity + +Only the parent profile can uninstall the app that is in the child +profiles. + +Flag: EXEMPT security bug fix +Bug: 333681693 +Test: atest CtsPackageInstallerCUJMultiUsersTestCases +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:585d5d6835dd2ddd65316fbbabd714c140da20fa) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9dd1962758bbc6d0a25aecf5d9fd0967f3eef085) +Merged-In: Id4eb5484563fdec530d5fc89a2c5973c351fdab8 +Change-Id: Id4eb5484563fdec530d5fc89a2c5973c351fdab8 +--- + .../packageinstaller/UninstallerActivity.java | 20 ++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + mode change 100755 => 100644 packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java + +diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +old mode 100755 +new mode 100644 +index 0198168f9fda..f487bfbd28c7 +--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java ++++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +@@ -51,6 +51,7 @@ import android.net.Uri; + import android.os.Build; + import android.os.Bundle; + import android.os.IBinder; ++import android.os.Process; + import android.os.RemoteException; + import android.os.ServiceManager; + import android.os.UserHandle; +@@ -63,8 +64,6 @@ import com.android.packageinstaller.television.ErrorFragment; + import com.android.packageinstaller.television.UninstallAlertFragment; + import com.android.packageinstaller.television.UninstallAppProgress; + +-import java.util.List; +- + /* + * This activity presents UI to uninstall an application. Usually launched with intent + * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute +@@ -172,13 +171,16 @@ public class UninstallerActivity extends Activity { + if (mDialogInfo.user == null) { + mDialogInfo.user = android.os.Process.myUserHandle(); + } else { +- UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); +- List profiles = userManager.getUserProfiles(); +- if (!profiles.contains(mDialogInfo.user)) { +- Log.e(TAG, "User " + android.os.Process.myUserHandle() + " can't request uninstall " +- + "for user " + mDialogInfo.user); +- showUserIsNotAllowed(); +- return; ++ if (!mDialogInfo.user.equals(Process.myUserHandle())) { ++ UserManager userManager = getBaseContext().getSystemService(UserManager.class); ++ final boolean isCurrentUserProfileOwner = Process.myUserHandle().equals( ++ userManager.getProfileParent(mDialogInfo.user)); ++ if (!isCurrentUserProfileOwner) { ++ Log.e(TAG, "User " + Process.myUserHandle() + " can't request uninstall " ++ + "for user " + mDialogInfo.user); ++ showUserIsNotAllowed(); ++ return; ++ } + } + } + +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/frameworks/native/24_00240001-servicemanager-set-consistent-flags-for-self-reg.bulletin.patch b/aosp_diff/preliminary/frameworks/native/24_00240001-servicemanager-set-consistent-flags-for-self-reg.bulletin.patch new file mode 100644 index 0000000000..0f9a1543b5 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/native/24_00240001-servicemanager-set-consistent-flags-for-self-reg.bulletin.patch @@ -0,0 +1,32 @@ +From fea775468179d34ae0a601010c97399a1e4f3a7d Mon Sep 17 00:00:00 2001 +From: Steven Moreland +Date: Fri, 6 Dec 2024 23:55:35 +0000 +Subject: [PATCH] servicemanager: set consistent flags for self-reg + +SM has special flags set to become a context object, but +these were not set when it registers as itself. + +Bug: 382775095 +Test: boot w/ harsher checks +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6ac542d1d0420f97ddef52991533662e96760fa9) +Merged-In: I0fb567cbcca67a2fc6c088f652c8af570b8d7e53 +Change-Id: I0fb567cbcca67a2fc6c088f652c8af570b8d7e53 +--- + cmds/servicemanager/main.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp +index 8c1beaca20..d8cc8e6a20 100644 +--- a/cmds/servicemanager/main.cpp ++++ b/cmds/servicemanager/main.cpp +@@ -122,6 +122,7 @@ int main(int argc, char** argv) { + ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY); + + sp manager = sp::make(std::make_unique()); ++ manager->setRequestingSid(true); + if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) { + LOG(ERROR) << "Could not self register servicemanager"; + } +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/packages/apps/Bluetooth/07_0007-backport-opp-validate-that-content-uri-belongs-to-current-user.bulletin.patch b/aosp_diff/preliminary/packages/apps/Bluetooth/07_0007-backport-opp-validate-that-content-uri-belongs-to-current-user.bulletin.patch new file mode 100644 index 0000000000..12a5d11b6d --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Bluetooth/07_0007-backport-opp-validate-that-content-uri-belongs-to-current-user.bulletin.patch @@ -0,0 +1,315 @@ +From 210f05f7875d81f8432251bfec2bbb890df4658a Mon Sep 17 00:00:00 2001 +From: Billy Huang +Date: Mon, 23 Sep 2024 13:53:14 -0700 +Subject: [PATCH] backport "opp: validate that content uri belongs to current + user" + +Check that the userInfo part of the content URI is +either implicit or the same as the current userId. + +Bug: 296915500 +Flag: EXEMPT fix for vulnerability +Test: atest GoogleBluetoothInstrumentationTests:BluetoothOppSendFileInfoTest +Ignore-AOSP-First: fix for undisclosed vulnerability +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:534e3a698d4805ac28f9d7916dbc7074a2ca4aad) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a1cc82a19f3c4368c7de2430fac27517f346b034) +Merged-In: I484c99fc392fb172e0c598511204ba520049f4e9 +Change-Id: I484c99fc392fb172e0c598511204ba520049f4e9 +--- + .../opp/BluetoothOppSendFileInfo.java | 15 ++ + .../opp/BluetoothOppSendFileInfoTest.java | 229 ++++++++++++++++++ + 2 files changed, 244 insertions(+) + create mode 100644 tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java + +diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +index 46e3ba144..10087c9f1 100644 +--- a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java ++++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +@@ -32,6 +32,8 @@ + + package com.android.bluetooth.opp; + ++import static android.os.UserHandle.myUserId; ++ + import android.content.ContentResolver; + import android.content.Context; + import android.content.res.AssetFileDescriptor; +@@ -39,6 +41,7 @@ import android.database.Cursor; + import android.database.sqlite.SQLiteException; + import android.net.Uri; + import android.provider.OpenableColumns; ++import android.text.TextUtils; + import android.util.EventLog; + import android.util.Log; + +@@ -48,6 +51,7 @@ import java.io.File; + import java.io.FileInputStream; + import java.io.FileNotFoundException; + import java.io.IOException; ++import java.util.Objects; + + /** + * This class stores information about a single sending file It will only be +@@ -116,6 +120,11 @@ public class BluetoothOppSendFileInfo { + return SEND_FILE_INFO_ERROR; + } + ++ if (isContentUriForOtherUser(uri)) { ++ Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId()); ++ return SEND_FILE_INFO_ERROR; ++ } ++ + contentType = contentResolver.getType(uri); + Cursor metadataCursor; + try { +@@ -247,6 +256,12 @@ public class BluetoothOppSendFileInfo { + return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0); + } + ++ private static boolean isContentUriForOtherUser(Uri uri) { ++ String uriUserId = uri.getUserInfo(); ++ return !TextUtils.isEmpty(uriUserId) ++ && !Objects.equals(uriUserId, String.valueOf(myUserId())); ++ } ++ + private static long getStreamSize(FileInputStream is) throws IOException { + long length = 0; + byte[] unused = new byte[4096]; +diff --git a/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java b/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +new file mode 100644 +index 000000000..cdd788c71 +--- /dev/null ++++ b/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2024 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++package com.android.bluetooth.opp; ++ ++import static android.os.UserHandle.myUserId; ++ ++import static com.google.common.truth.Truth.assertThat; ++ ++import static org.mockito.ArgumentMatchers.any; ++import static org.mockito.ArgumentMatchers.eq; ++import static org.mockito.Mockito.doReturn; ++import static org.mockito.Mockito.mock; ++import static org.mockito.Mockito.spy; ++ ++import android.content.ContentResolver; ++import android.content.Context; ++import android.content.IContentProvider; ++import android.content.pm.ApplicationInfo; ++import android.content.res.AssetFileDescriptor; ++import android.database.MatrixCursor; ++import android.net.Uri; ++import android.os.Build; ++import android.provider.OpenableColumns; ++import android.test.mock.MockContentProvider; ++import android.test.mock.MockContext; ++ ++import androidx.test.runner.AndroidJUnit4; ++ ++import org.junit.Before; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++ ++import java.io.FileInputStream; ++ ++@RunWith(AndroidJUnit4.class) ++public class BluetoothOppSendFileInfoTest { ++ public static final String PROVIDER_NAME_MEDIA = "media"; ++ ++ TestContext mContext; ++ TestContentResolver mContentResolver; ++ MockContentProvider mContentProvider; ++ MatrixCursor mCursor; ++ ++ private static Uri buildContentUriWithEncodedAuthority(String authority) { ++ return new Uri.Builder().scheme("content") ++ .encodedAuthority(authority) ++ .path("external/images/media/1") ++ .build(); ++ } ++ ++ @Before ++ public void setUp() { ++ mContext = new TestContext(); ++ mContentResolver = mContext.getContentResolver(); ++ mContentProvider = mContext.getContentProvider(); ++ } ++ ++ @Test ++ public void generateFileInfo_withContentUriForOtherUser_returnsSendFileInfoError() ++ throws Exception { ++ String type = "image/jpeg"; ++ Uri uri = buildContentUriWithEncodedAuthority((myUserId() + 1) + "@" + PROVIDER_NAME_MEDIA); ++ doReturn(type).when(mContentProvider).getType(any()); ++ ++ long fileLength = 1000; ++ String fileName = "pic.jpg"; ++ ++ FileInputStream fs = mock(FileInputStream.class); ++ AssetFileDescriptor fd = mock(AssetFileDescriptor.class); ++ doReturn(fileLength).when(fd).getLength(); ++ doReturn(fs).when(fd).createInputStream(); ++ ++ doReturn(fd).when(mContentProvider).openAssetFile(eq(uri), any(), any()); ++ ++ mCursor = ++ new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); ++ mCursor.addRow(new Object[]{fileName, fileLength}); ++ ++ doReturn(mCursor).when(mContentProvider).query(eq(uri), any(), any(), any(), any()); ++ ++ BluetoothOppSendFileInfo info = ++ BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); ++ ++ assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR); ++ } ++ ++ @Test ++ public void generateFileInfo_withContentUriForImplicitUser_returnsInfoWithCorrectLength() ++ throws Exception { ++ String type = "image/jpeg"; ++ Uri uri = buildContentUriWithEncodedAuthority(PROVIDER_NAME_MEDIA); ++ doReturn(type).when(mContentProvider).getType(any()); ++ ++ long fileLength = 1000; ++ String fileName = "pic.jpg"; ++ ++ FileInputStream fs = mock(FileInputStream.class); ++ AssetFileDescriptor fd = mock(AssetFileDescriptor.class); ++ doReturn(fileLength).when(fd).getLength(); ++ doReturn(fs).when(fd).createInputStream(); ++ ++ doReturn(fd).when(mContentProvider).openAssetFile(eq(uri), any(), any()); ++ ++ mCursor = ++ new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); ++ mCursor.addRow(new Object[]{fileName, fileLength}); ++ ++ doReturn(mCursor).when(mContentProvider).query(eq(uri), any(), any(), any(), any()); ++ ++ BluetoothOppSendFileInfo info = ++ BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); ++ ++ assertThat(info.mInputStream).isEqualTo(fs); ++ assertThat(info.mFileName).isEqualTo(fileName); ++ assertThat(info.mLength).isEqualTo(fileLength); ++ assertThat(info.mStatus).isEqualTo(0); ++ } ++ ++ @Test ++ public void generateFileInfo_withContentUriForSameUser_returnsInfoWithCorrectLength() ++ throws Exception { ++ String type = "image/jpeg"; ++ Uri uri = buildContentUriWithEncodedAuthority(myUserId() + "@" + PROVIDER_NAME_MEDIA); ++ doReturn(type).when(mContentProvider).getType(any()); ++ ++ long fileLength = 1000; ++ String fileName = "pic.jpg"; ++ ++ FileInputStream fs = mock(FileInputStream.class); ++ AssetFileDescriptor fd = mock(AssetFileDescriptor.class); ++ doReturn(fileLength).when(fd).getLength(); ++ doReturn(fs).when(fd).createInputStream(); ++ ++ doReturn(fd).when(mContentProvider).openAssetFile(eq(uri), any(), any()); ++ ++ mCursor = ++ new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); ++ mCursor.addRow(new Object[]{fileName, fileLength}); ++ ++ doReturn(mCursor).when(mContentProvider).query(eq(uri), any(), any(), any(), any()); ++ ++ BluetoothOppSendFileInfo info = ++ BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); ++ ++ assertThat(info.mInputStream).isEqualTo(fs); ++ assertThat(info.mFileName).isEqualTo(fileName); ++ assertThat(info.mLength).isEqualTo(fileLength); ++ assertThat(info.mStatus).isEqualTo(0); ++ } ++ ++ public static final class TestContext extends MockContext { ++ ++ private final TestContentResolver mContentResolver; ++ private final MockContentProvider mContentProvider; ++ ++ public TestContext() { ++ mContentProvider = spy(new MockContentProvider(this)); ++ mContentResolver = new TestContentResolver(this, mContentProvider); ++ } ++ ++ @Override ++ public TestContentResolver getContentResolver() { ++ return mContentResolver; ++ } ++ ++ public MockContentProvider getContentProvider() { ++ return mContentProvider; ++ } ++ ++ @Override ++ public String getOpPackageName() { ++ return "test.package"; ++ } ++ ++ @Override ++ public ApplicationInfo getApplicationInfo() { ++ ApplicationInfo applicationInfo = new ApplicationInfo(); ++ applicationInfo.targetSdkVersion = Build.VERSION.SDK_INT; ++ return applicationInfo; ++ } ++ } ++ ++ public static final class TestContentResolver extends ContentResolver { ++ ++ private final MockContentProvider mContentProvider; ++ ++ public TestContentResolver(Context context, MockContentProvider contentProvider) { ++ super(context, contentProvider); ++ mContentProvider = contentProvider; ++ } ++ ++ @Override ++ protected IContentProvider acquireProvider(Context c, String name) { ++ return mContentProvider.getIContentProvider(); ++ } ++ ++ @Override ++ public boolean releaseProvider(IContentProvider icp) { ++ return true; ++ } ++ ++ @Override ++ protected IContentProvider acquireUnstableProvider(Context c, String name) { ++ return mContentProvider.getIContentProvider(); ++ } ++ ++ @Override ++ public boolean releaseUnstableProvider(IContentProvider icp) { ++ return true; ++ } ++ ++ @Override ++ public void unstableProviderDied(IContentProvider icp) { ++ } ++ } ++} +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/packages/apps/Bluetooth/08_0008-Reset-permissions-for-not-bonded-device.bulletin.patch b/aosp_diff/preliminary/packages/apps/Bluetooth/08_0008-Reset-permissions-for-not-bonded-device.bulletin.patch new file mode 100644 index 0000000000..260f34ca40 --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Bluetooth/08_0008-Reset-permissions-for-not-bonded-device.bulletin.patch @@ -0,0 +1,51 @@ +From 6a42a14b811c345e3572646e230f686765377414 Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Thu, 14 Nov 2024 00:57:15 +0000 +Subject: [PATCH] Reset permissions for not bonded device + +According to the PBAP specification, +The PSE user shall have to confirm at least the first Phone Book Access +Profile connection from each new PCE. + +According to the MAP specification, +The MCE and MSE shall be bonded before setting up a Message Access Profile +connection. + +Let's remove the permissions when the device is unbonded. + +This is a backport of change ag/30386015 but requires minor changes to +logic. + +Flag: EXEMPT, security fix +Bug: 289375038 +Bug: 289811388 +Test: atest BluetoothInstrumentationTests +Ignore-AOSP-First: security fix +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bf3266f243a2e850a310d8116c7d8d0a9eb42dc7) +Merged-In: I78640e9d8bc8ea520f309811cd152106663c0e8a +Change-Id: I78640e9d8bc8ea520f309811cd152106663c0e8a +--- + src/com/android/bluetooth/btservice/BondStateMachine.java | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java +index bd2ddd03f..f46eaf1e6 100644 +--- a/src/com/android/bluetooth/btservice/BondStateMachine.java ++++ b/src/com/android/bluetooth/btservice/BondStateMachine.java +@@ -431,6 +431,13 @@ final class BondStateMachine extends StateMachine { + } + } + ++ if (newState == BluetoothDevice.BOND_NONE) { ++ // Remove the permissions for unbonded devices ++ mAdapterService.setMessageAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); ++ mAdapterService.setPhonebookAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); ++ mAdapterService.setSimAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); ++ } ++ + Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/packages/providers/DownloadProvider/0004-Ensure-ownership-validation-in-downloadprovider-insert-method.bulletin.patch b/aosp_diff/preliminary/packages/providers/DownloadProvider/0004-Ensure-ownership-validation-in-downloadprovider-insert-method.bulletin.patch new file mode 100644 index 0000000000..41f65d2fba --- /dev/null +++ b/aosp_diff/preliminary/packages/providers/DownloadProvider/0004-Ensure-ownership-validation-in-downloadprovider-insert-method.bulletin.patch @@ -0,0 +1,82 @@ +From a80a1fd6133b21117fec94d6a0a10e8cac3bf527 Mon Sep 17 00:00:00 2001 +From: Himanshu Arora +Date: Wed, 18 Dec 2024 12:40:54 +0000 +Subject: [PATCH] Ensure ownership validation in downloadprovider insert method + +Earlier, apps could access any file in Downloads by invoking the insert method in Downloadprovider. Now, it includes a check to ensure no existing record is present in Mediaprovider. If a record exists, the method validates the ownerPackageName before proceeding with the insert operation + +Bug: 304497167 +Test: manual +Flag: EXEMPT, fix only +(cherry picked from commit bf66a79ddcd8d93f502bf908621469893f513780) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1d5c91f62c1bffcf8b2243cf0634da17a96ac919) +Merged-In: I8b2d24974cdf3ae8a9e5680c22cf422bce396940 +Change-Id: I8b2d24974cdf3ae8a9e5680c22cf422bce396940 +--- + .../providers/downloads/DownloadProvider.java | 41 +++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java +index 1afa0901..70650d70 100644 +--- a/src/com/android/providers/downloads/DownloadProvider.java ++++ b/src/com/android/providers/downloads/DownloadProvider.java +@@ -84,6 +84,7 @@ import java.io.FileDescriptor; + import java.io.FileNotFoundException; + import java.io.IOException; + import java.io.PrintWriter; ++import java.util.Arrays; + import java.util.Iterator; + import java.util.Map; + +@@ -1103,8 +1104,48 @@ public final class DownloadProvider extends ContentProvider { + Helpers.checkDestinationFilePathRestrictions(file, getCallingPackage(), getContext(), + mAppOpsManager, getCallingAttributionTag(), isLegacyMode, + /* allowDownloadsDirOnly */ true); ++ // check whether record already exists in MP or getCallingPackage owns this file ++ checkWhetherCallingAppHasAccess(file.getPath(), Binder.getCallingUid()); + } + ++ private void checkWhetherCallingAppHasAccess(String filePath, int uid) { ++ try (ContentProviderClient client = getContext().getContentResolver() ++ .acquireContentProviderClient(MediaStore.AUTHORITY)) { ++ if (client == null) { ++ Log.w(Constants.TAG, "Failed to acquire ContentProviderClient for MediaStore"); ++ return; ++ } ++ ++ Uri filesUri = MediaStore.setIncludePending( ++ Helpers.getContentUriForPath(getContext(), filePath)); ++ ++ try (Cursor cursor = client.query(filesUri, ++ new String[]{MediaStore.Files.FileColumns._ID, ++ MediaStore.Files.FileColumns.OWNER_PACKAGE_NAME}, ++ MediaStore.Files.FileColumns.DATA + "=?", new String[]{filePath}, ++ null)) { ++ if (cursor != null && cursor.moveToFirst()) { ++ String fetchedOwnerPackageName = cursor.getString( ++ cursor.getColumnIndexOrThrow( ++ MediaStore.Files.FileColumns.OWNER_PACKAGE_NAME)); ++ String[] packageNames = getContext().getPackageManager().getPackagesForUid(uid); ++ ++ if (fetchedOwnerPackageName != null && packageNames != null) { ++ boolean isCallerAuthorized = Arrays.asList(packageNames) ++ .contains(fetchedOwnerPackageName); ++ if (!isCallerAuthorized) { ++ throw new SecurityException("Caller does not have access to this path"); ++ } ++ } ++ } ++ } ++ } catch (RemoteException e) { ++ Log.w(Constants.TAG, "Failed to query MediaStore: " + e.getMessage()); ++ } ++ } ++ ++ ++ + /** + * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to + * constraints in the rest of the code. Apps without that may still access this provider through +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/packages/services/Telecomm/16_0016-Resolve-cross-account-user-icon-validation-.bulletin.patch b/aosp_diff/preliminary/packages/services/Telecomm/16_0016-Resolve-cross-account-user-icon-validation-.bulletin.patch new file mode 100644 index 0000000000..27f0054e21 --- /dev/null +++ b/aosp_diff/preliminary/packages/services/Telecomm/16_0016-Resolve-cross-account-user-icon-validation-.bulletin.patch @@ -0,0 +1,85 @@ +From f4a61ef0d0ee320a81e2ff0827e95c8e2ebe0dc5 Mon Sep 17 00:00:00 2001 +From: Pranav Madapurmath +Date: Thu, 2 Jan 2025 15:04:45 -0800 +Subject: [PATCH] Resolve cross account user icon validation. + +Resolves a vulnerability found with the cross account user icon +validation in StatusHint and TelecomServiceImpl (when registering a +phone account). The reporter found that an uri formatted as `userId%` +isn't parsed properly with the existing reference to Uri.encodedUserInfo. + +Bug: 376461551 +Bug: 376259166 +Flag: EXEMPT bugfix +Test: atest TelecomServiceImplTest +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:573c6c89a571ed77cf326e41962e7ae4e75b5e88) +Merged-In: I7a5f64ae01eaf6a133ea04c51bd00dbe1653b74f +Change-Id: I7a5f64ae01eaf6a133ea04c51bd00dbe1653b74f +--- + .../server/telecom/TelecomServiceImpl.java | 17 ++++++++--------- + .../telecom/tests/TelecomServiceImplTest.java | 14 ++++++++++++++ + 2 files changed, 22 insertions(+), 9 deletions(-) + +diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java +index 864181c19..4f9c586d6 100644 +--- a/src/com/android/server/telecom/TelecomServiceImpl.java ++++ b/src/com/android/server/telecom/TelecomServiceImpl.java +@@ -54,6 +54,7 @@ import android.provider.Settings; + import android.telecom.Log; + import android.telecom.PhoneAccount; + import android.telecom.PhoneAccountHandle; ++import android.telecom.StatusHints; + import android.telecom.TelecomAnalytics; + import android.telecom.TelecomManager; + import android.telecom.VideoProfile; +@@ -2553,15 +2554,13 @@ public class TelecomServiceImpl { + // incompatible types. + if (icon != null && (icon.getType() == Icon.TYPE_URI + || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { +- String encodedUser = icon.getUri().getEncodedUserInfo(); +- // If there is no encoded user, the URI is calling into the calling user space +- if (encodedUser != null) { +- int userId = Integer.parseInt(encodedUser); +- if (userId != UserHandle.getUserId(Binder.getCallingUid())) { +- // If we are transcending the profile boundary, throw an error. +- throw new IllegalArgumentException("Attempting to register a phone account with" +- + " an image icon belonging to another user."); +- } ++ int callingUserId = UserHandle.getCallingUserId(); ++ int requestingUserId = StatusHints.getUserIdFromAuthority( ++ icon.getUri().getAuthority(), callingUserId); ++ if(callingUserId != requestingUserId) { ++ // If we are transcending the profile boundary, throw an error. ++ throw new IllegalArgumentException("Attempting to register a phone account with" ++ + " an image icon belonging to another user."); + } + } + } +diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +index e0e38a72a..fa68eba0d 100644 +--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java ++++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +@@ -606,6 +606,20 @@ public class TelecomServiceImplTest extends TelecomTestCase { + // This should fail; security exception will be thrown. + registerPhoneAccountTestHelper(phoneAccount, false); + ++ icon = Icon.createWithContentUri( ++ new Uri.Builder().scheme("content") ++ .encodedAuthority("10%40media") ++ .path("external/images/media/${mediaId.text}".trim()) ++ .build()); ++ phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build(); ++ // This should fail; security exception will be thrown ++ registerPhoneAccountTestHelper(phoneAccount, false); ++ ++ icon = Icon.createWithContentUri( Uri.parse("content://10%40play.ground")); ++ phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build(); ++ // This should fail; security exception will be thrown ++ registerPhoneAccountTestHelper(phoneAccount, false); ++ + icon = Icon.createWithContentUri("content://0@media/external/images/media/"); + phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build(); + // This should succeed. +-- +2.48.1.262.g85cc9f2d1e-goog + diff --git a/aosp_diff/preliminary/system/bt/68_0068-Prevent-use-after-free-of-HID-reports.patch b/aosp_diff/preliminary/system/bt/68_0068-Prevent-use-after-free-of-HID-reports.patch new file mode 100644 index 0000000000..cf776f01ef --- /dev/null +++ b/aosp_diff/preliminary/system/bt/68_0068-Prevent-use-after-free-of-HID-reports.patch @@ -0,0 +1,70 @@ +From 8069a35e45357f35639962b365f909c06402d1eb Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Tue, 11 Oct 2022 21:23:22 +0000 +Subject: [PATCH] Prevent use-after-free of HID reports + +BTA sends the the HID report pointer to BTIF and deallocates it immediately. +This is now prevented by providing a deep copy callback function for HID +reports when tranferring context from BTA to BTIF. + +This is a backport of change Icef7a7ed1185b4283ee4fe4f812ca154d8f1b825, +already merged on T for b/227620181. + +Bug: 228837201 +Test: Validated against researcher POC, ran BT unit tests, played audio +manually. +Tag: #security +Ignore-AOSP-First: Security +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:874c495c886cd8722625756dc5fd0634b16b4f42) +Merged-In: Ib837f395883de2369207f1b3b974d6bff02dcb19 +Change-Id: Ib837f395883de2369207f1b3b974d6bff02dcb19 +--- + btif/src/btif_hh.cc | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/btif/src/btif_hh.cc b/btif/src/btif_hh.cc +index fc7731e92..958dcc960 100644 +--- a/btif/src/btif_hh.cc ++++ b/btif/src/btif_hh.cc +@@ -1181,6 +1181,38 @@ static void btif_hh_hsdata_rpt_copy_cb(uint16_t event, char* p_dest, + } + } + ++/******************************************************************************* ++ * ++ * Function btif_hh_hsdata_rpt_copy_cb ++ * ++ * Description Deep copies the tBTA_HH_HSDATA structure ++ * ++ * Returns void ++ * ++ ******************************************************************************/ ++ ++static void btif_hh_hsdata_rpt_copy_cb(uint16_t event, char* p_dest, ++ char* p_src) { ++ tBTA_HH_HSDATA* p_dst_data = (tBTA_HH_HSDATA*)p_dest; ++ tBTA_HH_HSDATA* p_src_data = (tBTA_HH_HSDATA*)p_src; ++ BT_HDR* hdr; ++ ++ if (!p_src) { ++ BTIF_TRACE_ERROR("%s: Nothing to copy", __func__); ++ return; ++ } ++ ++ memcpy(p_dst_data, p_src_data, sizeof(tBTA_HH_HSDATA)); ++ ++ hdr = p_src_data->rsp_data.p_rpt_data; ++ if (hdr != NULL) { ++ uint8_t* p_data = ((uint8_t*)p_dst_data) + sizeof(tBTA_HH_HSDATA); ++ memcpy(p_data, hdr, BT_HDR_SIZE + hdr->offset + hdr->len); ++ ++ p_dst_data->rsp_data.p_rpt_data = (BT_HDR*)p_data; ++ } ++} ++ + /******************************************************************************* + * + * Function bte_hh_evt +-- +2.46.0 + diff --git a/aosp_diff/preliminary/system/bt/69_0069-Use-encrypted-link-for-avdtp-and-avctp-channels.bulletin.patch b/aosp_diff/preliminary/system/bt/69_0069-Use-encrypted-link-for-avdtp-and-avctp-channels.bulletin.patch new file mode 100644 index 0000000000..96ec274277 --- /dev/null +++ b/aosp_diff/preliminary/system/bt/69_0069-Use-encrypted-link-for-avdtp-and-avctp-channels.bulletin.patch @@ -0,0 +1,113 @@ +From b3f3ac55db667c8ab1fc6d4d3f84725e2bbb2241 Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Mon, 23 Sep 2024 21:39:40 +0000 +Subject: [PATCH] Use encrypted link for avdtp and avctp channels + +This is a backport of the AOSP changes for b/345258562. + +Test: mmm packages/modules/Bluetooth +Bug: 345258562 +Ignore-AOSP-First: security +Tag: #security +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a669cd8114916563d15a5bed7b203e9321e26ffb) +Merged-In: Ie5fe394f5c710690b8fc6e90cdd6c2b4cac9c7ca +Change-Id: Ie5fe394f5c710690b8fc6e90cdd6c2b4cac9c7ca +--- + stack/avct/avct_api.cc | 7 ++++--- + stack/avct/avct_bcb_act.cc | 5 +++-- + stack/avct/avct_lcb_act.cc | 4 ++-- + stack/avdt/avdt_ad.cc | 4 ++-- + stack/avdt/avdt_api.cc | 3 ++- + 5 files changed, 13 insertions(+), 10 deletions(-) + +diff --git a/stack/avct/avct_api.cc b/stack/avct/avct_api.cc +index da4c90427..281ba8887 100644 +--- a/stack/avct/avct_api.cc ++++ b/stack/avct/avct_api.cc +@@ -59,17 +59,18 @@ void AVCT_Register() { + /* initialize AVCTP data structures */ + memset(&avct_cb, 0, sizeof(tAVCT_CB)); + ++ uint16_t sec = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT; ++ + /* register PSM with L2CAP */ + L2CA_Register2(AVCT_PSM, avct_l2c_appl, true /* enable_snoop */, nullptr, +- kAvrcMtu, 0, BTA_SEC_AUTHENTICATE); ++ kAvrcMtu, 0, sec); + + /* Include the browsing channel which uses eFCR */ + tL2CAP_ERTM_INFO ertm_info; + ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE; + + L2CA_Register2(AVCT_BR_PSM, avct_l2c_br_appl, true /*enable_snoop*/, +- &ertm_info, kAvrcBrMtu, AVCT_MIN_BROWSE_MTU, +- BTA_SEC_AUTHENTICATE); ++ &ertm_info, kAvrcBrMtu, AVCT_MIN_BROWSE_MTU, sec); + + #if defined(AVCT_INITIAL_TRACE_LEVEL) + avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL; +diff --git a/stack/avct/avct_bcb_act.cc b/stack/avct/avct_bcb_act.cc +index ebb92e381..02d3c4277 100644 +--- a/stack/avct/avct_bcb_act.cc ++++ b/stack/avct/avct_bcb_act.cc +@@ -112,8 +112,9 @@ void avct_bcb_chnl_open(tAVCT_BCB* p_bcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) { + + /* call l2cap connect req */ + p_bcb->ch_state = AVCT_CH_CONN; +- p_bcb->ch_lcid = +- L2CA_ConnectReq2(AVCT_BR_PSM, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE); ++ p_bcb->ch_lcid = L2CA_ConnectReq2(AVCT_BR_PSM, p_lcb->peer_addr, ++ BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); ++ + if (p_bcb->ch_lcid == 0) { + /* if connect req failed, send ourselves close event */ + tAVCT_LCB_EVT avct_lcb_evt; +diff --git a/stack/avct/avct_lcb_act.cc b/stack/avct/avct_lcb_act.cc +index 1b4197861..e4e042e48 100644 +--- a/stack/avct/avct_lcb_act.cc ++++ b/stack/avct/avct_lcb_act.cc +@@ -188,8 +188,8 @@ void avct_lcb_chnl_open(tAVCT_LCB* p_lcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) { + uint16_t result = AVCT_RESULT_FAIL; + + p_lcb->ch_state = AVCT_CH_CONN; +- p_lcb->ch_lcid = +- L2CA_ConnectReq2(AVCT_PSM, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE); ++ p_lcb->ch_lcid = L2CA_ConnectReq2(AVCT_PSM, p_lcb->peer_addr, ++ BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + if (p_lcb->ch_lcid == 0) { + /* if connect req failed, send ourselves close event */ + tAVCT_LCB_EVT avct_lcb_evt; +diff --git a/stack/avdt/avdt_ad.cc b/stack/avdt/avdt_ad.cc +index ff102548f..6a077ae20 100644 +--- a/stack/avdt/avdt_ad.cc ++++ b/stack/avdt/avdt_ad.cc +@@ -546,8 +546,8 @@ void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb, + p_tbl->state = AVDT_AD_ST_CONN; + + /* call l2cap connect req */ +- lcid = +- L2CA_ConnectReq2(AVDT_PSM, p_ccb->peer_addr, BTM_SEC_OUT_AUTHENTICATE); ++ lcid = L2CA_ConnectReq2(AVDT_PSM, p_ccb->peer_addr, ++ BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); + if (lcid != 0) { + /* if connect req ok, store tcid in lcid table */ + avdtp_cb.ad.lcid_tbl[lcid] = avdt_ad_tc_tbl_to_idx(p_tbl); +diff --git a/stack/avdt/avdt_api.cc b/stack/avdt/avdt_api.cc +index 9bc6f5a16..02912ad02 100644 +--- a/stack/avdt/avdt_api.cc ++++ b/stack/avdt/avdt_api.cc +@@ -93,9 +93,10 @@ void avdt_scb_transport_channel_timer_timeout(void* data) { + * + ******************************************************************************/ + void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) { ++ uint16_t sec = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT; + /* register PSM with L2CAP */ + L2CA_Register2(AVDT_PSM, avdt_l2c_appl, true /* enable_snoop */, nullptr, +- kAvdtpMtu, 0, BTA_SEC_AUTHENTICATE); ++ kAvdtpMtu, 0, sec); + + /* initialize AVDTP data structures */ + avdt_scb_init(); +-- +2.48.1.262.g85cc9f2d1e-goog +