{
 "month": "2026-06",
 "generated": "2026-10-14T01:30+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 51,
    "certain": 51,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-64505",
     "osv_id": "ASB-A-463980379",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "libpng png_set_quantize now allocates and identity-initialises the quantize_index lookup table to PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so out-of-range palette indices from a malformed PNG no longer read past the table in png_do_quantize; a companion commit frees the previous quantize_index before reallocating and makes quantize_sort a local variable to stop leaks on repeated calls.",
      "confidence": "medium",
      "backport_notes": [
       "On trees that lack the png_free of quantize_index, apply da0c851e9072 before 411d9be12a3e. On trees that already have it (the Android 15/16 base), only the second change is needed, and the allocation line context differs: (png_alloc_size_t)num_palette instead of the (png_uint_32)num_palette * sizeof form.",
       "When backporting, keep the quantize_sort member in pngstruct.h, as Google did on Android 14, to avoid an ABI change."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "411d9be12a3e8377c09fcb57c90ccfa8ea45fc28",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "da0c851e90725ddadbea9fdcf7c006899be52cf9",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-64720",
     "osv_id": "ASB-A-463995203",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In libpng pngrtran.c png_init_read_transformations, the palette tRNS compositing loop now branches on PNG_FLAG_OPTIMIZE_ALPHA. When the flag is set, each palette component is only premultiplied by its alpha (with gamma correction) and is not composited against the background. Palette values then keep the premultiplied invariant component <= alpha, which png_image_read_composite relies on for its png_sRGB_base[]/png_sRGB_delta[] table lookups.",
      "confidence": "high",
      "backport_notes": [
       "A single-commit, single-file, single-hunk change with no dependencies. The only difference between branches is a 4-line offset (1697 vs 1701) with identical context lines, so the hunk should apply with an offset to any pngrtran.c that contains the original png_composite palette block."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "ba5981bebfba58075fb61afdca3d1e11f2f80300",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-65018",
     "osv_id": "ASB-A-463998243",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Fixes a heap buffer overflow in libpng's simplified read API (png_image_finish_read / png_image_read_direct) when a 16-bit PNG is read into an 8-bit output buffer: first by rejecting IHDR/output bit-depth mismatches, then (rearchitected) by routing interlaced 16-to-8 conversions through an intermediate local_row buffer in a new png_image_read_direct_scaled routine.",
      "confidence": "high",
      "backport_notes": [
       "Order matters: 5e46c0292d5c's diff base (d804c90e3) already contains the rejection block from 566e14540832 and removes it, so apply the rejection commit first or drop that hunk; if a branch (like 15/16 as shown) stops at the rejection commit, it is protected but with the broader API restriction.",
       "The rearchitected commit uses png_image_read_control fields local_row, first_row and row_bytes and png_safe_execute; local_row and row_bytes are visible in existing code, first_row is not shown; the diff touches only pngread.c."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "566e145408329f09a74702419a754db48ae91b4f",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "5e46c0292d5cabd5e82b2f950017e7c065e898c6",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2026-0039",
     "osv_id": "ASB-A-470966846",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow checks call a library-private runtime that throws std::runtime_error, replacing the ubsan-minimal runtime that hard-aborts the process when an overflow is detected.",
      "confidence": "high",
      "backport_notes": [
       "The Android.bp hunk is byte-identical across 14 to 17 (same pre/post blob ids), so it should apply cleanly wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults with rtti: true and -fexceptions. Only OWNERS needs manual resolution. Per the inline comment, the fix relies on sanitize.never combined with explicit -fsanitize cflags, plus the handlers being hidden-visibility symbols inside the same library."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0040",
     "osv_id": "ASB-A-467994860",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that signed/unsigned integer overflow detected by UBSan throws a C++ std::runtime_error (through a new in-tree runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of using the ubsan-minimal runtime, which hard-aborts.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. It depends on libdng_sdk-defaults already having rtti: true and cppflags -fexceptions, which are present at android-security-14.0.0_r29. The Android.bp hunk has the same blob indexes (1978b39..3d95a7b) on all four majors. Only the OWNERS hunk varies, and it has no functional effect.",
       "For forks: the build relies on Soong honoring sanitize.never together with raw -fsanitize cflags and -fno-sanitize-link-runtime. If a fork builds libdng_sdk with -fno-sanitize-recover, clang emits calls to __ubsan_handle_*_overflow_abort, which this runtime does not define (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0041",
     "osv_id": "ASB-A-467994310",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk without the ubsan-minimal runtime and links a project-local replacement (ubsan-replacement/ubsan_throwing_runtime.cpp) whose integer-overflow handlers throw std::runtime_error, so a UBSan-detected signed/unsigned integer overflow in libdng_sdk becomes a C++ exception instead of a hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "The Android.bp hunk applies against the same base blob (index 1978b39) on all four branches, so only OWNERS should conflict. The change relies on libdng_sdk-defaults already providing rtti: true and cppflags -fexceptions."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0042",
     "osv_id": "ASB-A-456471487",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error instead of going to the ubsan minimal runtime, which does a hard abort.",
      "confidence": "high",
      "backport_notes": [
       "The Android.bp hunk is the same on 14, 15, 16 and 17 (same blob ids 1978b39..3d95a7b), so the per-branch differences are only in OWNERS, which has no functional effect. The throw can only unwind if libdng_sdk-defaults already has `rtti: true` and `cppflags: [\"-fexceptions\"]`. Both are at lines 141-142 in the Android 14 context.",
       "For Android 14, the '3/5 lines present' metric appears to be a line-matching artifact: the android-security-14.0.0_r29 context shows all added Android.bp lines and the full ubsan_throwing_runtime.cpp."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0043",
     "osv_id": "ASB-A-453649377",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk with Soong sanitizer integration disabled (sanitize.never), adds explicit -fsanitize=signed-integer-overflow,unsigned-integer-overflow plus -fno-sanitize-link-runtime, and links a new in-tree UBSan handler file (ubsan-replacement/ubsan_throwing_runtime.cpp). Its overflow handlers throw std::runtime_error instead of the ubsan-minimal hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. The Android.bp hunk applies to the same base index 1978b39 on 14, 15, 16 and 17. The library must have C++ exceptions enabled; context shows rtti: true and -fexceptions in libdng_sdk-defaults on 14. Apply the sanitize.never setting, the -fsanitize/-fno-sanitize-link-runtime cflags and the new source file together.",
       "The fix depends on Soong. A fork with a different build configuration must make sure the ubsan minimal runtime is not linked and that the in-tree handlers are used."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0044",
     "osv_id": "ASB-A-449728942",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits throw a C++ exception (handlers in the new file ubsan-replacement/ubsan_throwing_runtime.cpp) instead of going to the ubsan minimal runtime, which aborts the process.",
      "confidence": "high",
      "backport_notes": [
       "The change consists only of build configuration and one new source file, and it is identical across 14 through 17 (Android.bp index 1978b39..3d95a7b in every branch). It should apply cleanly wherever libdng_sdk's cc_library still has cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults sets -fexceptions. The fix depends on the toolchain emitting the non-minimal handler names (__ubsan_handle_*_overflow). With -fsanitize-minimal-runtime, clang calls __ubsan_handle_*_minimal symbols instead (general platform knowledge).",
       "The per-major '3/5 lines present' figure is a heuristic on the patched region. The code context at android-security-14.0.0_r29 shows the complete fix present."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0051",
     "osv_id": "ASB-A-461790658",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that the integer-overflow UBSan checks call a library-private runtime that throws std::runtime_error instead of the ubsan-minimal runtime that aborts the process, turning overflow detections on malformed DNG input into recoverable C++ exceptions.",
      "confidence": "high",
      "backport_notes": [
       "The change is build configuration plus one new source file and should apply wherever libdng_sdk still has the single line cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults already sets rtti: true and cppflags -fexceptions (both present in the Android 14 context). It relies on Soong honouring sanitize.never as described in the commit's comment; verify at link time that no UBSan runtime is still pulled in on customized branches."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0052",
     "osv_id": "ASB-A-467352655",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error from a handler runtime inside the project, instead of aborting the process through the default ubsan-minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. The throwing handlers rely on C++ exceptions being enabled for the library (cppflags -fexceptions at line 141-142 on 14). On branches where the libdng_sdk module already has its own srcs list (as on 17), add ubsan-replacement/ubsan_throwing_runtime.cpp to that list instead of adding a second srcs property.",
       "No 'Fix in fork' comparison was provided in the material, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0080",
     "osv_id": "ASB-A-462431872",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime in ubsan-replacement/ubsan_throwing_runtime.cpp) instead of aborting the process through the ubsan-minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Apply all four pieces together: sanitize.never, the explicit -fsanitize cflags, -fno-sanitize-link-runtime, and the new ubsan_throwing_runtime.cpp source. The module must keep -fexceptions (from libdng_sdk-defaults) so the throw is usable."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0097",
     "osv_id": "ASB-A-446114623",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In the LE Secure Connections Passkey Display and Key Notification association models, the fix makes the legacy SMP layer wait for both the local user's approval (SMP_SecurityGrant) and the remote device's passkey confirmation before emitting SMP_KEY_READY_EVT. It treats a user rejection as an authentication failure, cancels LE pairing on authentication timeout, and sets the default LE pairing flags (is_le_only/is_le_nc) in btif for passkey notification.",
      "confidence": "medium",
      "backport_notes": [
       "This is one combined cherry-pick of three main CLs (ag/35861798, ag/35914708, ag/35861802), and all five files should land together: smp_keys.cc and smp_api.cc depend on the new smp_int.h field. The commit lists a flag (passkey_entry_pairing_approval), but the backported code has no flag check.",
       "Adapt per branch: btm_cb vs btm_sec_cb for pairing_flags, BTM_TRACE_*/SMP_TRACE_* vs log::* macros, and btif_dm_ble_key_notif_evt vs btif_dm_ble_passkey_notif_evt (Android 16)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "725df035dfe256fc8a205b8a96a7558751766439",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-21352",
     "osv_id": "ASB-A-483693973",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to 2471. The update adds integer-overflow checks around JXL decode/encode buffer sizing and allocations, fixes an off-by-one table index in dng_opcode_MapTable::ReplicateLastEntry, and guards several divisions by zero that occur when blackLevel is 65535.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. Its base is SDK 1.7.1 build 2410 (dng_jxl.cpp with jxl_memory_block and the chunk reader classes; dng_safe_arithmetic.h with the __ANDROID__ SMULL block). The new 3-arg SafeInt64Mult declaration and definition must be carried with the dng_jxl.cpp Decode hunk, which calls it.",
       "On Android 14, android-security-14.0.0_r29 shows build 2502 in dng_flags.h. When verifying presence, check the code hunks rather than the build string."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "72d44e39d499a5b9a7d749fe892534a4ff2341e8",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-21353",
     "osv_id": "ASB-A-483697751",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Updates external/dng_sdk from build 2410 to 2471, adding overflow-checked arithmetic and range checks to the JXL decode/encode pixel-buffer geometry and allocation sizes in dng_jxl.cpp, an overflow check in dng_ref_counted_block::Allocate, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and division-by-zero guards for blackLevel == 65535 in the GainMap, MapTable, Vignette and BaselineMapPoly paths.",
      "confidence": "medium",
      "backport_notes": [
       "This is a whole-SDK drop (2410 -> 2471), applied identically on all four branches; the dng_jxl.cpp Decode change depends on the new 3-arg SafeInt64Mult in dng_safe_arithmetic.h/.cpp, so take the safe-arithmetic hunks together with dng_jxl.cpp."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "72d44e39d499a5b9a7d749fe892534a4ff2341e8",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-26418",
     "osv_id": "ASB-A-351830787",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a MANAGE_USERS-or-INTERACT_ACROSS_USERS permission check to CarDevicePolicyService.setUserDisclaimerAcknowledged and a matching @RequiresPermission annotation on CarDevicePolicyManager. Callers without either permission can no longer have the car service acknowledge the new-user disclaimer.",
      "confidence": "high",
      "backport_notes": [
       "One self-contained commit: add the enforcePermission() call and the new method to CarDevicePolicyService.setUserDisclaimerAcknowledged. Make sure in-tree callers (for example Car Settings NewUserDisclaimerActivity, per index search) hold MANAGE_USERS or INTERACT_ACROSS_USERS, or acknowledging the disclaimer will start failing with SecurityException."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Car",
       "sha": "6afaae7b4be46a46a7b27bee6a49e3860caad6c9",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Car"
     ]
    },
    {
     "cve": "CVE-2025-32348",
     "osv_id": "ASB-A-406880479",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops treating a process whose only visible activity is in pinned (PIP) windowing mode as \"has a visible window / foreground task\" for background activity launch (BAL) exemptions. It does this by adding VisibleActivityProcessTracker.hasVisibleNotPinnedActivity and an inPinnedWindow parameter to BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed.",
      "confidence": "medium",
      "backport_notes": [
       "The change has an app compatibility history: reverted on Android 14 at r24/r25, then present again at r29 with a test file that differs from the original. Align a backport with the android-security-14.0.0_r29 code rather than the original commit, and expect PIP apps that start activities to be affected.",
       "Apply as one unit: the VisibleActivityProcessTracker change removes null-predicate support in match(), and the BackgroundLaunchProcessController signature change must be mirrored in WindowProcessController.areBackgroundActivityStartsAllowed and in any tests that call it (on 15/16, BackgroundLaunchProcessControllerTests pass mInPinnedWindoMode)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r22",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4ec3e4cea49df48012810534a18208652e895d84",
       "tag": "android-security-14.0.0_r22",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "58a95019e2451e13b02c1f55fb7dc3f9ec16da54",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "4e7b14350ecedb73b3ef6f0eb89a41b8cc51ce97",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "75ca85729ad30b71c98ee30dedcf7a24f5274a65",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 4,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 272,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48570",
     "osv_id": "ASB-A-376048041",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix adds a 3-second check to PipTaskOrganizer#onTaskAppeared in WM Shell: if no PiP-entering transition state change follows a pinned task appearing (the commit attributes this to a SystemUI reboot), tasks whose top activity matches that component are removed.",
      "confidence": "medium",
      "backport_notes": [
       "Backport the reland c0f88bec3bf0 (with the info/topActivity null checks), not the original 670f2df6bf5e. The original was reverted (b/458676344), and the reland addresses an NPE (b/459832318). If the original is already applied, apply the revert first and then the reland."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-opus-5-5",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "670f2df6bf5e311ecf75d38dccba54b784defa26",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "94929458bc265ddf2962b35d5cda67751ee46fb6",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "c0f88bec3bf04db6a1ba3fad276dcde898de0f5e",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48595",
     "osv_id": "ASB-A-430889718",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Google fixed CVE-2025-48595 by moving the bundled SQLite library to a patched upstream point release instead of applying a single targeted patch. On Android 14 that meant 3.42.0 to 3.42.1, after a first 3.39.6 update was reverted. On Android 15 and 16 it meant RELEASE_PACKAGE_LIBSQLITE3 flag changes (Android 15: trunk_staging flag file deleted; Android 16: ap3a moved to 3.44.5).",
      "confidence": "medium",
      "backport_notes": [
       "Android 14: the effective commit is 3b55568966b4. It applies on top of a 3.42.0 baseline; per the revert message, the branch must carry the mandatory aosp/2622587 sqlite upgrade (3.40.0). Keep dist/Android.patch rebased so the Android-specific edits stay applied.",
       "Android 15/16: the fix is a RELEASE_PACKAGE_LIBSQLITE3 flag change (Android 16 ap3a: 3440500; Android 15: trunk_staging flag file deleted). It depends on the matching sqlite package being present in the tree."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/sqlite",
       "sha": "2abdb383b72c869707fdd0a7a85dbb05b6d781c0",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "3b55568966b40a27188e3beb43d1e22a99f220c3",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "ba40d842b44fc0f7b33c9771c921ae92e90cdc93",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "5cb9c5a64a68d7561f048aa997a7a8de2c20d674",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "96f51a7f6bad7f3345775eaa0e3e929c38d5b29a",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 5,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/external/sqlite"
     ]
    },
    {
     "cve": "CVE-2025-48612",
     "osv_id": "ASB-A-429417453",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In Settings' NFC default payment app selector, the picker key used to be a \"flattenedComponentName userId\" string that was parsed back on selection. The fix replaces it with an opaque hash key looked up in a map of PaymentAppInfo entries, so the component and user passed to setDefaultPaymentApp always come from an enumerated candidate instead of a string parse.",
      "confidence": "high",
      "backport_notes": [
       "The hunks are the same on 14 through 17 and self-contained (two files, java.util imports only). The 15, 16 and 17 commits apply to DefaultPaymentSettings.java blob ddac08beb00; the Android 14 reland applies to blob d75844bb0ff. PaymentBackend.java is 021d673e152 everywhere. Check the surrounding context in your tree.",
       "On Android 14 the first landing was reverted for a regression and then relanded with the same hunks. Take the final Revert^2 state (4b903dacbc6c). The material does not describe the regression."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "c90fee1f1ea489b9734ff3540a100b9d1ea32c68",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "4b903dacbc6cbf2001f9b5456fcde7b32ad42f35",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "da3689792a459ab15d0b0940ebf9017024b541e7",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48615",
     "osv_id": "ASB-A-433250316",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 1024-character cap on media button receiver component names in MediaButtonReceiverHolder, first for components resolved from a PendingIntent (skipped with a warning) and then for ComponentName broadcast receivers (rejected with IllegalArgumentException).",
      "confidence": "medium",
      "backport_notes": [
       "Apply the two commits in order: e20813dba23a depends on a1bbb371980e for MAX_COMPONENT_NAME_LENGTH and modifies lines that a1bbb371980e introduced. Applying only the first leaves create(int, ComponentName) unchecked.",
       "Both diffs are confined to MediaButtonReceiverHolder.java and add no new imports (TextUtils and Log were already imported), so they should apply cleanly wherever that class has the same getComponentName loop and create(int, ComponentName) overload."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a1bbb371980e18faad420c77c442c6fa71c1268d",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "e20813dba23aeaeca5708028c228ce1546101e3b",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48616",
     "osv_id": "ASB-A-438973280",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "KeyguardViewMediator now queries KeyguardUpdateMonitor.isUserInLockdown instead of LockPatternUtils.isUserInLockdown at the three places that decide whether lockdown forces the keyguard to show or prevents it from being hidden.",
      "confidence": "high",
      "backport_notes": [
       "Single commit: replace mLockPatternUtils.isUserInLockdown with mUpdateMonitor.isUserInLockdown at the three sites in KeyguardViewMediator and update the two unit tests' mocks; on branches using mSelectedUserInteractor.getSelectedUserId() follow the 15/16 variant."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r24",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "991998d5464f23c7a7f1f8e6ccf91d7cd8855821",
       "tag": "android-security-14.0.0_r24",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 182,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48648",
     "osv_id": "ASB-A-396667508",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "NotificationManagerService stops relying on PackageManagerInternal.isSameApp (which, per the commit message, carries an SDK sandbox exception) for its caller-owns-package enforcement and instead compares the caller uid directly against the package uid, and additionally rejects SDK sandbox uids in areNotificationsEnabledForPackage and checks system/SystemUI/shell identity before canNotifyAsPackage in getConversationNotificationChannel.",
      "confidence": "medium",
      "backport_notes": [
       "The production change is small and self-contained (three hunks in NotificationManagerService.java); the bulk of the commit is test refactoring that removes the blanket isSameApp stub. When backporting, grep NotificationManagerService.java for any remaining direct `mPackageManagerInternal.isSameApp` callers (the 15/16 commits converted getEffectsSuppressor and the wellbeing check to isCallerSameApp) and route them through the patched helper if present.",
       "The updated NotificationManagerServiceTest uses com.android.compatibility.common.util.SystemUtil; the Android 17 commit adds `compatibility-device-util-axt-minus-dexmaker` to services/tests/uiservicestests/Android.bp, so a tree without that static_lib needs the same Android.bp change for the tests to compile."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "699e4677367eebaf75ec72e5bdcfb6e8c88cc58d",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48649",
     "osv_id": "ASB-A-339109116",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Threads a restorePregrantedPermissions flag from ActivityManager through ActivityManagerService, PackageManagerService and PermissionManagerServiceImpl so that an app clearing its own data (public ActivityManager.clearApplicationUserData()) no longer gets default- or role-granted runtime permissions re-granted by the system, while callers holding CLEAR_APP_USER_DATA keep the old restoring behaviour.",
      "confidence": "high",
      "backport_notes": [
       "If the target 14 tree already has the isRestore overload of ActivityManagerService.clearApplicationUserData and LocalService.clearApplicationUserData (present at android-14.0.0_r75 per index search), use the Android 15 AMS hunks (LocalService passes restorePregrantedPermissions=true) instead of the 14 security-branch hunks; also update every implementor of PermissionManagerServiceInterface present in the tree (the 15/16 diffs additionally touch PermissionManagerServiceTestingShim and PermissionManagerServiceTracingDecorator) and, if AppIdPermissionPolicy.kt.resetRuntimePermissions is a real implementation rather than a TODO, port the Kotlin hunk from 15.",
       "Since the IPackageManager and IActivityManager AIDL signatures change, any out-of-tree callers of IPackageManager.clearApplicationUserData(pkg, observer, userId) must be updated to pass the boolean."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r25",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c1f49d2c2cac52dca44dbe6fa5c54982e6510909",
       "tag": "android-security-14.0.0_r25",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0036",
     "osv_id": "ASB-A-405392600",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a shell-side check in StageCoordinator.startAnimation. When split is not active, it detects a split-enter transition whose opening tasks belong to only one split stage and starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea. Previously such tasks were left to the default transition handler, which showed them outside of split.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. StageCoordinator needs a stored Transitions reference (add mTransitions in every constructor if absent), and StageTaskListener must be public for the new common/split helper. Pick the branch-appropriate split-active predicate (mMainStage.isActive() on 14/15 vs isSplitActive() on 16) and ProtoLog import (com.android.internal.protolog.common.ProtoLog on 14/15 vs com.android.internal.protolog.ProtoLog on 16). Use TransitionUtil.isOpeningMode where it exists instead of the local helper."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3ef64c4444d5413ddf810c4d536345d55f3788e5",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0045",
     "osv_id": "ASB-A-380091558",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In the Bluetooth stack's btm_sec_is_upgrade_possible (system/stack/btm/btm_sec.cc), the fix makes the function return true when a link key is known, the service requires authentication in the relevant direction and the peer's bond is temporary; the code comment says this is meant to trigger pairing.",
      "confidence": "medium",
      "backport_notes": [
       "The field layout differs by release. 14 (and the 17 diff as shown) use p_dev_rec->security_required and p_dev_rec->is_bond_type_temporary(); 15 and 16 use p_dev_rec->sec_rec.security_required and p_dev_rec->sec_rec.is_bond_type_temporary(). If a tree already carries the flagged upgrade_temp_bonding_on_auth_req variant, the change needed is to drop the flag gate and replace BTM_SEC_OUT_AUTHENTICATE with the OUT/IN bit selected by is_originator.",
       "The bta_jv_act.cc RFC_MUX BTM_SetSecurityLevel removal is part of the 15 and 16 fixes but not of the 14 backport. Check whether the target tree has that block in bta_jv_rfcomm_connect before deciding whether to carry it."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "2e2a7bf09a3f4aaf779e1862bf75862a97b86287",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0046",
     "osv_id": "ASB-A-443272513",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix makes the Letterbox input window (InputInterceptor in Letterbox.java) report the surrounded app's pid/uid as its owner instead of system_server's, so the input layer's obscured-touch (tapjacking) checks no longer treat the letterbox as a trusted system window.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit, two-line functional change: in the Letterbox.InputInterceptor constructor, replace WindowManagerService.MY_PID/MY_UID with win.mSession.mPid/win.mSession.mUid. On Android 16 the surrounding layoutParamsType/inputConfig lines differ (flagged SPY vs SLIPPERY), but the change touches the same two assignments.",
       "The three Cherrypick-From references in the commit message point to internal changes. The AOSP commit contains only the Letterbox.java hunk, so the material shows no additional dependency."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8284ae9c703a191a2e23d997ed4b446c87f8aa20",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0048",
     "osv_id": "ASB-A-463364410",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowState.hide() now hides a force-hidden window (non-system overlay, suspended owner, app-op hidden, or permanently hidden) immediately when that window is already playing an exit animation, and cancels any in-progress animation, instead of letting the hide wait for the previous exit animation to finish.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained single-hunk change in WindowState.hide(), inserted after the `if (!mToken.okToAnimate()) { doAnimation = false; }` check. The four policy flags are already used in show() on Android 14. The diff is identical on 14 through 17 (only line offsets differ). Confirm that mAnimatingExit, the no-argument isAnimating() and cancelAnimation() are available in your fork; the provided context does not show them."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7c5437456c5d38fb60863d4ed370cb5b5141adb8",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0055",
     "osv_id": "ASB-A-460779368",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstallerService.createSessionInternal now checks the caller-supplied SessionParams.volumeUuid with StorageManager.convert() when INSTALL_FORCE_VOLUME_UUID is set. Invalid values are rejected with an IllegalArgumentException when the session is created, instead of passing into later install handling.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk in createSessionInternal. The diff adds no import, so StorageManager must already be imported in PackageInstallerService.java on the target branch; verify this on your tree. Line numbers differ per branch, so anchor on the `params.installFlags |= PackageManager.INSTALL_INTERNAL;` line inside the INSTALL_FORCE_VOLUME_UUID branch."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "06fb4af542b78649b626fa04febbef718396a8d3",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0056",
     "osv_id": "ASB-A-462431486",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds two validations of ResStringPool_header.styleCount in ResStringPool::setTo (libs/androidfw/ResourceTypes.cpp) so that the declared style offset array cannot overflow its size computation or extend past stringsStart, returning BAD_TYPE for such chunks.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The ResourceTypes.cpp hunk is identical on 14 through 17 and anchors on the existing stylesStart BAD_TYPE check just before the mStylePoolSize assignment in ResStringPool::setTo. The new test uses androidfw/StringPool.h, androidfw/Util.h and libgmock (libgmock is already in libandroidfw_tests static_libs). If a target branch lacks these, the ResourceTypes.cpp change can be taken alone."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f1c0bc9dd174d0076ff62691e9ca08d509c8a157",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0059",
     "osv_id": "ASB-A-447536200",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "RCE"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a remaining-buffer-length parameter and check to sdpu_build_attrib_seq, and adds buffer accounting to process_service_attr_rsp, so the SDP client can no longer write an attribute ID sequence past the end of the SDP_DATA_BUF_SIZE request buffer.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with no dependency. The parts that matter are the sdpu_build_attrib_seq signature and body change, the sdpint.h prototype, the two call sites, and the new bytes_left/base_bytes block in process_service_attr_rsp. The loop-variable refactors across sdp_utils.cc can be dropped. Because the prototype changes, any out-of-tree callers (tests, fuzzers) must be updated to pass a bytes_left reference."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "ce4be454822b2273334197fb1e8e73582901d0f7",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0060",
     "osv_id": "ASB-A-459639258",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Gates the Developer Options \"Enable ANGLE\" switch in Settings behind a debug system property (debug.graphics.angle.developeroption.enable), keeps the switch usable when ANGLE is already active so it can be turned off, relabels the option as experimental, and rewrites the summary and reboot-dialog warnings (back up data; a failed restart needs a factory reset).",
      "confidence": "medium",
      "backport_notes": [
       "Apply in order: 2097b24534af (adds getBoolean to GraphicsDriverSystemPropertiesWrapper, the property constant and the updateState guard), then 468f22c59b52 (rewrites the guard condition). Then apply the strings: 72801bd6f244's pre-image includes an intermediate backup and factory-reset wording that is not in 2097b24534af, so expect a conflict in strings.xml. Any custom or test implementation of GraphicsDriverSystemPropertiesWrapper must implement getBoolean; Robolectric tests need ShadowSystemProperties.override(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION, \"true\") as in the patch.",
       "The strings.xml hunks sit at different line offsets per branch (about 10511 to 10513 on 14, 12304 on 16), and translated values-*/strings.xml files are updated by separate translation-import commits. Backports without the translation imports fall back to the English default strings (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "2097b24534af75e9a0c485ba3b7bf2c9a30cf28f",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "468f22c59b52dad9b4e5411668c437fb3ac74b30",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "72801bd6f244d68376ac8e72353a8360d6c71767",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "899f6046f7cbc8025e12513bdaab447c3d4df353",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "8c3fddbc357e3a12f07f103eccad30f524663ad9",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 6,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0061",
     "osv_id": "ASB-A-452010556",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowState now applies the server-side visibility policy (hide/show) to the WindowState's own mSurfaceControl through the pending transaction, in addition to the existing mWinAnimator handling, so policy visibility no longer depends only on a surface the client can access.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit; the three code hunks are identical across 14 to 17 apart from line offsets. Surrounding context differs only in whether mWinAnimator.hide() takes getGlobalTransaction() (14, 17) or getPendingTransaction() (15, 16); the new lines always use getPendingTransaction().",
       "The test change adds verify/clearInvocations on mTransaction in testIsOnScreen_hiddenByPolicy; the test context differs on 15/16 (ANIMATION_TYPE_TOKEN_TRANSFORM)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b53981d87f80c719973c110722266fc8f94551e5",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0067",
     "osv_id": "ASB-A-470967228",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In external/dng_sdk, libdng_sdk stops using the Soong-managed ubsan_minimal runtime and instead enables the signed/unsigned integer-overflow sanitizers via cflags with -fno-sanitize-link-runtime, plus a bundled ubsan_throwing_runtime.cpp whose __ubsan_handle_*_overflow handlers throw std::runtime_error, so detected overflows become C++ exceptions rather than a hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit. The runtime file is identical on every branch; only the Android.bp hunk placement varies (14/15/16 replace the one-line cflags in the libdng_sdk module; 17 has a separate srcs hunk). The change relies on the module already having cppflags -fexceptions and rtti: true, which libdng_sdk-defaults provides."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0070",
     "osv_id": "ASB-A-438186009",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.setApplicationHidden() now refuses (returns false) to hide packages listed in a new framework resource array application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings), because per the commit message hiding these critical apps in the work profile may cause failure to boot.",
      "confidence": "high",
      "backport_notes": [
       "Apply all three files together. The Java change references R.array.application_hidden_policy_exempt_apps, which needs both the array declaration in policy_exempt_apps.xml and the java-symbol entry in symbols.xml (general platform knowledge). The setApplicationHidden hunk anchors on the existing listPolicyExemptAppsUnchecked(mContext) guard, and the same hunks appear on the 14, 15, 16 and 17 branches."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "987b313aefa04f4c934b86d8aa83f6100368d0fa",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0074",
     "osv_id": "ASB-A-468061774",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds process-level and thread-level default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and registers a Launcher (Quickstep) process default listener that rejects non-allowlisted image mime types and downscales images whose estimated decoded size exceeds a configured memory budget (300 MB).",
      "confidence": "medium",
      "backport_notes": [
       "Two-repo dependency: the Launcher3 commit calls the @hide ImageDecoder.setDefaultProcessListener, which is added by the frameworks/base commit (Change-Id I694d45deb8755aab5e994c12d2627fe08b5ce78c). Without that commit, Launcher3 references a method that does not exist, so apply frameworks/base first or in the same build. Both commits share Bug 468061774.",
       "The Launcher3 change adds a Kotlin source and new resources in quickstep/. The shown code contains no runtime flag check despite the Flag: line."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "bf47585e86180d6357cba68430e15ee5bb01efb1",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "c466fd8d9215f64dade6323902adf6cdc1595b30",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2026-0075",
     "osv_id": "ASB-A-465133716",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "When the caller lacks the provider's READ_PERMISSION (query; READ_CONTACTS according to the commit message) or WRITE_PERMISSION (insert/update/delete), ContactsProvider2 now replaces any SQLiteException whose message contains \"json\" with one carrying the fixed message \"Stripped exception message\". It also calls cursor.getCount() inside the try block of query so that deferred SQLite errors are caught and filtered too.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained commit in ContactsProvider2.java. It relies on things already present in the catch/finally blocks of query/insert/update/delete: the WRITE_PERMISSION/READ_PERMISSION constants, LogFields.Builder.setException/setResultCount and the LogUtils logging wrappers. A tree without the LogFields logging scaffolding would need the helper's logBuilder parameter adapted."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "7d344b678bde7b70b1f46c819f5ef241af53399c",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0076",
     "osv_id": "ASB-A-470115162",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a minimum-size check on ResXMLTree_attrExt::attributeSize in ResXMLTree::validateNode so binary XML nodes declaring an attribute size smaller than sizeof(ResXMLTree_attribute) are rejected with BAD_TYPE instead of being parsed.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit: a 5-line insertion inside ResXMLTree::validateNode between the existing attrExt bounds branch and the attrSize computation; the diff context is identical across 14 through 17 (only the hunk line number differs), so it should apply with a line offset to branches with the same validateNode code.",
       "The new test is appended to ResourceTypes_test.cpp, which has the same pre-image blob 3d4a88eda247 on all four majors, and uses util::HostToDevice16/32 from androidfw/Util.h."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "40ed3bb3016e15ea89c2d829a61db78192ddf726",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0078",
     "osv_id": "ASB-A-445418705",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.setGlobalProxy now rejects proxySpec and exclusionList strings whose modified-UTF-8 encoding exceeds 65535 bytes (via PolicySizeVerifier.enforceMaxStringLength) before storing them in the admin's policy state.",
      "confidence": "medium",
      "backport_notes": [
       "If the target branch lacks core/java/android/app/admin/PolicySizeVerifier.java, carry the 14 version of the class with the service change; only enforceMaxStringLength and the private countBytes are used, and the unused com.android.modules.utils.ModifiedUtf8 import can be dropped if that library is not available. The <set-global-proxy /> addition to device_admin_sample.xml accompanies the new test."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "fa2c8531ae6f36adf8c4e5cc9ad6e9c8958da4a0",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0079",
     "osv_id": "ASB-A-470966318",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "libdng_sdk stops linking the aborting ubsan-minimal runtime and instead ships its own hidden __ubsan_handle_*_overflow handlers that throw std::runtime_error, so integer overflows detected in the library become C++ exceptions rather than a process abort.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. The target's libdng_sdk must already build with rtti: true and cppflags -fexceptions (in libdng_sdk-defaults at lines 141-142 in the 14 context). Soong must honour sanitize.never together with raw -fsanitize/-fno-sanitize-link-runtime cflags. Where srcs are listed in the cc_library, append the new file to that list as in the Android 17 variant."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "42e65547eb33a30fd8b751dcb711bdd951310149",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0085",
     "osv_id": "ASB-A-414389102",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider resolve the ContentValues key case-insensitively, so that a Data column supplied with mismatched case (e.g. \"DaTa1\") is still truncated to the simple-field maximum size instead of bypassing the check.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The source change is confined to applySimpleFieldMaxSize plus a new private static findKey in DataRowHandler.java, with the same logic on 14, 15, 16 and 17; only the test file differs across branches (@Test annotations on 16). The removal of the unused mMockContactsDatabaseHelper test field is incidental and can be dropped if it conflicts."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "adef234467ab08122b8e7712d044e8a723977359",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0087",
     "osv_id": "ASB-A-483142784",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" or hosts ending in \".xyz.com\", instead of any host whose string ends in \"xyz.com\" (e.g. \"abcxyz.com\").",
      "confidence": "high",
      "backport_notes": [
       "Single-commit fix; the production hunk is the same one-condition change on all four branches (only the line offset differs). Only the test differs: addPackage(pkg) on 14/17 versus addPackage(pkg, null) on 15/16, so pick the test variant matching the branch's DomainVerificationService.addPackage signature."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b035d109935c5a7a55094dd724bdca87e629bbda",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0088",
     "osv_id": "ASB-A-471127462",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller.getCallingAppLabel() now gets the calling app's label via ApplicationInfo.loadSafeLabel(pm) instead of loadLabel(pm), so the label shown in the CA certificate redirect dialog is sanitized before display.",
      "confidence": "high",
      "backport_notes": [
       "Single-line, self-contained change with no dependencies. The same hunk (file index 883c897..1eff53f) appears on 14 through 17, so forks whose getCallingAppLabel() still ends in appInfo.loadLabel(pm) can take it verbatim.",
       "loadSafeLabel(PackageManager) is a system API on PackageItemInfo, not part of the public SDK. CertInstaller can use it because it is a platform-built system app (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "d90ffd033f10d5034d8bf161f47ce1bc88befad7",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2026-0091",
     "osv_id": "ASB-A-438742644",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WM core now records the remote-animation delegate process on the server-side Transition, strips the IApplicationThread from the RemoteTransition it sends to shell, and changes IActivityTaskManager.setRunningRemoteTransitionDelegate to take a transition token instead of an IApplicationThread.",
      "confidence": "medium",
      "backport_notes": [
       "The AIDL change, Transition.mRemoteDelegate, the server-side WindowOrganizerController handling and all Shell call sites must land together because the method signature changes. Pick a HIERARCHY_OP constant value and parcel field that are free in the target tree (14 uses 18/mReparent; 15/16 use insetsFrameOwner).",
       "On 16, the new toString case follows HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS, which has no break. The new hierarchy-op case follows a SET_SAFE_REGION_BOUNDS block whose break is not visible. Check fall-through when porting."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-opus-5-5",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "caffcf6be82b0aea04fe746907920500f1f2c81d",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0093",
     "osv_id": "ASB-A-473812391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces applicationInfo.loadLabel() with applicationInfo.loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where Settings renders the requesting app's name in the RequestManageCredentials UI, so an app-controlled label can no longer be used to hide from the user what permissions are requested.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit relying on PackageItemInfo.loadSafeLabel(PackageManager, float, int), PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and the TextUtils.SAFE_STRING_FLAG_* constants, which are framework APIs available to the platform-signed Settings app (general platform knowledge). On forks with modified imports in RequestManageCredentials.java, the import hunk may need manual placement. The three code hunks are the same in all four branch diffs."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "47f5449218e474d1ba8abda9ed35fc3e03334645",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0095",
     "osv_id": "ASB-A-484861632",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Widens the local buf_size in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the allocation size for the cloned L2CAP buffer is no longer truncated before osi_malloc, preventing a heap buffer overflow in the following memcpy.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained one-line change with no dependencies. It applies cleanly wherever l2c_fcr_clone_buf declares a uint16_t buf_size. Verify that the local type is widened (uint32_t or size_t) and that no other uint16_t intermediate is introduced between the sum and osi_malloc."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "006620f1caaa489e8e955c1df7fdfae4f5ca66f3",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0098",
     "osv_id": "ASB-A-397217317",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DocumentsUI's Shared.getCallingPackageName now honors the EXTRA_PACKAGE_NAME calling-package override only when the caller's signature matches the platform (SYSTEM_UID) signature. Before, it honored it for any caller flagged as a system or updated system app.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit in Shared.java. 14/15/17 share the same pre/post blob hashes. 16 has a different base blob, so check how it applies there. PackageManager.checkSignatures(int,int), Process.SYSTEM_UID and SIGNATURE_MATCH are long-standing public APIs (general platform knowledge).",
       "Downstream builds that rely on non-platform-signed system apps passing EXTRA_PACKAGE_NAME to DocumentsUI will lose that override after the backport; verify such integrations."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "a712c9468e47fba7280200f5d7f31f543b597fb4",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    },
    {
     "cve": "CVE-2026-0099",
     "osv_id": "ASB-A-385917501",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds onBindingDied and onNullBinding handlers to the ServiceConnection objects in HostEmulationManager so the NFC service unbinds from a HostApduService whose binding died or whose onBind returned null, instead of leaving the binding in place.",
      "confidence": "medium",
      "backport_notes": [
       "Single-commit fix per major. For 14-style code (packages/apps/Nfc, anonymous mPaymentConnection/mConnection), the 14 variant calls unbindPaymentServiceLocked()/unbindServiceIfNeededLocked() under mLock, so mPaymentServiceBound/mServiceBound are reset. The 15+ onNullBinding calls unbindService(this) directly and does not reset those flags in the shown diff. On 16+ the file is under NfcNci/ in packages/modules/Nfc, and the proto enum addition is only needed if the event-log call is ported."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "34a7821a1bfe2a57ced37c8be2470962647aec4f",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Nfc"
     ]
    },
    {
     "cve": "CVE-2026-0100",
     "osv_id": "ASB-A-484973621",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp) so that a RES_TABLE_PACKAGE_TYPE chunk whose 32-bit package id exceeds 255 is rejected and the ARSC fails to load instead of being accepted with an out-of-range package id.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit; the LoadedArsc.cpp hunk is identical across 14 through 17 with only line offsets differing, so it should apply to trees where LoadedPackage::Load still contains 'loaded_package->package_id_ = dtohl(header->id);'."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6a9eb9dd74d05c8c7bc8037f19b07b77fd7659b1",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28577",
     "osv_id": "ASB-A-389950114",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is true and the target WindowToken already contains a window, enforcing one toast window per toast token.",
      "confidence": "high",
      "backport_notes": [
       "The functional change is the check in WindowManagerService.addWindow. The Session.java constructor and WindowTestsBase.createTestSession only support the new test.",
       "The 14 commit adds a protolog.json hash entry (-610155150) for the new ProtoLog.w call. The 15 and 16 commits comment the call out instead. Check how your branch handles ProtoLog messages."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2f08e7d895e3f65d98c6b8028d4fd6432e73e55e",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28578",
     "osv_id": "ASB-A-475228205",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a per-package-name length check (enforcePackagePolicyPackageNamesLength) to the three DevicePolicyManagerService setters that accept a PackagePolicy (managed profile caller-id/contacts access policies and credential manager policy), rejecting overlong package names before the policy is processed.",
      "confidence": "high",
      "backport_notes": [
       "The Android 14 commit lists two Cherrypick-From references (4914d6e98bd2... and 62a1a105f243...). It uses an inline Preconditions.checkArgument against MAX_PACKAGE_NAME_LENGTH instead of PolicySizeVerifier.enforceMaxPackageNameLength. A tree without PolicySizeVerifier can use that form, provided MAX_PACKAGE_NAME_LENGTH is visible in DevicePolicyManagerService.",
       "Only DevicePolicyManagerService.java is changed. In each of the three setters, place the helper call after the mHasFeature early return."
      ],
      "meta": {
       "generated": "2026-10-03",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r28",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1d7703a6b66d10bb3d977f9b98d2604639c6ffa1",
       "tag": "android-security-14.0.0_r28",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r28"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "477935679"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c31c5d04d4c451b66552bb409f680ae013aa78fb",
       "subject": "DO NOT MERGE: Enforce suspend restrictions in startOp and noteOp paths",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": [
        "cve_fix",
        "restrict_automerge"
       ]
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "401188957"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1329a9c3ce3c6f24ee3340847066937eb649b60b",
       "subject": "Checking userId in com.android.server.pm.ComputerEngine#resolveContentProvider",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "471173239"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/KeyChain",
       "sha": "a9b74a603a2dde215baf6409ecd68c1beb3c824f",
       "subject": "Sanitize app label in KeyChainActivity to prevent UI injection.",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "bulletin_history": [
      {
       "bulletin": "2026-06",
       "cve": "CVE-2026-0094",
       "severity": "High",
       "versions": [
        "14",
        "15",
        "16",
        "16-qpr2"
       ],
       "status": "removed",
       "removed_seen": "2026-10-13",
       "removed_in_version": "1.4"
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "368376056",
      "489303684"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "54e06116bb5ed929c4c84fca3fda19033be88044",
       "subject": "Load the Secure NFC setting when the current user is changed upon query",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "368376056",
      "475315815",
      "475447365"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "09d6111fa3b4a9f09b001d96e93395dbd0824daa",
       "subject": "NFC Security Vulnerability in Multi-User Mode",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "368376056"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "648d77dd382c4454b84445b0186221b1f3172419",
       "subject": "Allow multi-user to control secure nfc",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "315320090",
      "418085016"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/Permission",
       "sha": "b16adcfc72885ce7cb0e008f36d18bb8225db589",
       "subject": "Revert^2 \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/modules/Permission",
       "sha": "7d9873e08efbe28351b5e567e591f03a1e26eefd",
       "subject": "Revert \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    },
    {
     "bug_ids": [
      "462214379"
     ],
     "commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "17cda9f1fd526508c371f3616d50925ea58cc21f",
       "subject": "Disallow shell to change CarrierRestrictionRules",
       "tag": "android-security-14.0.0_r28",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r28"
     ],
     "tag": "android-security-14.0.0_r28"
    }
   ],
   "coverage": {
    "n_ingested_tags": 30,
    "first_ingested_tag": "android-security-14.0.0_r1",
    "latest_ingested_tag": "android-security-14.0.0_r30",
    "window_base_tag": "android-14.0.0_r1",
    "window_start": "2023-10-04",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2025-64505": {
     "cve": "CVE-2025-64505",
     "summary": "libpng png_set_quantize now allocates and identity-initialises the quantize_index lookup table to PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so out-of-range palette indices from a malformed PNG no longer read past the table in png_do_quantize; a companion commit frees the previous quantize_index before reallocating and makes quantize_sort a local variable to stop leaks on repeated calls.",
     "component": "platform/external/libpng, read transformations (pngrtran.c, png_set_quantize / png_do_quantize)",
     "weakness": "Heap buffer over-read from an undersized lookup table (missing bounds handling), plus memory leak on repeated API calls",
     "root_cause": "In pngrtran.c png_set_quantize, when full_quantize == 0, the lookup table png_ptr->quantize_index was allocated with only num_palette bytes and filled for i < num_palette. According to the commit message, the pixel processing path in png_do_quantize indexes this table with palette indices from the image data and does no runtime bounds check. A malformed PNG whose indices are >= num_palette therefore causes reads past the end of the allocation. Separately, per the leak-fix commit message, png_set_quantize overwrote png_ptr->quantize_index without freeing the earlier allocation, so memory leaked when an application called the function more than once on the same png_struct (the message also mentions quantize_sort).",
     "fix_analysis": [
      "Commit da0c851e9072 (first), pngrtran.c:png_set_quantize: adds png_free(png_ptr, png_ptr->quantize_index) before the png_malloc in the full_quantize == 0 branch, so repeated calls do not leak. Turns quantize_sort from a png_struct member into a function-local png_bytep, allocated with (png_alloc_size_t)num_palette and freed with png_free(png_ptr, quantize_sort) at the end of the histogram branch. Moves the int j declaration up to the block scope. The commit message says the pngstruct.h change was reverted for the security backport to avoid an ABI change.",
      "Commit 411d9be12a3e (second), pngrtran.c:png_set_quantize: changes the quantize_index allocation from num_palette bytes to PNG_MAX_PALETTE_LENGTH (256) bytes, and the initialisation loop to i < PNG_MAX_PALETTE_LENGTH, so every entry from 0 to 255 holds the identity mapping. Any 8-bit index therefore stays inside the table, and indices >= num_palette map to themselves. No bounds check is added to the pixel loop; the commit message says this is deliberate, to avoid per-pixel overhead."
     ],
     "completeness": [
      "Covers the quantize_index table used in the full_quantize == 0 path. The table now spans the whole 8-bit index space, matching the oversizing of palette[], trans_alpha[] and riffled_palette[] described in the commit message.",
      "The png_do_quantize pixel loop is not changed and does no runtime bounds check. Safety depends on the table being 256 bytes.",
      "quantize_sort is still allocated with num_palette bytes. In the shown code it is indexed only by loop variables bounded by num_palette.",
      "In the full_quantize != 0 branch, the only change is the quantize_sort renaming from da0c851e9072. The material does not show how the RGB lookup table is set up or sized."
     ],
     "behavior_changes": [
      "quantize_index is always a 256-byte allocation, whatever num_palette is.",
      "During quantization, palette indices >= num_palette now map to the same index value (identity) instead of reading past the allocation.",
      "Calling png_set_quantize repeatedly on one png_struct no longer leaks the previous quantize_index; quantize_sort is a local temporary freed within the histogram branch.",
      "No new error or warning is emitted. On Android 14, png_struct layout is unchanged because the pngstruct.h change was reverted."
     ],
     "preconditions": [
      "The application must call png_set_quantize on the read png_struct with full_quantize == 0, so that quantize_index is allocated and PNG_QUANTIZE is set.",
      "A PNG whose pixel data contains palette indices >= num_palette must then be decoded through the read transformations so that png_do_quantize runs.",
      "For the leak, png_set_quantize must be called more than once on the same png_struct.",
      "Per index search, Android code that uses libpng includes frameworks/base/tools/aapt/Images.cpp read_png, frameworks/base/libs/androidfw/Png.cpp readPng and PngCrunch.cpp. None of the snippets shown call png_set_quantize."
     ],
     "per_major": {
      "14": "same fix, two commits in android-security-14.0.0_r28: da0c851e9072 (leak fix, quantize_sort made local, pngstruct.h change reverted for ABI), then 411d9be12a3e (256-byte quantize_index). The android-security-14.0.0_r29 code excerpt shows both applied; the facts report 7/8 lines of the patched region present.",
      "15": "adapted fix: 299ab85302d7 in android-security-15.0.0_r16 changes only the quantize_index allocation to 256 bytes. Its base (3749ebfa6) already has the png_free line and uses the (png_alloc_size_t)num_palette allocation form. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: 93147074acc1 in android-security-16.0.0_r7, identical diff to Android 15 against the same base (3749ebfa6). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same diff as Android 14's 411d9be12a3e: dc721fee982c in android-17.0.0_r1 (resolved via base) against blob 831d98d08, the same pre-image as on 14 after da0c851e9072. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "On trees that lack the png_free of quantize_index, apply da0c851e9072 before 411d9be12a3e. On trees that already have it (the Android 15/16 base), only the second change is needed, and the allocation line context differs: (png_alloc_size_t)num_palette instead of the (png_uint_32)num_palette * sizeof form.",
      "When backporting, keep the quantize_sort member in pngstruct.h, as Google did on Android 14, to avoid an ABI change."
     ],
     "confidence": "medium",
     "unknowns": [
      "The png_do_quantize code that does the out-of-bounds read is not in the material; its indexing behaviour comes from the commit message.",
      "Whether any Android platform component calls png_set_quantize, and therefore whether this is reachable on a device, is not shown.",
      "The bulletin lists the type as DoS at Critical severity. The material does not show what impact was demonstrated.",
      "Why the patched region is 7/8 present on Android 14 and 17.",
      "How and when the png_free leak fix reached the Android 15/16 base."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-64720": {
     "cve": "CVE-2025-64720",
     "summary": "In libpng pngrtran.c png_init_read_transformations, the palette tRNS compositing loop now branches on PNG_FLAG_OPTIMIZE_ALPHA. When the flag is set, each palette component is only premultiplied by its alpha (with gamma correction) and is not composited against the background. Palette values then keep the premultiplied invariant component <= alpha, which png_image_read_composite relies on for its png_sRGB_base[]/png_sRGB_delta[] table lookups.",
     "component": "platform/external/libpng, read transformations (pngrtran.c), simplified read API compositing",
     "weakness": "Incorrect calculation that produces values outside the range a downstream table lookup expects, leading to an out-of-bounds array index in png_sRGB_base[]/png_sRGB_delta[] (per the commit message)",
     "root_cause": "In png_init_read_transformations (pngrtran.c), the palette path for entries with 0 < trans_alpha[i] < 0xff always applied background compositing through png_composite(w, v, trans_alpha[i], back_1.*) and stored gamma_from_1[w] into palette[i]. It did this even when PNG_FLAG_OPTIMIZE_ALPHA was set. In that mode the rest of the pipeline expects palette entries to be pure premultiplied RGB (component <= alpha), with background compositing left to png_image_read_composite. Composited values can exceed alpha, so png_image_read_composite computed indices beyond the PNG_sRGB_FROM_LINEAR lookup tables png_sRGB_base[] and png_sRGB_delta[]. This mechanism comes from the commit message; the consumer code is not in the material.",
     "fix_analysis": [
      "pngrtran.c:png_init_read_transformations: inside the palette loop, in the else branch for trans_alpha[i] != 0 and != 0xff, the fix adds `if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0)`. In that branch, each of red, green and blue goes through: component = gamma_to_1[palette[i].c]; component = (component * trans_alpha[i] + 128) / 255; palette[i].c = gamma_from_1[component]. This only premultiplies: the value is linearised before and re-encoded after, and there is no background term.",
      "pngrtran.c:png_init_read_transformations: the original png_composite-based code (v = gamma_to_1[...]; png_composite(w, v, trans_alpha[i], back_1.*); palette[i].* = gamma_from_1[w]) moves unchanged into the else branch, so behaviour without PNG_FLAG_OPTIMIZE_ALPHA is preserved.",
      "Note: the commit message gives the rounding as `+ 127) / 255`, but the code uses `+ 128) / 255`. The code is what ships."
     ],
     "completeness": [
      "Covers only the palette path for partially transparent tRNS entries (0 < trans_alpha[i] < 0xff) in png_init_read_transformations. The commit names this path as the source of the out-of-range premultiplied values.",
      "The trans_alpha[i] == 0 case directly above (palette[i] = back) is not changed and still applies whatever the PNG_FLAG_OPTIMIZE_ALPHA setting. The material does not show whether the downstream consumer handles alpha 0 safely.",
      "The commit changes only pngrtran.c. No bounds check or clamping was added on the png_image_read_composite side. The fix relies on producing in-range values.",
      "The trailing `else` of the `if (i < num_trans && trans_alpha[i] != 0xff)` test (entries that are not partially transparent) is not modified, and its body is not shown."
     ],
     "behavior_changes": [
      "For palette images with partial tRNS alpha read with PNG_FLAG_OPTIMIZE_ALPHA set, palette entries are now premultiplied RGB with no background applied. Per the commit message, background compositing happens later in png_image_read_composite, so decoded pixel values change in this configuration.",
      "No change for reads without PNG_FLAG_OPTIMIZE_ALPHA: the original png_composite calculation is kept verbatim.",
      "No new error or warning paths are added."
     ],
     "preconditions": [
      "A palette PNG with a tRNS chunk that has at least one entry with alpha strictly between 0 and 255, processed with background/gamma handling so that png_init_read_transformations reaches the palette compositing loop.",
      "PNG_FLAG_OPTIMIZE_ALPHA set on the png_struct. The commit message ties this to the PNG_ALPHA_OPTIMIZED mode used with png_image_read_composite (simplified read API).",
      "Per index search, frameworks/base has classic-API users (tools/aapt/Images.cpp read_png, tools/aapt2/compile/Png.cpp readPng, libs/androidfw/Png.cpp readPng) that call png_set_palette_to_rgb/png_set_tRNS_to_alpha. The material does not show them setting PNG_ALPHA_OPTIMIZED, so it is not established that they reach this path."
     ],
     "per_major": {
      "14": "Same fix: commit ba5981bebfba in android-security-14.0.0_r28, a single hunk in pngrtran.c at line 1701 (blob 03ffef132 -> 38d1306f1). The code context at android-security-14.0.0_r29 shows the fix in place; the resolver reports 6/8 region lines present.",
      "15": "Same fix: commit 2a900c24d056 in android-security-15.0.0_r16, identical hunk content at line 1697 (blob 5fd5b7330 -> 6a5a401a0). The resolver reports 6/8 region lines present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 2113a245b832 in android-security-16.0.0_r7, diff identical to Android 15 (same blob ids, line 1697). The resolver reports 6/8 region lines present at android-security-16.0.0_r8.",
      "17": "Same fix: commit 20c02c533b25 in android-17.0.0_r1 (base), diff identical to Android 14 (blob 03ffef132 -> 38d1306f1, line 1701). The resolver reports 6/8 region lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A single-commit, single-file, single-hunk change with no dependencies. The only difference between branches is a 4-line offset (1697 vs 1701) with identical context lines, so the hunk should apply with an offset to any pngrtran.c that contains the original png_composite palette block."
     ],
     "confidence": "high",
     "unknowns": [
      "The consumer code in png_image_read_composite and the sizes of the png_sRGB_base[]/png_sRGB_delta[] tables are not in the material. The out-of-bounds mechanism is taken from the commit message.",
      "The material does not show which Android components call the simplified libpng API with PNG_ALPHA_OPTIMIZED, so actual on-device reachability is unknown.",
      "Why the resolver reports 6/8 rather than 8/8 region lines at the latest tags, when the code context shows the fix applied."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-65018": {
     "cve": "CVE-2025-65018",
     "summary": "Fixes a heap buffer overflow in libpng's simplified read API (png_image_finish_read / png_image_read_direct) when a 16-bit PNG is read into an 8-bit output buffer: first by rejecting IHDR/output bit-depth mismatches, then (rearchitected) by routing interlaced 16-to-8 conversions through an intermediate local_row buffer in a new png_image_read_direct_scaled routine.",
     "component": "platform/external/libpng, simplified read API (pngread.c: png_image_finish_read, png_image_read_direct)",
     "weakness": "Out-of-bounds write (heap buffer overflow): row writes sized by the IHDR bit depth into a user buffer sized by PNG_IMAGE_SIZE for the smaller requested output format",
     "root_cause": "In pngread.c png_image_read_direct, when the PNG's IHDR bit depth is 16 and the caller requests a non-LINEAR (8-bit) output format, png_set_scale_16 is applied and rows are read into the caller's buffer, which was sized with PNG_IMAGE_SIZE(image) for 8-bit components. Per the fix commits, for interlaced images png_combine_row writes into that buffer using the IHDR (16-bit) depth before the 16-to-8 transformation completes, so writes exceed the 8-bit-sized destination (the commit gives 6144 vs 4096 bytes for a 32x32 16-bit RGB to 8-bit RGBA read).",
     "fix_analysis": [
      "Commit 566e14540832 (Change-Id I1aa2538e), pngread.c:png_image_finish_read: before building the png_image_read_control, reads image->opaque->png_ptr->bit_depth and image->format & PNG_FORMAT_FLAG_LINEAR; returns png_image_error(...) with '16-bit PNG must use 16-bit output format' if bit_depth == 16 and LINEAR is not requested, and '8-bit PNG must not use 16-bit output format' if bit_depth < 16 and LINEAR is requested. This blocks the overflow by refusing every cross-depth conversion in the simplified API.",
      "Commit 5e46c0292d5c (Change-Id I6fe5ac89), pngread.c:png_image_finish_read: removes the rejection block above (the commit states it was unnecessarily limiting because only interlaced 16-to-8 reads are affected).",
      "Same commit, pngread.c:png_image_read_direct: adds int do_local_scale; in the 8-bit output branch after png_set_scale_16(png_ptr), sets do_local_scale = 1 when png_ptr->interlaced != 0. In the end-routine selection adds 'else if (do_local_scale != 0)': allocates row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)), sets display->local_row = row, runs png_safe_execute(image, png_image_read_direct_scaled, display), clears local_row and frees it, returns the result. Per the commit, non-interlaced 16-to-8 reads continue to use the unchanged fast path.",
      "Same commit, pngread.c: new static png_image_read_direct_scaled: computes passes from png_ptr->interlaced (1 for NONE, PNG_INTERLACE_ADAM7_PASSES for ADAM7, png_error otherwise); for each pass and each of image->height rows calls png_read_row(png_ptr, local_row, NULL) then memcpy(output_row, local_row, row_bytes) with row_bytes = display->row_bytes, advancing output_row by row_bytes. The user buffer thus only receives row_bytes bytes per row from the intermediate buffer."
     ],
     "completeness": [
      "Final (rearchitected) fix covers only the png_image_read_direct path (non-colormap output) and only when png_set_scale_16 is selected (IHDR 16-bit, non-LINEAR output) and png_ptr->interlaced != 0.",
      "The new branch is added as an 'else if' after a preceding branch in the end-routine selection, so it applies only when that earlier branch was not taken; the commit says the design follows the pattern involving do_local_compose.",
      "The colormap path (png_image_read_colormap / png_image_read_colormapped) is not modified by either commit; the material does not show whether it has an analogous issue.",
      "The reverse mismatch (sub-16-bit PNG read with a LINEAR 16-bit output format, png_set_expand_16) was rejected by the first commit and is no longer rejected after the second; the second commit asserts only 16-to-8 interlaced reads are affected.",
      "On Android 15 and 16 only the first (rejection) commit is shown, which is broader: it blocks all cross-depth conversions, interlaced or not, for both colormap and direct paths, because the check sits in png_image_finish_read just before the colormap/direct routine selection."
     ],
     "behavior_changes": [
      "With the rejection commit alone (Android 15/16 state as shown): png_image_finish_read fails via png_image_error for any 16-bit PNG read with a non-LINEAR format and for any <16-bit PNG read with a LINEAR format; callers relying on the simplified API's automatic 16-to-8 or 8-to-16 conversion now get an error.",
      "With the rearchitected fix (Android 14/17 state): no new API rejection; interlaced 16-bit PNGs read to 8-bit formats allocate one extra row buffer (png_get_rowbytes) and perform a per-row memcpy for every row of every pass, described by the commit as slow but necessary. Non-interlaced reads are unchanged."
     ],
     "preconditions": [
      "A process uses the libpng simplified read API: png_image_begin_read_* followed by png_image_finish_read with a caller-supplied buffer sized by PNG_IMAGE_SIZE for an 8-bit (non PNG_FORMAT_FLAG_LINEAR), non-colormap output format.",
      "The input PNG has IHDR bit depth 16 and is interlaced, per the second commit's narrowing; the first commit treated any 16-bit input with 8-bit output as affected.",
      "Per index search on Android 14, the callers shown (frameworks/base/tools/aapt/Images.cpp read_png, frameworks/base/tools/aapt2/compile/Png.cpp readPng, frameworks/base/libs/androidfw/Png.cpp readPng) use the low-level libpng API (png_read_info/png_get_IHDR; androidfw also png_set_strip_16) in the excerpts shown rather than png_image_finish_read; the excerpts are partial and actual Android consumers of the simplified API are not shown."
     ],
     "per_major": {
      "14": "Both commits applied in android-security-14.0.0_r28: 566e14540832 (rejection check) then 5e46c0292d5c (rearchitected local_row fix, which removes the check). Final state is the rearchitected fix; at android-security-14.0.0_r29 the context shows no rejection block and 8/8 patched lines present.",
      "15": "Only the rejection commit shown (6a5cceb169b3 in android-security-15.0.0_r16, same diff as A14's first commit, offset +6 lines); 4/4 lines present at android-security-15.0.0_r17. The rearchitected commit is not shown for this branch, so 15 carries the stricter bit-depth rejection.",
      "16": "Only the rejection commit shown (960064fdb5f4 in android-security-16.0.0_r7, identical diff to A15); 4/4 lines present at android-security-16.0.0_r8. Rearchitected commit not shown for this branch.",
      "17": "Rearchitected commit 0e9750be87cb in android-17.0.0_r1 (diff base d804c90e3, i.e. on top of the rejection commit, same content as A14's second commit); 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Order matters: 5e46c0292d5c's diff base (d804c90e3) already contains the rejection block from 566e14540832 and removes it, so apply the rejection commit first or drop that hunk; if a branch (like 15/16 as shown) stops at the rejection commit, it is protected but with the broader API restriction.",
      "The rearchitected commit uses png_image_read_control fields local_row, first_row and row_bytes and png_safe_execute; local_row and row_bytes are visible in existing code, first_row is not shown; the diff touches only pngread.c."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the png_combine_row code or the full else-if chain in png_image_read_direct, so the exact mechanism by which the IHDR depth governs the write size, and the conditions of the preceding branch, are taken from the commit messages.",
      "Whether Android 15/16 later received the rearchitected commit is not shown; only the rejection commit appears for those branches.",
      "Whether first_row is a pre-existing png_image_read_control field on a fork's base is not shown.",
      "png_image_read_direct_scaled copies local_row into every output row of every pass from a single reused buffer; the material does not show whether the resulting interlaced image content is pixel-correct (the commit reports regression tests passing). This is a correctness question, not a memory-safety one.",
      "Which Android components actually call png_image_finish_read is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0039": {
     "cve": "CVE-2026-0039",
     "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow checks call a library-private runtime that throws std::runtime_error, replacing the ubsan-minimal runtime that hard-aborts the process when an overflow is detected.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image parsing, exercised by ImageDecoder per the commit's Test line)",
     "weakness": "Integer overflow in DNG processing turned into a hard process abort by the ubsan-minimal runtime (CWE-190 leading to reachable abort / DoS)",
     "root_cause": "libdng_sdk inherits sanitize.misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] from libdng_sdk-defaults in Android.bp. With that configuration the ubsan-minimal runtime is linked, and its handlers abort the process on a detected overflow (the commit message calls this a 'hard abort'). Integer-overflow sites in the DNG code, referenced by the seven bug ids listed in the commit, therefore terminate the decoding process when a malformed DNG triggers them instead of failing the decode. The library already builds with rtti: true and cppflags: [\"-fexceptions\"], so an exception-based error path was available but the sanitizer did not use it.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize: { never: true } to turn off Soong-managed sanitizers for this module, which per the inline comment disables the ubsan_minimal runtime and overrides the misc_undefined list inherited from libdng_sdk-defaults.",
      "Android.bp :: cc_library libdng_sdk: re-enables the same instrumentation manually with the cflags \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" and adds \"-fno-sanitize-link-runtime\", so the compiler still emits __ubsan_handle_* calls but no sanitizer runtime is linked. \"-DqDNGValidate=0\" is kept.",
      "Android.bp :: cc_library libdng_sdk: adds srcs ubsan-replacement/ubsan_throwing_runtime.cpp to the library.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each throws std::runtime_error(\"ubsan: <kind>\"), so a detected overflow now unwinds as a C++ exception (the library builds with -fexceptions) instead of aborting.",
      "OWNERS: metadata-only change (bug component and owner list), no functional effect."
     ],
     "completeness": [
      "The fix does not correct any of the individual overflowing arithmetic sites. It changes how every detected signed or unsigned overflow in libdng_sdk is reported, so all referenced bugs and any other such overflow in the library take the same exception path.",
      "Only five handler symbols are provided (add, sub, mul, negate, divrem). There are no _abort variants and no handlers for other UBSan checks, so enabling further -fsanitize checks or -fno-sanitize-recover on this module would need additional handlers.",
      "The change is scoped to the libdng_sdk module. libdng_sdk-defaults still carries misc_undefined, so any other module that uses those defaults (none shown in the material) would still get the default sanitizer runtime.",
      "The material does not show whether every call path into libdng_sdk catches std::runtime_error. An uncaught exception would still terminate the process via std::terminate."
     ],
     "behavior_changes": [
      "Integer overflow detected during DNG processing now raises std::runtime_error(\"ubsan: add-overflow\" / \"sub-overflow\" / \"mul-overflow\" / \"negate-overflow\" / \"divrem-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links a sanitizer runtime (ubsan-minimal). The overflow instrumentation is still compiled in through cflags.",
      "The change applies to both device and host builds of libdng_sdk (host_supported: true)."
     ],
     "preconditions": [
      "A process decodes an untrusted DNG image through libdng_sdk (the commit's test is ImageDecoderTest#testInterestingDngs, which points to the ImageDecoder path).",
      "The DNG content must drive a signed or unsigned integer overflow in libdng_sdk arithmetic. The specific sites are referenced only by bug id and are not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk and ubsan_throwing_runtime.cpp are identical to the other branches; only OWNERS differs. Code context at android-security-14.0.0_r29 shows the change in place (the per-major check reports 3/5 lines of the patched region present).",
      "15": "same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16, with identical Android.bp (blob 1978b39..3d95a7b) and runtime file. OWNERS differs.",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7, with identical Android.bp and runtime file. OWNERS differs.",
      "17": "same fix: commit 7937f2f835ab (subject marked RESTRICT AUTOMERGE) is in the android-17.0.0_r1 base, with identical Android.bp and runtime file."
     },
     "backport_notes": [
      "The Android.bp hunk is byte-identical across 14 to 17 (same pre/post blob ids), so it should apply cleanly wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults with rtti: true and -fexceptions. Only OWNERS needs manual resolution. Per the inline comment, the fix relies on sanitize.never combined with explicit -fsanitize cflags, plus the handlers being hidden-visibility symbols inside the same library."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact overflowing expressions behind the seven bug ids are not in the material.",
      "Which catch handlers (in dng_sdk or its callers such as the ImageDecoder native path) absorb std::runtime_error is not shown, so it is unverified whether every entry point turns the exception into a clean decode failure.",
      "The material does not explain why the bulletin rates this DoS as Critical."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0040": {
     "cve": "CVE-2026-0040",
     "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that signed/unsigned integer overflow detected by UBSan throws a C++ std::runtime_error (through a new in-tree runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of using the ubsan-minimal runtime, which hard-aborts.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image library, build configuration in Android.bp)",
     "weakness": "Integer overflow in the DNG SDK turned into a process abort by the UBSan minimal runtime (reachable abort, denial of service)",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined = [unsigned-integer-overflow, signed-integer-overflow]. With that configuration the ubsan-minimal runtime is used, and the commit message says it does a hard abort on a detected overflow. Any detected integer overflow in the DNG SDK's arithmetic while processing a crafted DNG therefore ended the hosting process instead of producing a recoverable error. This was the case even though the library is already built with rtti: true and cppflags -fexceptions, and the commit message says C++ exceptions are enabled in this library. The commit lists seven bug IDs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942). The material does not show the individual arithmetic sites.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize: { never: true }. Per the in-file comment, this disables the ubsan_minimal runtime so the library can inject its own, overriding the Soong sanitizer handling inherited from libdng_sdk-defaults.",
      "Android.bp :: cc_library libdng_sdk: re-enables the overflow checks directly with the cflag -fsanitize=signed-integer-overflow,unsigned-integer-overflow and adds -fno-sanitize-link-runtime, so overflows are still instrumented but no sanitizer runtime is linked. The existing -DqDNGValidate=0 is kept.",
      "Android.bp :: cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs. This file supplies the handler symbols that the instrumentation calls.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\"). Because the library is compiled with -fexceptions (libdng_sdk-defaults, Android.bp lines 141-142), the exception propagates out of the SDK code to a caller that can catch it, instead of a hard abort.",
      "OWNERS: ownership metadata update only, no functional effect."
     ],
     "completeness": [
      "The fix works at the build/runtime level. It changes what happens on an overflow (an exception instead of an abort) but does not fix any overflowing arithmetic in the DNG SDK sources. No SDK source file is changed in this commit.",
      "Handlers are provided for add, sub, mul, negate and divrem overflow, the checks tied to the two sanitizers enabled through cflags. The _abort handler variants are not defined. Whether the build could ever emit calls to them is not shown.",
      "The thrown type is std::runtime_error. Whether every consumer of libdng_sdk catches it is not shown in the material. The commit's test, ImageDecoderTest#testInterestingDngs, covers the ImageDecoder path only.",
      "sanitize.never: true applies to the whole libdng_sdk module, so any other sanitizer Soong would otherwise apply to it is also dropped (general platform knowledge). The diff only re-adds the two integer-overflow checks through cflags."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk now raises a C++ std::runtime_error with the message \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\" instead of aborting the process.",
      "libdng_sdk no longer links a sanitizer runtime (-fno-sanitize-link-runtime plus sanitize.never). Integer-overflow instrumentation is kept through explicit cflags.",
      "Callers whose process previously died now receive an exception or a decode failure, depending on how they handle it."
     ],
     "preconditions": [
      "A DNG file whose contents push the DNG SDK's integer arithmetic into signed or unsigned overflow is processed by libdng_sdk. The commit's test (ImageDecoderTest#testInterestingDngs) points to the platform image decoding path as one entry point.",
      "The overflow checks must be compiled in. Before the fix this comes from misc_undefined in libdng_sdk-defaults, and after it from explicit -fsanitize cflags.",
      "Per index search, frameworks/base DngCreator JNI and frameworks/av img_utils DngUtils are DNG-related code outside dng_sdk. The snippets do not show them using libdng_sdk, so they are not evidence of a reachable path."
     ],
     "per_major": {
      "14": "same fix: commit 42e65547eb33 in android-security-14.0.0_r28 (cherry-pick, per Cherrypick-From). At android-security-14.0.0_r29 the resolver reports 3/5 lines of the patched region present. The code context at that tag shows the new Android.bp sanitize/cflags/srcs block and ubsan_throwing_runtime.cpp.",
      "15": "same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the runtime file are identical to the other branches, and only the prior OWNERS content differs. At android-security-15.0.0_r17 the resolver reports 3/4 lines of the patched region present.",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the runtime file are identical, and only the prior OWNERS content differs. At android-security-16.0.0_r8 the resolver reports 3/5 lines present.",
      "17": "same fix: commit 7937f2f835ab in the base release android-17.0.0_r1 (subject tagged RESTRICT AUTOMERGE). Android.bp and the runtime file are identical. At android-security-17.0.0_r1 the resolver reports 3/5 lines present."
     },
     "backport_notes": [
      "Single commit. It depends on libdng_sdk-defaults already having rtti: true and cppflags -fexceptions, which are present at android-security-14.0.0_r29. The Android.bp hunk has the same blob indexes (1978b39..3d95a7b) on all four majors. Only the OWNERS hunk varies, and it has no functional effect.",
      "For forks: the build relies on Soong honoring sanitize.never together with raw -fsanitize cflags and -fno-sanitize-link-runtime. If a fork builds libdng_sdk with -fno-sanitize-recover, clang emits calls to __ubsan_handle_*_overflow_abort, which this runtime does not define (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "The specific overflowing expressions in the DNG SDK behind the seven referenced bugs are not shown.",
      "The material does not show which process hosts the decode in the reported scenario, so the scope of the DoS impact is unknown.",
      "Whether all consumers of libdng_sdk catch the new std::runtime_error, so that it does not itself end in termination from an uncaught exception, is not shown. Only ImageDecoderTest is cited.",
      "Whether the build could ever emit calls to the _abort handler variants that the new runtime does not define is not shown.",
      "The material does not explain why the per-major resolver reports only 3/5 (or 3/4) lines of the patched region present in the latest tags."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0041": {
     "cve": "CVE-2026-0041",
     "summary": "Rebuilds libdng_sdk without the ubsan-minimal runtime and links a project-local replacement (ubsan-replacement/ubsan_throwing_runtime.cpp) whose integer-overflow handlers throw std::runtime_error, so a UBSan-detected signed/unsigned integer overflow in libdng_sdk becomes a C++ exception instead of a hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk (Android.bp build config and UBSan runtime replacement)",
     "weakness": "Integer overflow in libdng_sdk arithmetic, surfaced as a sanitizer-triggered abort (denial of service). The fix changes the failure mode, not the arithmetic.",
     "root_cause": "In Android.bp, libdng_sdk-defaults enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow. libdng_sdk inherited these defaults together with the ubsan_minimal runtime. With that runtime, an integer overflow detected at runtime inside libdng_sdk causes a hard abort of the hosting process. The commit references seven bug IDs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942). The specific arithmetic sites are not in the diff; the fix handles all of them at the runtime level.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize.never: true. The comment explains that Soong has no flag to drop only the ubsan_minimal runtime, so 'never' is combined with explicit -fsanitize cflags.",
      "Android.bp, cc_library libdng_sdk: cflags now include -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which re-enables the same two checks the defaults request, and -fno-sanitize-link-runtime. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: srcs adds ubsan-replacement/ubsan_throwing_runtime.cpp, so the replacement handlers are compiled into libdng_sdk.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new file): defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short message. libdng_sdk-defaults already sets rtti: true and cppflags -fexceptions (Android.bp lines 141-142), and the commit message says C++ exceptions are enabled in this library.",
      "OWNERS: metadata only (bug component and owner list). No functional effect."
     ],
     "completeness": [
      "Covers only libdng_sdk's own code. The overflowing arithmetic itself is not changed; only the outcome of a detected overflow changes.",
      "Only five handlers are provided (add, sub, mul, negate, divrem). The defaults enable only the two integer-overflow checks, and no other UBSan classes are added.",
      "The handlers are declared with no parameters, and the exception messages contain no source location.",
      "The handlers have hidden visibility, so they are not exported from libdng_sdk.",
      "The material does not show whether callers of libdng_sdk catch std::runtime_error. If no caller catches it, the process would still terminate."
     ],
     "behavior_changes": [
      "On a UBSan-detected integer overflow inside libdng_sdk, a std::runtime_error is thrown (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of the process aborting.",
      "libdng_sdk no longer uses the Soong-provided ubsan_minimal runtime (sanitize.never plus -fno-sanitize-link-runtime). Instrumentation for the two overflow checks is kept through explicit cflags.",
      "Exceptions can now leave libdng_sdk overflow sites, where previously the process aborted."
     ],
     "preconditions": [
      "A DNG is processed by code that links libdng_sdk. The commit's Test line (atest ImageDecoderTest#testInterestingDngs) points to ImageDecoder as an exercised consumer.",
      "The input causes libdng_sdk's instrumented signed or unsigned integer arithmetic to overflow at runtime.",
      "The material does not show the specific overflow sites or input conditions."
     ],
     "per_major": {
      "14": "same fix: commit 42e65547eb33 in android-security-14.0.0_r28 (cherry-picked from three internal changes). The Android.bp hunk and the new ubsan_throwing_runtime.cpp are the same as on the other branches. The OWNERS diff differs only in the old owner list. The code shown at android-security-14.0.0_r29 contains the fix, although the per-major facts report 3/5 lines of the patched region present.",
      "15": "same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16. The Android.bp and new-file blob indexes are identical to the 14 change; OWNERS differs only in prior content. Per-major facts report 3/4 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the new file are identical; OWNERS differs only in prior content. Per-major facts report 3/5 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "same fix: commit 7937f2f835ab in android-17.0.0_r1 (resolved in base), titled with RESTRICT AUTOMERGE. Android.bp and the new file are identical. Per-major facts report 3/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Android.bp hunk applies against the same base blob (index 1978b39) on all four branches, so only OWNERS should conflict. The change relies on libdng_sdk-defaults already providing rtti: true and cppflags -fexceptions."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow for the seven referenced bugs, and which input fields cause them.",
      "Whether the thrown std::runtime_error is caught inside dng_sdk or by consumers (for example the ImageDecoder path), so that decoding fails gracefully rather than ending in std::terminate.",
      "Whether clang emits calls to these exact handler names under the given flags. For example, build settings such as -fno-sanitize-recover or trap mode could select other handlers, and the material does not cover this.",
      "Why the per-major facts report only a partial line match of the patched region at the latest tags.",
      "Why the bulletin rates a DoS as Critical.",
      "Whether host_linux builds (static_libs libcompiler_rt-extras) behave the same with the replacement runtime."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0042": {
     "cve": "CVE-2026-0042",
     "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error instead of going to the ubsan minimal runtime, which does a hard abort.",
     "component": "platform/external/dng_sdk: the libdng_sdk build (Android.bp) and a new ubsan-replacement runtime. The test points to the ImageDecoder DNG path as the consumer.",
     "weakness": "A signed or unsigned integer overflow in libdng_sdk code. Under the ubsan minimal runtime used before the fix, this caused a hard abort of the hosting process (DoS). The diff does not show the overflow sites themselves.",
     "root_cause": "libdng_sdk-defaults (Android.bp lines 144-149) enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow. With the stock Soong configuration, this uses the ubsan minimal runtime, whose handlers abort on any detected overflow (general platform knowledge). libdng_sdk is built with rtti and -fexceptions (lines 141-142). So an input that triggered an arithmetic overflow anywhere in the instrumented library aborted the process. The commit message names the abort as the problem: 'C++ exceptions are enabled in this library and are a much, much better solution for ubsan errors than a hard abort is.'",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds `sanitize: { never: true }`. The comment says this disables the ubsan_minimal runtime so the module can inject its own, and that it is a workaround because Soong has no specific flag for this.",
      "Android.bp, cc_library libdng_sdk: replaces `cflags: [\"-DqDNGValidate=0\"]` with cflags that keep -DqDNGValidate=0 and add `-fsanitize=signed-integer-overflow,unsigned-integer-overflow` (turning the instrumentation back on by hand) and `-fno-sanitize-link-runtime`, so no sanitizer runtime is linked.",
      "Android.bp, cc_library libdng_sdk: adds `ubsan-replacement/ubsan_throwing_runtime.cpp` to srcs. This file provides the handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines five extern \"C\" functions with hidden visibility: __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one does `throw std::runtime_error(\"ubsan: <kind>\")`. Because the library is built with -fexceptions, the throw unwinds out of the overflowing code to the nearest catch instead of aborting.",
      "OWNERS: metadata only (bug component, an include of the frameworks/base graphics OWNERS file, LAST_RESORT_SUGGESTION owners). It has no functional effect.",
      "Test: atest ImageDecoderTest#testInterestingDngs. This points to the ImageDecoder DNG path as the validated consumer."
     ],
     "completeness": [
      "The fix changes only the failure mode. It does not correct any specific overflowing arithmetic in the DNG SDK sources. Overflows caught by the five provided handlers now throw instead of aborting.",
      "Only the `libdng_sdk` cc_library is changed. `libdng_sdk-defaults` still carries sanitize.misc_undefined (context lines 144-149), so any other module that uses those defaults would not get this change. No such module is shown in the material.",
      "The material does not show whether every caller of libdng_sdk catches std::runtime_error (or uses catch(...)). An uncaught exception would still terminate the process. The test only exercises the ImageDecoder path.",
      "`sanitize: { never: true }` also removes any other sanitizer Soong would otherwise apply to this module (general platform knowledge). The diff adds back only the two integer-overflow checks."
     ],
     "behavior_changes": [
      "When libdng_sdk detects an integer overflow, the handler no longer aborts. It throws a std::runtime_error with the message 'ubsan: add-overflow', 'sub-overflow', 'mul-overflow', 'negate-overflow' or 'divrem-overflow', which unwinds to the nearest handler. If no caller catches it, the process still terminates.",
      "libdng_sdk no longer links a sanitizer runtime (-fno-sanitize-link-runtime). The handler symbols are defined inside the library with hidden visibility.",
      "libdng_sdk is built with sanitize.never, so Soong applies none of its own sanitizer configuration to this module. Integer-overflow instrumentation is kept through explicit cflags."
     ],
     "preconditions": [
      "A DNG image is parsed by libdng_sdk code compiled with the integer-overflow sanitizers. The test name (ImageDecoderTest#testInterestingDngs) points to the ImageDecoder path as the exercised consumer.",
      "The image must push an arithmetic operation in the DNG SDK into signed or unsigned overflow. The material does not show which sites."
     ],
     "per_major": {
      "14": "Same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28, a cherry-pick. The Android.bp hunk (1978b39..3d95a7b) is identical to the other branches and the OWNERS change is adapted to this branch. The code context at android-security-14.0.0_r29 shows the full change (sanitize never, cflags, srcs, runtime file), even though the per-major fact says 3/5 lines of the patched region are present.",
      "15": "Same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. The Android.bp hunk and runtime file are identical. Only the OWNERS diff differs, because the branch had different prior content; it has no functional effect.",
      "16": "Same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. The Android.bp hunk and runtime file are identical. The OWNERS diff is adapted to this branch's prior content.",
      "17": "Same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (base tag). The commit title carries RESTRICT AUTOMERGE. The Android.bp hunk and runtime file are identical."
     },
     "backport_notes": [
      "The Android.bp hunk is the same on 14, 15, 16 and 17 (same blob ids 1978b39..3d95a7b), so the per-branch differences are only in OWNERS, which has no functional effect. The throw can only unwind if libdng_sdk-defaults already has `rtti: true` and `cppflags: [\"-fexceptions\"]`. Both are at lines 141-142 in the Android 14 context.",
      "For Android 14, the '3/5 lines present' metric appears to be a line-matching artifact: the android-security-14.0.0_r29 context shows all added Android.bp lines and the full ubsan_throwing_runtime.cpp."
     ],
     "confidence": "high",
     "unknowns": [
      "Which DNG SDK arithmetic sites overflow for the seven linked bugs. The diff contains no source changes to the parser.",
      "Which handler symbols the compiler actually emits under these flags, and whether the five provided handlers cover all of them.",
      "Which processes host libdng_sdk, and whether they all catch std::runtime_error. Only the ImageDecoder test is referenced.",
      "Why the bulletin rates this DoS as Critical (for example, which process crashes). The material does not say.",
      "Whether any other cc module in external/dng_sdk uses libdng_sdk-defaults and still uses the aborting minimal runtime.",
      "Whether the defaults on Android 15, 16 and 17 also carry rtti and -fexceptions. Only the Android 14 context is shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0043": {
     "cve": "CVE-2026-0043",
     "summary": "Rebuilds libdng_sdk with Soong sanitizer integration disabled (sanitize.never), adds explicit -fsanitize=signed-integer-overflow,unsigned-integer-overflow plus -fno-sanitize-link-runtime, and links a new in-tree UBSan handler file (ubsan-replacement/ubsan_throwing_runtime.cpp). Its overflow handlers throw std::runtime_error instead of the ubsan-minimal hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (DNG image parsing library; the commit's test is ImageDecoderTest#testInterestingDngs)",
     "weakness": "Integer overflow (signed and unsigned add/sub/mul/negate/divrem) in DNG parsing arithmetic. The diff only changes how UBSan-detected overflows are handled (abort vs C++ exception) and does not show the individual overflow sites.",
     "root_cause": "libdng_sdk-defaults (Android.bp lines 144-149 in context) enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow, and the libdng_sdk cc_library inherited it. Per the commit message and the Android.bp comment, the ubsan-minimal runtime was used, so an integer overflow detected in the library caused a hard abort rather than a recoverable error. This was the case even though the library is built with rtti: true and -fexceptions (context lines 141-142) and C++ exceptions are enabled. Seven bug IDs are attached to the commit. The specific arithmetic sites are not in the material.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize { never: true } to turn off Soong's sanitizer handling (and its ubsan_minimal runtime) for this module. The comment says this is a workaround because Soong has no flag that only drops the runtime.",
      "Android.bp :: cc_library libdng_sdk: adds cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow so the overflow instrumentation is still emitted, and -fno-sanitize-link-runtime so the sanitizer runtime is not linked.",
      "Android.bp :: cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs so the handler symbols are provided inside libdng_sdk.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\"), so a detected overflow becomes a C++ exception instead of an abort.",
      "OWNERS: ownership metadata update only, no functional effect."
     ],
     "completeness": [
      "Covers only the five integer-overflow handler kinds (add, sub, mul, negate, divrem). Only signed-integer-overflow and unsigned-integer-overflow are enabled, which matches what the defaults enabled before. No other UBSan checks are enabled or handled.",
      "The underlying arithmetic in dng_sdk is not changed. The fix turns each detected overflow into an exception. The material does not show whether each caller of libdng_sdk catches std::runtime_error, and an uncaught exception would still terminate the process.",
      "The override applies only to the libdng_sdk cc_library. libdng_sdk-defaults still carries sanitize.misc_undefined, so any other module built from those defaults (none shown in the material) is not covered by this change.",
      "The handlers have hidden visibility, so they only resolve references from within libdng_sdk. The host_linux static_libs libcompiler_rt-extras line is unchanged."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk now throws std::runtime_error with the message \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\" instead of aborting the process.",
      "libdng_sdk no longer uses the ubsan_minimal runtime. The instrumentation is still present through the explicit -fsanitize cflags.",
      "The replacement handlers take no arguments and throw a fixed message per overflow kind."
     ],
     "preconditions": [
      "A process must parse an attacker-controlled DNG file with libdng_sdk. The commit's test (ImageDecoderTest#testInterestingDngs) indicates that the image decoding path reaches this code.",
      "The DNG content must make one of the instrumented arithmetic operations in dng_sdk overflow. The specific sites are not in the material."
     ],
     "per_major": {
      "14": "same fix: 42e65547eb33 in android-security-14.0.0_r28, with the same Android.bp and ubsan_throwing_runtime.cpp hunks (the OWNERS hunk differs). The code context at android-security-14.0.0_r29 shows the new Android.bp block and runtime file. The resolver reports 3/5 lines of the patched region present at r29.",
      "15": "same fix: 1ec41401b3d4 in android-security-15.0.0_r16, with the same Android.bp and runtime hunks and an OWNERS hunk adapted to that branch's prior content. The resolver reports 3/4 lines present at r17.",
      "16": "same fix: 74a5a91d169a in android-security-16.0.0_r7, with the same Android.bp and runtime hunks and an OWNERS hunk adapted to that branch's prior content. The resolver reports 3/5 lines present at r8.",
      "17": "same fix: 7937f2f835ab in android-17.0.0_r1 (base), marked RESTRICT AUTOMERGE, with the same Android.bp and runtime hunks. The resolver reports 3/5 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. The Android.bp hunk applies to the same base index 1978b39 on 14, 15, 16 and 17. The library must have C++ exceptions enabled; context shows rtti: true and -fexceptions in libdng_sdk-defaults on 14. Apply the sanitize.never setting, the -fsanitize/-fno-sanitize-link-runtime cflags and the new source file together.",
      "The fix depends on Soong. A fork with a different build configuration must make sure the ubsan minimal runtime is not linked and that the in-tree handlers are used."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the specific overflow sites in dng_sdk behind the seven bug IDs, nor why the bulletin rates an abort-to-exception change as Critical EoP.",
      "Whether callers of libdng_sdk (e.g. the ImageDecoder DNG path) catch std::runtime_error is not shown.",
      "Whether any other module uses libdng_sdk-defaults is not shown.",
      "Whether the compiler emits calls to the recoverable (non-_abort) handler names under these cflags is not shown in the material.",
      "Why the resolver reports only partial presence (3/5 or 3/4 lines) of the patched region in the latest tags is not explained."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0044": {
     "cve": "CVE-2026-0044",
     "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits throw a C++ exception (handlers in the new file ubsan-replacement/ubsan_throwing_runtime.cpp) instead of going to the ubsan minimal runtime, which aborts the process.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and sanitizer runtime",
     "weakness": "Integer overflow in DNG SDK arithmetic. The UBSan integer-overflow sanitizer turned it into a process abort (denial of service).",
     "root_cause": "libdng_sdk-defaults compiles the library with sanitize.misc_undefined = [unsigned-integer-overflow, signed-integer-overflow] (Android.bp lines 144-149). Under Soong's handling, detected overflows go to the ubsan minimal runtime, which does a hard abort. On input that drives DNG SDK arithmetic into overflow, the host process was therefore aborted, even though the library is built with -fexceptions and rtti. The commit message says C++ exceptions are enabled in this library and are 'a much, much better solution for ubsan errors than a hard abort'. The commit lists seven Bug IDs; their individual content is not shown.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true }. Per the inline comment, this disables the ubsan_minimal runtime so the library can supply its own handlers.",
      "Android.bp, cc_library libdng_sdk: turns the checks back on through cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow and adds -fno-sanitize-link-runtime. The overflow checks are still emitted, but no sanitizer runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message. Because the library is compiled with -fexceptions, the exception can unwind to a catch handler instead of aborting the process.",
      "OWNERS: ownership metadata only, no functional effect."
     ],
     "completeness": [
      "The fix does not correct any of the overflowing arithmetic in the DNG SDK sources. It changes only how a detected overflow is reported (exception instead of abort). Handling sits at the sanitizer handler level, so it applies the same way to every instrumented overflow site in libdng_sdk.",
      "The module provides only the five handlers that correspond to the enabled checks (add, sub, mul, negate and divrem overflow).",
      "The handlers have hidden visibility and are compiled only into the libdng_sdk module. Any other module that reuses libdng_sdk-defaults (which still carries sanitize.misc_undefined) would keep the abort behavior. The material does not show whether such modules exist.",
      "The material does not show whether every code path that can hit an overflow sits inside a try/catch that handles std::runtime_error. An uncaught exception would still terminate the process.",
      "host_linux still links libcompiler_rt-extras statically (Android.bp line 196). The material does not show whether this interacts with the replacement handlers on host builds."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\" and so on) instead of aborting the process.",
      "libdng_sdk is no longer linked against Soong's ubsan minimal runtime. The signed and unsigned integer-overflow checks are still compiled in through explicit cflags.",
      "The commit's test is atest ImageDecoderTest#testInterestingDngs. The material does not show how callers see the new exception."
     ],
     "preconditions": [
      "A process that links libdng_sdk must decode a DNG file whose contents drive libdng_sdk arithmetic into signed or unsigned integer overflow. The regression test targets android.graphics.ImageDecoder.",
      "The material does not show the exact overflow sites or the attack surface. Per index search, the matches (frameworks/base android_hardware_camera2_DngCreator.cpp, frameworks/av img_utils DngUtils.cpp) are DNG writing code and do not establish reachability of libdng_sdk."
     ],
     "per_major": {
      "14": "Same fix: 42e65547eb33 in android-security-14.0.0_r28, a cherry-pick of three internal commits. The code context at android-security-14.0.0_r29 shows the full change in place (sanitize.never, cflags, srcs and the new file).",
      "15": "Same fix: 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the new runtime file are identical to 14 (index 1978b39..3d95a7b and aeaad0c). Only the OWNERS diff differs.",
      "16": "Same fix: 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the new file are identical to 14. The OWNERS diff differs only in the prior content.",
      "17": "Same fix: 7937f2f835ab in android-17.0.0_r1 (subject tagged RESTRICT AUTOMERGE). Android.bp and the new file are identical to 14."
     },
     "backport_notes": [
      "The change consists only of build configuration and one new source file, and it is identical across 14 through 17 (Android.bp index 1978b39..3d95a7b in every branch). It should apply cleanly wherever libdng_sdk's cc_library still has cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults sets -fexceptions. The fix depends on the toolchain emitting the non-minimal handler names (__ubsan_handle_*_overflow). With -fsanitize-minimal-runtime, clang calls __ubsan_handle_*_minimal symbols instead (general platform knowledge).",
      "The per-major '3/5 lines present' figure is a heuristic on the patched region. The code context at android-security-14.0.0_r29 shows the complete fix present."
     ],
     "confidence": "high",
     "unknowns": [
      "Which DNG SDK functions contain the overflowing arithmetic. Seven bug IDs are referenced, but the commit includes no source-level fix.",
      "Whether all libdng_sdk entry points used by the platform catch std::runtime_error, or whether some paths would still terminate through an uncaught exception.",
      "Whether other modules build from libdng_sdk-defaults and so keep the aborting minimal runtime.",
      "Why the bulletin rates a DoS as Critical. The material gives no detail on the affected process or on remote reachability."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0051": {
     "cve": "CVE-2026-0051",
     "summary": "Rebuilds libdng_sdk so that the integer-overflow UBSan checks call a library-private runtime that throws std::runtime_error instead of the ubsan-minimal runtime that aborts the process, turning overflow detections on malformed DNG input into recoverable C++ exceptions.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG parsing arithmetic, surfaced as a process abort by the UBSan minimal runtime (denial of service)",
     "root_cause": "libdng_sdk-defaults (Android.bp) enables sanitize.misc_undefined for \"unsigned-integer-overflow\" and \"signed-integer-overflow\". With Soong's default handling this links the ubsan-minimal runtime, whose handlers abort the process when an overflow is detected (the commit message calls this a hard abort). A DNG file that makes any instrumented add/sub/mul/negate/div operation in the DNG SDK overflow therefore kills the process decoding the image, even though the library is built with rtti and -fexceptions and could otherwise recover via exceptions. The arithmetic itself is not changed by this fix; the material does not identify the specific overflowing sites (seven bug IDs are listed).",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } to stop Soong from applying its own sanitizer configuration (and thus from linking the ubsan-minimal runtime) to this module, overriding the misc_undefined list inherited from libdng_sdk-defaults.",
      "Android.bp, cc_library libdng_sdk: replaces cflags [\"-DqDNGValidate=0\"] with cflags that keep -DqDNGValidate=0 and add \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" so the overflow instrumentation is still compiled in, plus \"-fno-sanitize-link-runtime\" so no compiler-rt UBSan runtime is linked.",
      "Android.bp, cc_library libdng_sdk: adds srcs ubsan-replacement/ubsan_throwing_runtime.cpp so the handler symbols the instrumentation calls are satisfied by the library itself.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow, each of which throws std::runtime_error with a message naming the overflow kind. Because libdng_sdk-defaults already sets rtti: true and cppflags -fexceptions, the throw unwinds through the DNG SDK instead of aborting.",
      "OWNERS: ownership metadata update only, no functional effect."
     ],
     "completeness": [
      "Covers the two sanitizers enabled by the new -fsanitize flag (signed and unsigned integer overflow) and provides five non-abort handler entry points for those checks; no other UBSan check is enabled on libdng_sdk by the module's own configuration.",
      "Does not fix any of the underlying overflowing arithmetic in the DNG SDK sources; it changes the failure mode from abort to exception. Whether every caller of libdng_sdk catches std::runtime_error (as opposed to the SDK's own exception types) is not shown in the material.",
      "Only the libdng_sdk cc_library is changed. libdng_sdk-defaults still carries sanitize.misc_undefined, so any other module built from those defaults keeps the default Soong UBSan runtime behaviour; the material does not list such modules.",
      "No _abort handler variants are defined; they would be needed if non-recoverable sanitizer mode were ever enabled for this module.",
      "The handlers take no parameters and ignore the diagnostic data clang passes, so no source location is reported; this is a diagnostics limitation, not a security gap."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\", \"sub-overflow\", \"mul-overflow\", \"negate-overflow\" or \"divrem-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the compiler-rt ubsan-minimal runtime; the handler symbols are provided internally with hidden visibility, so they are not exported from the shared library.",
      "Soong sanitizer processing is disabled for libdng_sdk (sanitize.never: true); sanitizer coverage is now controlled solely by the explicit cflags."
     ],
     "preconditions": [
      "A process must decode a crafted DNG file with libdng_sdk; the test referenced by the commit (ImageDecoderTest#testInterestingDngs) indicates the ImageDecoder path is one such consumer.",
      "The crafted file must drive an instrumented add, sub, mul, negate or div/rem operation in the DNG SDK into signed or unsigned overflow.",
      "Prior to the fix the library had to be built with the inherited misc_undefined overflow sanitizers, which is the stock configuration in libdng_sdk-defaults."
     ],
     "per_major": {
      "14": "same fix: commit 42e65547eb33 in android-security-14.0.0_r28, identical Android.bp hunk and new ubsan_throwing_runtime.cpp; OWNERS diff differs only in the pre-existing content. Per-major facts: 3/5 lines of the patched region present at android-security-14.0.0_r29; the r29 code context shows the new sanitize/cflags/srcs block and runtime file.",
      "15": "same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16, identical Android.bp hunk and new runtime file; per-major facts report 3/4 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7, identical Android.bp hunk and new runtime file; per-major facts report 3/5 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "same fix: commit 7937f2f835ab (RESTRICT AUTOMERGE) in android-17.0.0_r1 (resolved via base), identical Android.bp hunk and new runtime file; per-major facts report 3/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The change is build configuration plus one new source file and should apply wherever libdng_sdk still has the single line cflags: [\"-DqDNGValidate=0\"] and libdng_sdk-defaults already sets rtti: true and cppflags -fexceptions (both present in the Android 14 context). It relies on Soong honouring sanitize.never as described in the commit's comment; verify at link time that no UBSan runtime is still pulled in on customized branches."
     ],
     "confidence": "high",
     "unknowns": [
      "The specific overflowing expressions in the DNG SDK behind the seven referenced bug IDs are not shown.",
      "Which processes host libdng_sdk decoding of untrusted DNGs (and so which process was crashing) is not shown beyond the ImageDecoder test reference.",
      "Whether all callers of libdng_sdk catch std::runtime_error, or whether an uncaught exception could still terminate a caller, is not shown.",
      "Why the bulletin rates a DoS as Critical is not explained in the material.",
      "Why the per-major region check reports only partial line matches (3/5, 3/4) at the latest tags is not explained."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0052": {
     "cve": "CVE-2026-0052",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error from a handler runtime inside the project, instead of aborting the process through the default ubsan-minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow in dng_sdk arithmetic surfaced as a sanitizer abort (process crash, DoS). The individual overflow sites are not shown in the diff.",
     "root_cause": "The module just before libdng_sdk in Android.bp (lines 138-163, labelled cc_library in the context; presumably libdng_sdk-defaults, which libdng_sdk uses as defaults) enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow (lines 144-149). According to the commit message and the fix comment, that configuration links the ubsan-minimal runtime, which does a hard abort on a detected overflow. The commit message says C++ exceptions are enabled in this library (rtti: true and cppflags -fexceptions at lines 141-142), but the abort leaves no chance for exception-based error handling. Integer overflow in dng_sdk's instrumented arithmetic therefore terminates the hosting process. The commit lists seven bug IDs, which fits several distinct overflow sites that all share this abort behavior. Those sites are not changed or shown.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true }. The in-file comment says this disables the ubsan_minimal runtime so that a custom runtime can be injected.",
      "Android.bp, cc_library libdng_sdk: re-enables the instrumentation by hand with the cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow, and adds -fno-sanitize-link-runtime so that no sanitizer runtime is linked. The existing -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs, so the library provides its own handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow as extern \"C\" with hidden visibility. Each one throws std::runtime_error with a short message instead of aborting.",
      "OWNERS: metadata-only change (bug component, include of the frameworks/base graphics OWNERS, LAST_RESORT_SUGGESTION markers). Not part of the security fix."
     ],
     "completeness": [
      "The fix changes the failure mode across the whole library: a detected signed or unsigned add/sub/mul/negate/divrem overflow in libdng_sdk now throws instead of aborting. It does not fix any of the underlying overflow computations.",
      "Only five overflow handlers are provided, matching the two integer-overflow sanitizer classes that the new cflags enable.",
      "The handlers have hidden visibility and are compiled only into libdng_sdk. The misc_undefined setting in the preceding module block (lines 144-149) is unchanged, so any other module that uses those defaults (none shown in the material) is not covered by this change.",
      "The material does not show whether dng_sdk itself or its callers catch the thrown std::runtime_error. If nothing catches it, the result is still a crash via std::terminate. The Test line (ImageDecoderTest#testInterestingDngs) shows that Google tested the ImageDecoder DNG path."
     ],
     "behavior_changes": [
      "Detected integer overflows inside libdng_sdk now raise std::runtime_error with messages such as \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" and \"ubsan: divrem-overflow\" instead of aborting the process.",
      "libdng_sdk no longer links the ubsan-minimal runtime. Signed and unsigned integer-overflow instrumentation is still enabled through cflags.",
      "Malformed DNG inputs that previously crashed the hosting process should now reach the caller as an error, provided the exception is caught upstream."
     ],
     "preconditions": [
      "A process must decode an attacker-supplied DNG image through a path that uses libdng_sdk. The commit's Test line points to the ImageDecoder DNG path (ImageDecoderTest#testInterestingDngs).",
      "The input must make at least one of the sanitized integer operations in dng_sdk overflow. The specific sites are not shown in the material.",
      "The index results for frameworks/base DngCreator and frameworks/av img_utils relate to DNG writing via TiffWriter and do not establish a precondition for this CVE (per index search)."
     ],
     "per_major": {
      "14": "Same fix: commit 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and runtime-file diff matches 15 and 16 (same Android.bp blob ids 1978b39..3d95a7b); only the OWNERS hunk differs. The code context at android-security-14.0.0_r29 shows the sanitize block, the cflags and the srcs entry. The resolver reports 3/5 lines of the patched region present.",
      "15": "Same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16, with an identical Android.bp and runtime diff. Only the OWNERS hunk differs.",
      "16": "Same fix: commit 74a5a91d169a in android-security-16.0.0_r7, with an identical Android.bp and runtime diff. Only the OWNERS hunk differs.",
      "17": "Adapted fix: commit 81ded662eee0 (one of the Cherrypick-From sources, resolved by related bug) in android-17.0.0_r1. The runtime file is the same. In Android.bp, ubsan_throwing_runtime.cpp is added to the end of an existing srcs list (hunk at line 220) instead of in a new srcs block. No OWNERS change is included."
     },
     "backport_notes": [
      "Single commit. The throwing handlers rely on C++ exceptions being enabled for the library (cppflags -fexceptions at line 141-142 on 14). On branches where the libdng_sdk module already has its own srcs list (as on 17), add ubsan-replacement/ubsan_throwing_runtime.cpp to that list instead of adding a second srcs property.",
      "No 'Fix in fork' comparison was provided in the material, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "Which dng_sdk source locations overflow for the seven referenced bugs. The diff changes no parsing code.",
      "Whether dng_sdk or its callers (for example the Skia DNG codec or ImageDecoder) catch std::runtime_error, so that the new exception becomes a clean decode failure rather than std::terminate.",
      "Which processes load libdng_sdk and are therefore the ones whose crash the bulletin rates as a Critical DoS.",
      "The name of the module block at lines 138-163 and whether other modules use it as defaults and still abort on overflow.",
      "Which UBSan handler variants the compiler emits under these flags (recoverable or abort)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0080": {
     "cve": "CVE-2026-0080",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime in ubsan-replacement/ubsan_throwing_runtime.cpp) instead of aborting the process through the ubsan-minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and sanitizer runtime",
     "weakness": "Integer overflow in DNG processing arithmetic surfaced as a sanitizer-triggered process abort (denial of service); the overflow sites themselves are not shown in the diff",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow, which instruments the arithmetic and uses the ubsan-minimal runtime. Per the commit title and Android.bp comment, a detected overflow led to a hard abort, even though the library is built with -fexceptions and rtti. A DNG input that causes an instrumented overflow inside libdng_sdk could therefore abort the hosting process. The commit lists seven bug IDs; their details are not shown.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so Soong no longer applies its sanitizer configuration or the ubsan_minimal runtime for this module. The comment in the diff describes this as a workaround because Soong has no dedicated flag.",
      "Android.bp, cc_library libdng_sdk: adds cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow to keep the overflow instrumentation, plus -fno-sanitize-link-runtime so no sanitizer runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs. On Android 14/15/16 this is a new srcs block; on Android 17 the file is appended to the existing source list.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines hidden-visibility extern \"C\" __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short message. The module is compiled with cppflags -fexceptions (libdng_sdk-defaults, Android.bp line 142), so the throw can unwind as an exception. Where it is caught is not shown.",
      "OWNERS (14/15/16 only): ownership metadata update with no functional effect."
     ],
     "completeness": [
      "The fix does not correct any specific overflowing computation. It changes the consequence of each detected signed/unsigned overflow in libdng_sdk from an abort to a thrown exception. The overflow sites remain in the code.",
      "Only five handlers are provided (add, sub, mul, negate, divrem overflow), matching the two overflow sanitizers enabled by the new -fsanitize flag.",
      "The thrown type is std::runtime_error, not a dng_sdk exception type. The material does not show whether every caller path catches std::exception (or uses catch(...)).",
      "The change is scoped to the libdng_sdk cc_library. libdng_sdk-defaults still carries sanitize.misc_undefined for both overflow checks (Android.bp lines 144-149). Whether other modules use those defaults is not shown.",
      "The handlers are declared without parameters, so the diagnostic data the compiler passes is ignored and the overflow location is not logged."
     ],
     "behavior_changes": [
      "On an instrumented integer overflow inside libdng_sdk, a std::runtime_error (\"ubsan: add-overflow\" etc.) is thrown instead of the process aborting. The outcome depends on whether a caller catches it.",
      "Build-level: libdng_sdk is no longer sanitized through Soong's sanitize property. The instrumentation now comes from explicit cflags and the handlers from an in-tree source file."
     ],
     "preconditions": [
      "A process that links libdng_sdk decodes untrusted DNG data. The commit's test is ImageDecoderTest#testInterestingDngs, which points to the ImageDecoder DNG path.",
      "The input must drive an instrumented add/sub/mul/negate/divrem operation in libdng_sdk into signed or unsigned overflow.",
      "Per index search, the only results were the frameworks/base DngCreator JNI and frameworks/av img_utils DngUtils. These are DNG writers and not clearly consumers of libdng_sdk, so the material does not establish which production code calls the library."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28 (cherry-pick, matched by change_id). Android.bp changes and new runtime file are identical, plus an OWNERS update. Code context at android-security-14.0.0_r29 shows the patched Android.bp block and runtime file, while the resolver reports 3/5 lines of the patched region present.",
      "15": "same fix: 1ec41401b3d4 in android-security-15.0.0_r16 (change_id). Android.bp and runtime file are identical to 14; the OWNERS hunk differs only in the pre-existing content. Resolver: 3/4 lines present at r17.",
      "16": "same fix: 74a5a91d169a in android-security-16.0.0_r7 (change_id). Android.bp and runtime file are identical to 14; the OWNERS hunk differs only in pre-existing content. Resolver: 3/5 lines present at r8.",
      "17": "adapted fix: 81ded662eee0 in android-17.0.0_r1 (matched by related_bug; listed as a Cherrypick-From source). The Android.bp layout differs: the hunk is at line 105 and the new source is appended to the existing srcs list instead of a new srcs block. There is no OWNERS change, and the runtime file is identical. Resolver: 5/6 lines present."
     },
     "backport_notes": [
      "Apply all four pieces together: sanitize.never, the explicit -fsanitize cflags, -fno-sanitize-link-runtime, and the new ubsan_throwing_runtime.cpp source. The module must keep -fexceptions (from libdng_sdk-defaults) so the throw is usable."
     ],
     "confidence": "high",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow for the inputs tracked by the seven listed bugs; the diff touches no parser code.",
      "Where up the call stack the std::runtime_error is caught, and whether all consumers catch std::exception/catch(...) rather than only dng_sdk-specific exception types.",
      "Whether other modules consume libdng_sdk-defaults and therefore still abort on overflow.",
      "How the build behaves if only some of the Android.bp pieces are applied.",
      "The meaning of the resolver's partial line counts (e.g. 3/5 on Android 14) given that the code context shows the patched region."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0097": {
     "cve": "CVE-2026-0097",
     "summary": "In the LE Secure Connections Passkey Display and Key Notification association models, the fix makes the legacy SMP layer wait for both the local user's approval (SMP_SecurityGrant) and the remote device's passkey confirmation before emitting SMP_KEY_READY_EVT. It treats a user rejection as an authentication failure, cancels LE pairing on authentication timeout, and sets the default LE pairing flags (is_le_only/is_le_nc) in btif for passkey notification.",
     "component": "platform/packages/modules/Bluetooth: system/stack/smp (Security Manager Protocol), system/stack/btm/btm_sec.cc, system/btif/src/btif_dm.cc",
     "weakness": "Missing authorization check: in the passkey display / key notification models, LE pairing key generation went ahead without waiting for local user consent",
     "root_cause": "In system/stack/smp/smp_keys.cc, smp_process_stk always emitted SMP_KEY_READY_EVT (SMP_KEY_TYPE_STK) as soon as the key was computed. For the SMP_MODEL_SEC_CONN_PASSKEY_DISP and SMP_MODEL_KEY_NOTIF models, no state tied this step to the local user's approval. SMP_SecurityGrant had no handling for these models, so the user's accept or reject did not gate the SMP state machine. Also, btif_dm_ble_key_notif_evt did not set pairing_cb.is_le_only / is_le_nc. In BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, btm_sec_pairing_timeout did not cancel an active LE SMP pairing, so a timeout did not tear that pairing down.",
     "fix_analysis": [
      "system/stack/smp/smp_int.h: tSMP_CB gains a passkey_display_state struct with two booleans: approved (the user approved the pairing) and confirmed (the remote device confirmed the passkey).",
      "system/stack/smp/smp_keys.cc:smp_process_stk: after smp_mask_enc_key, if selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF, it sets passkey_display_state.confirmed = true and stores the masked key in p_cb->tk. If passkey_display_state.approved is not yet set, it logs 'Waiting for user to approve pairing' and returns early. Otherwise (other models, or already approved) it emits SMP_KEY_READY_EVT with SMP_KEY_TYPE_STK as before.",
      "system/stack/smp/smp_api.cc:SMP_SecurityGrant: new early branch when bd_addr matches smp_cb.pairing_bda and the model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF. If res == SMP_SUCCESS, it sets approved = true. If confirmed is already set, it then emits SMP_KEY_READY_EVT with key_type SMP_KEY_TYPE_TK and p_data = smp_cb.tk.data(); if not, it only logs and waits. For any other res, it emits SMP_AUTH_CMPL_EVT with status SMP_PAIR_AUTH_FAIL. The branch returns before the pre-existing handling, which begins with the SMP_CONSENT_REQ_EVT check. Logging now includes bd_addr, res, br_state, cb_evt, pairing_bda and the association model.",
      "system/stack/btm/btm_sec.cc:btm_sec_pairing_timeout: in case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, if pairing_flags has BTM_PAIR_FLAGS_LE_ACTIVE, it calls SMP_PairCancel(p_cb->pairing_bda). When SMP is not idle for that address, SMP_PairCancel raises SMP_AUTH_CMPL_EVT with SMP_PAIR_FAIL_UNKNOWN. The code then falls through to the existing BTM_PAIR_STATE_GET_REM_NAME UI notification. The trace level is raised from EVENT to WARNING.",
      "system/btif/src/btif_dm.cc:btif_dm_ble_key_notif_evt: sets pairing_cb.is_le_only = true and pairing_cb.is_le_nc = false before invoking the SSP request callback with BT_SSP_VARIANT_PASSKEY_NOTIFICATION. The commit describes this as initializing the default LE pairing flags."
     ],
     "completeness": [
      "The gating applies only to SMP_MODEL_SEC_CONN_PASSKEY_DISP and SMP_MODEL_KEY_NOTIF. For other association models, smp_process_stk still emits SMP_KEY_READY_EVT immediately.",
      "The diff does not reset passkey_display_state (approved/confirmed) at pairing start or completion. The material does not show whether the control block is cleared elsewhere between pairings.",
      "The deferred path in SMP_SecurityGrant emits SMP_KEY_READY_EVT with key_type SMP_KEY_TYPE_TK, while the direct path in smp_process_stk uses SMP_KEY_TYPE_STK for the same data. The material does not show how the state machine handles this difference.",
      "The commit message lists Flag: com.android.bluetooth.flags.passkey_entry_pairing_approval, but the shown diff has no flag check, so the new behaviour is unconditional in the shown code.",
      "The timeout cancellation applies only to BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with BTM_PAIR_FLAGS_LE_ACTIVE. Other pairing states are unchanged.",
      "On Android 14, SMP_SecurityGrant and SMP_PairCancel have a LOG_ASSERT that the GD shim is not enabled, so there the fix is in the legacy SMP stack."
     ],
     "behavior_changes": [
      "In passkey display / key notification LE pairings, key readiness is deferred until the user approves. If the remote finishes first, the stack logs 'Waiting for user to approve pairing' and waits.",
      "For these models, a user rejection via SMP_SecurityGrant with res != SMP_SUCCESS now fails the pairing with SMP_PAIR_AUTH_FAIL, and the pre-existing SMP_SecurityGrant handling is skipped.",
      "An authentication timeout during an active LE pairing now cancels the SMP pairing (SMP_PairCancel, failure reason SMP_PAIR_FAIL_UNKNOWN) in addition to notifying the UI.",
      "For BLE passkey notification requests, pairing_cb.is_le_only is set to true and is_le_nc to false.",
      "More logging, at higher levels, in SMP_SecurityGrant, smp_process_stk and btm_sec_pairing_timeout."
     ],
     "preconditions": [
      "An LE pairing on the legacy SMP stack in which selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF.",
      "smp_process_stk runs before the local user has responded to the BT_SSP_VARIANT_PASSKEY_NOTIFICATION request raised from btif_dm_ble_key_notif_evt.",
      "Beyond the bulletin's Critical EoP rating, the material does not describe the exact trigger or attacker position."
     ],
     "per_major": {
      "14": "Same fix: combined backport 725df035dfe2 in android-security-14.0.0_r28 (BTM_TRACE_*/SMP_TRACE_* logging, btm_cb, btif_dm_ble_key_notif_evt). 7/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: c18ac65121e1 in android-security-15.0.0_r16. Same logic, using log::verbose/log::warn/log::info, btm_sec_cb.pairing_flags, and tSMP_CB as a class. 7/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix: ce76f82dac2a in android-security-16.0.0_r7, same logic. The btif change is in btif_dm_ble_passkey_notif_evt, and the surrounding code uses std::format. 7/8 lines present at android-security-16.0.0_r8.",
      "17": "Resolved in base (android-17.0.0_r1, commit 57541dad9e1f). Only 3/8 lines of the patched region are present at android-security-17.0.0_r1, so the surrounding code differs on 17. The 17 diff in the material is identical to the 14 diff (legacy macros), so the exact form on 17 is uncertain."
     },
     "backport_notes": [
      "This is one combined cherry-pick of three main CLs (ag/35861798, ag/35914708, ag/35861802), and all five files should land together: smp_keys.cc and smp_api.cc depend on the new smp_int.h field. The commit lists a flag (passkey_entry_pairing_approval), but the backported code has no flag check.",
      "Adapt per branch: btm_cb vs btm_sec_cb for pairing_flags, BTM_TRACE_*/SMP_TRACE_* vs log::* macros, and btif_dm_ble_key_notif_evt vs btif_dm_ble_passkey_notif_evt (Android 16)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether passkey_display_state is reset between pairings (the diff has no reset, and the SMP control-block reset code is not shown).",
      "Why the deferred SMP_KEY_READY_EVT uses SMP_KEY_TYPE_TK while the direct path uses SMP_KEY_TYPE_STK, and how smp_sm_event handles each.",
      "How smp_process_stk is reached in the LE Secure Connections flow (its callers are not shown).",
      "How pairing_cb.is_le_only / is_le_nc affect the processing of the user's reply in btif.",
      "The actual code on Android 17 (android-security-17.0.0_r1 has only 3/8 lines of the patched region, and the 17 diff in the material duplicates the 14 diff).",
      "The concrete trigger and attacker position. The bulletin states only Critical EoP."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21352": {
     "cve": "CVE-2026-21352",
     "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to 2471. The update adds integer-overflow checks around JXL decode/encode buffer sizing and allocations, fixes an off-by-one table index in dng_opcode_MapTable::ReplicateLastEntry, and guards several divisions by zero that occur when blackLevel is 65535.",
     "component": "platform/external/dng_sdk (Adobe DNG SDK: JXL codec glue, opcode processing, memory blocks, safe arithmetic)",
     "weakness": "Integer overflow in buffer size computation leading to undersized allocation / out-of-bounds access; also off-by-one out-of-bounds read and division by zero",
     "root_cause": "Several size and coordinate computations used unchecked arithmetic. In dng_jxl.cpp, jxl_image_chunk_reader and jxl_buffer_chunk_reader built a dng_pixel_buffer from size_t xpos/ypos/xsize/ysize by truncating them to int32 and adding them without overflow checks (fArea.b/r and, in the buffer reader, fArea.t/l). fRowStep was computed with SafeUint32Mult. dng_jxl_decoder::Decode computed fRowStep and the 64-bit bytesNeeded with plain multiplication, and cast the JxlDecoderImageOutBufferSize result to uint32 for host.Allocate without a range check. EncodeJXL_Common computed previewBytesNeeded with plain multiplication. jxl_memory_block added 160 to bytesNeeded without a wrap check and did not check allocator.Malloc for a null return. dng_ref_counted_block::Allocate called malloc(size + sizeof(header)) without a wrap check. In dng_misc_opcodes.cpp, dng_opcode_MapTable::ReplicateLastEntry read table[fCount] instead of table[fCount - 1]. Because fCount is validated to 1..0x10000 and the table is allocated as 0x10000 uint16 entries, fCount == 0x10000 reads one element past the allocation and smaller counts read an unwritten slot. dng_opcode_MapTable::Prepare, dng_opcode_GainMap::ProcessArea, RefVignette32 and RefBaselineMapPoly32 divided by (65535 - blackLevel) or by blackScale2, both of which are zero when blackLevel == 65535. The material does not identify which of these hunks is the one tracked as CVE-2026-21352 (the commit references two bugs, 483697751 and 483693973).",
     "fix_analysis": [
      "source/dng_jxl.cpp:jxl_memory_block ctor: computes mallocSize = 160u + bytesNeeded into a size_t and calls ThrowOverflow() if mallocSize <= bytesNeeded (wrap). It now calls ThrowMemoryFull() if allocator.Malloc returns null, instead of passing the null pointer to DNG_ALIGN_SIMD.",
      "source/dng_jxl.cpp:jxl_image_chunk_reader and jxl_buffer_chunk_reader (get_color_channel_data_at handlers): reject xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\"), compute the fArea bounds with SafeInt32Add, add DNG_REQUIRE checks that fPlanes and fArea.W() are <= 0x7fffffff, and compute fRowStep with SafeInt32Mult instead of SafeUint32Mult. The debug printf format changes to %zu.",
      "source/dng_jxl.cpp:EncodeJXL_Common: adds DNG_REQUIRE fArea.W() <= 0x7fffffff, computes fRowStep with SafeInt32Mult, and computes previewBytesNeeded with 3-arg SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp:dng_jxl_decoder::Decode: adds DNG_REQUIRE basicInfo.xsize/ysize <= 0x7fffffff before assigning to dng_point. buffer.fRowStep and wholeBuffer.fRowStep use SafeInt32Mult, and bytesNeeded uses the new 3-arg SafeInt64Mult. Adds DNG_REQUIRE size <= 0xffffffff before casting the JxlDecoderImageOutBufferSize result to uint32 for host.Allocate.",
      "source/dng_safe_arithmetic.h/.cpp: adds int64 SafeInt64Mult(int64, int64, int64), which chains the two-arg SafeInt64Mult and throws on overflow. Removes the __ANDROID__ special case that forced __USE_BUILTIN_SMULL_OVERFLOW to 0, so Android clang builds now use __builtin_smull_overflow.",
      "source/dng_ref_counted_block.cpp:dng_ref_counted_block::Allocate: computes size + sizeof(header) into a size_t mallocSize and calls ThrowOverflow() if mallocSize <= size, before malloc.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::ReplicateLastEntry: reads lastEntry from table[fCount - 1] instead of table[fCount].",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::Prepare: srcScale is 0.0 when blackLevel >= 65535 instead of dividing by zero.",
      "source/dng_gain_map.cpp:dng_opcode_GainMap::ProcessArea, source/dng_reference.cpp:RefVignette32 and RefBaselineMapPoly32: blackScale1 is 0.0 when blackScale2 == 0.0 instead of 1.0f / 0.",
      "source/dng_flags.h and source/dng_tag_codes.h: build number 2410 -> 2471 and a new tcWebP enum value (non-security bookkeeping)."
     ],
     "completeness": [
      "The diff converts the JXL computations it touches (fArea bounds, fRowStep, bytesNeeded, previewBytesNeeded, the output buffer size cast) to Safe* helpers or explicit range checks. Both chunk reader classes get the same treatment. The rest of those functions is not shown.",
      "jxl_memory_block adds the null check only on the Malloc branch (bytesNeeded > 0x80000000). The else branch relies on allocator.Allocate, whose failure behavior is not shown in the material.",
      "The dng_ref_counted_block::Allocate check (mallocSize <= size) can only trigger where size_t is 32-bit. On 64-bit builds, uint32 size + sizeof(header) cannot wrap.",
      "The MapTable fix changes only the index read. The fCount range validation (1..0x10000) in the constructor was already present and is unchanged.",
      "Division-by-zero guards are applied in four places (MapTable::Prepare, GainMap::ProcessArea, RefVignette32, RefBaselineMapPoly32). Whether other blackLevel-based divisions exist elsewhere in the SDK is not shown.",
      "The commit is a vendor drop (build 2471) that bundles fixes for two bug IDs. The material does not isolate which hunk corresponds to this CVE."
     ],
     "behavior_changes": [
      "JXL images whose xsize or ysize exceeds 0x7fffffff, or whose decoder output buffer size exceeds 0xffffffff, now fail decode with a DNG_REQUIRE exception instead of being processed.",
      "JXL chunk callbacks with coordinates or sizes above 0x7fffffff, or whose area or row-step arithmetic overflows, now throw instead of continuing.",
      "Allocation-size wraparound in jxl_memory_block and dng_ref_counted_block::Allocate now throws ThrowOverflow. A null return from allocator.Malloc in jxl_memory_block now throws ThrowMemoryFull.",
      "MapTable opcodes now replicate the actual last entry (table[fCount-1]) rather than the slot after it. This changes output for tables with fCount < 0x10000.",
      "blackLevel == 65535 now yields a scale of 0.0 instead of a divide-by-zero result in GainMap, Vignette, BaselineMapPoly and MapTable processing.",
      "On Android clang builds, SafeInt64Mult now uses __builtin_smull_overflow instead of the SafeInt64MultSlow fallback.",
      "The SDK build string changes from 2410 to 2471, and tcWebP is added to the tag code enum."
     ],
     "preconditions": [
      "A process using external/dng_sdk parses a DNG/TIFF-family file. The JXL hunks require JXL-compressed image data. The MapTable/GainMap/Vignette hunks require the corresponding opcodes.",
      "The material does not show which Android consumers of external/dng_sdk reach these paths. Per index search, the frameworks/base DngCreator hits use TiffWriter rather than the patched dng_sdk functions, so they are not evidence of reachability."
     ],
     "per_major": {
      "14": "same fix: commit 72d44e39d499 'Update dng_sdk to 1.7.1 2471' in android-security-14.0.0_r28. At android-security-14.0.0_r29, 5/8 patched-region lines are present. The context there shows the code hunks present and the build string at 2502, but the material does not say which 3 lines differ.",
      "15": "same fix: commit 7a737bb68ca6 in android-security-15.0.0_r16, with the same diff. 5/8 patched-region lines are present at android-security-15.0.0_r17; the reason is not shown.",
      "16": "same fix: commit b0d9d2a8d100 in android-security-16.0.0_r7, with the same diff. 5/8 patched-region lines are present at android-security-16.0.0_r8; the reason is not shown.",
      "17": "same fix in base: commit 9a9e97362f2d in android-17.0.0_r1. The content is the same except that the dng_reference.cpp RefBaselineMapPoly32 hunk is at line 3011 (two lines later) because the file base differs (95a6087). 5/8 patched-region lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. Its base is SDK 1.7.1 build 2410 (dng_jxl.cpp with jxl_memory_block and the chunk reader classes; dng_safe_arithmetic.h with the __ANDROID__ SMULL block). The new 3-arg SafeInt64Mult declaration and definition must be carried with the dng_jxl.cpp Decode hunk, which calls it.",
      "On Android 14, android-security-14.0.0_r29 shows build 2502 in dng_flags.h. When verifying presence, check the code hunks rather than the build string."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which specific hunk (JXL size overflow, ref-counted block overflow, MapTable off-by-one, or divide-by-zero) is the one assigned CVE-2026-21352; the commit covers two bug IDs.",
      "Which Android processes use dng_sdk with JXL support enabled and parse untrusted input, and therefore the concrete attack surface behind the Critical EoP rating.",
      "How dng_memory_allocator::Allocate behaves on failure in the non-Malloc branch of jxl_memory_block.",
      "Which 3 of the 8 patched-region lines are absent at the latest security tags of each major.",
      "Whether a build between 2410 and 2471 was ever shipped on any branch."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21353": {
     "cve": "CVE-2026-21353",
     "summary": "Updates external/dng_sdk from build 2410 to 2471, adding overflow-checked arithmetic and range checks to the JXL decode/encode pixel-buffer geometry and allocation sizes in dng_jxl.cpp, an overflow check in dng_ref_counted_block::Allocate, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and division-by-zero guards for blackLevel == 65535 in the GainMap, MapTable, Vignette and BaselineMapPoly paths.",
     "component": "platform/external/dng_sdk (Adobe DNG SDK 1.7.1): JXL codec glue (dng_jxl.cpp), opcode processing (dng_misc_opcodes.cpp, dng_gain_map.cpp, dng_reference.cpp), memory blocks, safe arithmetic helpers",
     "weakness": "Integer overflow / unchecked narrowing in buffer size and stride computation, plus an off-by-one out-of-bounds table read and float division by zero",
     "root_cause": "In source/dng_jxl.cpp the pixel-buffer geometry used for JXL processing was derived from decoder-supplied values without overflow checks: jxl_image_chunk_reader and jxl_buffer_chunk_reader get_color_channel_data_at cast size_t xpos/ypos/xsize/ysize to int32 and added them with plain arithmetic to form fArea, and computed fRowStep with SafeUint32Mult (the fix switches to SafeInt32Mult with 0x7fffffff bounds, suggesting a signed-range concern); dng_jxl_decoder::Decode assigned basicInfo.xsize/ysize to dng_point fields without a range check, computed fRowStep with plain multiplication, computed bytesNeeded with unchecked uint64 multiplication, and cast the JxlDecoderImageOutBufferSize result to uint32 without a range check; jxl_memory_block added 160u to bytesNeeded without overflow check and did not check the Malloc result; EncodeJXL_Common computed fRowStep and previewBytesNeeded with plain int32/uint32 multiplication. Wrapped strides or sizes are the hazard these checks guard against (the downstream pixel addressing is not shown). Separately, dng_opcode_MapTable::ReplicateLastEntry read table[fCount] where the table has 0x10000 entries and fCount may equal 0x10000 (one past the end, and in any case the wrong entry), and several black-level normalisations divided by (1 - blackLevel/65535), which is zero when blackLevel == 65535.",
     "fix_analysis": [
      "source/dng_jxl.cpp:jxl_memory_block::jxl_memory_block: computes mallocSize = 160u + bytesNeeded into a size_t, throws ThrowOverflow if mallocSize <= bytesNeeded, and throws ThrowMemoryFull if allocator.Malloc returns null.",
      "source/dng_jxl.cpp:jxl_image_chunk_reader get_color_channel_data_at: rejects xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\"); computes fArea.b/fArea.r with SafeInt32Add; DNG_REQUIREs fPlanes <= 0x7fffffff and fArea.W() <= 0x7fffffff; computes fRowStep with SafeInt32Mult instead of SafeUint32Mult.",
      "source/dng_jxl.cpp:jxl_buffer_chunk_reader get_color_channel_data_at: same coordinate limit; fArea.t/l/b/r now computed with SafeInt32Add against fBuffer.fArea; same DNG_REQUIREs; fRowStep via SafeInt32Mult. bytesNeeded was already SafeUint32Mult.",
      "source/dng_jxl.cpp:EncodeJXL_Common: DNG_REQUIRE fArea.W() <= 0x7fffffff; fRowStep via SafeInt32Mult; previewBytesNeeded via 3-arg SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp:dng_jxl_decoder::Decode: DNG_REQUIRE basicInfo.xsize/ysize <= 0x7fffffff before assigning to dng_point; buffer.fRowStep and wholeBuffer.fRowStep via SafeInt32Mult; bytesNeeded via new 3-arg SafeInt64Mult; DNG_REQUIRE size <= 0xffffffff before the (uint32) cast passed to host.Allocate.",
      "source/dng_safe_arithmetic.h/.cpp: adds int64 SafeInt64Mult(int64, int64, int64) (chained 2-arg calls) used by Decode; removes the __ANDROID__ special case that forced __USE_BUILTIN_SMULL_OVERFLOW to 0, so Android clang builds use the __builtin_smull_overflow path (SafeInt64MultByClang) instead of SafeInt64MultSlow.",
      "source/dng_ref_counted_block.cpp:dng_ref_counted_block::Allocate: computes size + sizeof(header) into size_t mallocSize and throws ThrowOverflow if mallocSize <= size before malloc.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::ReplicateLastEntry: reads table[fCount - 1] instead of table[fCount]; the constructor already rejects fCount == 0 and fCount > 0x10000, so the index is in range.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::Prepare, source/dng_gain_map.cpp:dng_opcode_GainMap::ProcessArea, source/dng_reference.cpp:RefVignette32 and RefBaselineMapPoly32: guard the division so that blackLevel == 65535 yields a scale of 0.0 instead of dividing by zero.",
      "source/dng_flags.h: build version 2410 -> 2471; source/dng_tag_codes.h: appends tcWebP to the end of the enum (no evident security effect)."
     ],
     "completeness": [
      "All JXL pixel-buffer stride and size computations visible in the diff (two chunk readers, EncodeJXL_Common, both buffers in Decode) now use Safe* arithmetic or explicit upper bounds.",
      "The chunk-reader checks only prevent integer wrap; whether the resulting fArea is validated against the destination image/buffer bounds is not shown in the material.",
      "In dng_ref_counted_block::Allocate, size is uint32 and sizeof(header) is size_t, so the new check can only trigger on 32-bit builds; on 64-bit the addition cannot wrap.",
      "The jxl_memory_block check compares the size_t mallocSize against the uint64 bytesNeeded, which catches both wrap and 32-bit truncation for the >2 GB path.",
      "Division-by-zero guards are applied to all four black-level sites shown (GainMap, MapTable::Prepare, RefVignette32, RefBaselineMapPoly32); no other unguarded instance is visible in the material.",
      "The commit bundles several upstream changes; the material does not identify which of them is the CVE-2026-21353 issue (two bug IDs are referenced)."
     ],
     "behavior_changes": [
      "JXL content with chunk coordinates or image dimensions above 0x7fffffff, or whose stride/size products overflow, now aborts with a dng_exception (ThrowOverflow / DNG_REQUIRE failure) instead of proceeding.",
      "jxl_memory_block now throws ThrowMemoryFull on a null Malloc result for allocations above 2 GB.",
      "With blackLevel == 65535, GainMap/RefVignette32/RefBaselineMapPoly32 now use blackScale1 = 0.0 (blackOffset1 = 1.0) and MapTable::Prepare uses srcScale = 0.0, instead of dividing by zero.",
      "MapTable replication now copies the actual last entry (table[fCount-1]) into entries fCount..0xFFFF.",
      "SafeInt64Mult on Android clang builds now uses the __builtin_smull_overflow implementation.",
      "Reported SDK build string changes to 2471 (the latest security tags show 2502)."
     ],
     "preconditions": [
      "Processing of an attacker-controlled DNG/TIFF-style file through dng_sdk; the dng_jxl.cpp changes require JXL-compressed image data handled by dng_jxl_decoder::Decode or the chunk readers, the opcode changes require a MapTable or GainMap/Vignette/BaselineMapPoly opcode in the file's opcode lists.",
      "Which Android process links dng_sdk and feeds it untrusted files is not shown in the material; the index matches in frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp are DNG writer code using TiffWriter and their relevance to the patched paths is unclear (per index search)."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 72d44e39d499 in android-security-14.0.0_r28, the full 1.7.1 build 2471 update; at android-security-14.0.0_r29, 5/8 lines of the patched region are present and dng_flags.h shows build 2502.",
      "15": "same fix: 7a737bb68ca6 in android-security-15.0.0_r16, identical diff; at android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "same fix: b0d9d2a8d100 in android-security-16.0.0_r7, identical diff; at android-security-16.0.0_r8, 5/8 lines of the patched region are present.",
      "17": "same fix in base: 9a9e97362f2d in android-17.0.0_r1, identical changes; only the dng_reference.cpp blob hashes and the RefBaselineMapPoly32 hunk offset differ (3011 vs 3009); android-security-17.0.0_r1 shows 5/8 lines of the patched region present."
     },
     "backport_notes": [
      "This is a whole-SDK drop (2410 -> 2471), applied identically on all four branches; the dng_jxl.cpp Decode change depends on the new 3-arg SafeInt64Mult in dng_safe_arithmetic.h/.cpp, so take the safe-arithmetic hunks together with dng_jxl.cpp."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which specific change in the bundled update corresponds to CVE-2026-21353 (the commit references bugs 483697751 and 483693973 without mapping them to hunks).",
      "Which Android component/process consumes dng_sdk's JXL decode or opcode paths with untrusted input.",
      "Whether code after the chunk readers validates the computed fArea against the destination image bounds.",
      "The declared type of dng_pixel_buffer::fRowStep and how wrapped strides were used downstream.",
      "Which 3 of the 8 patched-region lines are absent at the latest security tags, and what changed between build 2471 and 2502.",
      "Whether trees older than build 2410 contain dng_jxl.cpp."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-26418": {
     "cve": "CVE-2025-26418",
     "summary": "Adds a MANAGE_USERS-or-INTERACT_ACROSS_USERS permission check to CarDevicePolicyService.setUserDisclaimerAcknowledged and a matching @RequiresPermission annotation on CarDevicePolicyManager. Callers without either permission can no longer have the car service acknowledge the new-user disclaimer.",
     "component": "platform/packages/services/Car, CarDevicePolicyService (car admin / device policy)",
     "weakness": "missing permission check (confused deputy via Binder service)",
     "root_cause": "ICarDevicePolicyService.setUserDisclaimerAcknowledged(int userId) in service/src/com/android/car/admin/CarDevicePolicyService.java did not check the caller's permissions. For any userId the caller passed in, it set the internal disclaimer status to NEW_USER_DISCLAIMER_STATUS_ACKED, cancelled the user disclaimer notification and called DevicePolicyManager.acknowledgeNewUserDisclaimer() through the car service's own context (mContext.createContextAsUser(user, 0)). Per index search, DevicePolicyManagerService.acknowledgeNewUserDisclaimer itself requires canManageUsers or INTERACT_ACROSS_USERS. That check applies to the car service as the caller, so other apps could acknowledge the disclaimer indirectly through the car service. The commit message describes this as 'previously allowed other apps to call it indirectly'.",
     "fix_analysis": [
      "service/src/com/android/car/admin/CarDevicePolicyService.java:setUserDisclaimerAcknowledged: calls a new private enforcePermission() as its first statement, before any state change, notification cancellation or DPM call.",
      "service/src/com/android/car/admin/CarDevicePolicyService.java:enforcePermission (new): calls mContext.checkCallingOrSelfPermission for MANAGE_USERS and for INTERACT_ACROSS_USERS. If neither is PERMISSION_GRANTED, it throws SecurityException(\"Requires MANAGE_USERS or INTERACT_ACROSS_USERS permissions\"). Per index search, this is the same permission set that DevicePolicyManagerService.acknowledgeNewUserDisclaimer enforces.",
      "car-lib/src/android/car/admin/CarDevicePolicyManager.java:setUserDisclaimerAcknowledged(UserHandle): adds @RequiresPermission(anyOf = {MANAGE_USERS, INTERACT_ACROSS_USERS}). This is an annotation only and adds no runtime check in the client library.",
      "One commit touching two files: 16 lines added, none removed."
     ],
     "completeness": [
      "Covers the Binder entry point setUserDisclaimerAcknowledged.",
      "The sibling Binder method setUserDisclaimerShown(int userId) in the same file still has no permission check (lines 215-218 of the post-fix code). In the code shown, it only writes the internal disclaimer status via setUserDisclaimerStatus. It does not call DPM or cancel notifications.",
      "The check only tests for the two permissions. It does not check that userId is a valid user, and it does not limit which user a caller holding either permission may target.",
      "The material does not show whether the AIDL interface or SELinux also restricts who can reach ICarDevicePolicyService."
     ],
     "behavior_changes": [
      "Callers of CarDevicePolicyManager.setUserDisclaimerAcknowledged or ICarDevicePolicyService.setUserDisclaimerAcknowledged that hold neither MANAGE_USERS nor INTERACT_ACROSS_USERS now get a SecurityException over Binder, and the disclaimer is not acknowledged.",
      "Callers holding either permission see no change.",
      "The new @RequiresPermission annotation on the @hide method may produce lint warnings for callers that lack the permission."
     ],
     "preconditions": [
      "An automotive device running the car service (packages/services/Car) with CarDevicePolicyService registered.",
      "The caller can reach ICarDevicePolicyService (for example through CarDevicePolicyManager) and call setUserDisclaimerAcknowledged with a userId of its choice. Before the fix, this method checked no permission.",
      "Per index search, the legitimate caller is NewUserDisclaimerActivity in packages/apps/Car/Settings, which uses CarDevicePolicyManager."
     ],
     "per_major": {
      "14": "same fix: commit 6afaae7b4be4 in android-security-14.0.0_r28 (Cherrypick-From 275d46a39674, Change-Id Ic4b8ed6037a0ca6fe5d7bc8faf74d3b33eb49a27). All 8 patched lines are present at android-security-14.0.0_r29.",
      "15": "same fix: commit 20d4c410ce12 in android-security-15.0.0_r16. The service change is identical. The manager hunk differs only because 15's CarDevicePolicyManager has no @AddedInOrBefore(majorVersion = 33) line there. All 8 lines are present at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin (Android 16 is not listed). The patched region (all 8 lines) is present at android-security-16.0.0_r8.",
      "17": "same fix: commit 91c7ed0c3871 in android-17.0.0_r1 (base). All 8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "One self-contained commit: add the enforcePermission() call and the new method to CarDevicePolicyService.setUserDisclaimerAcknowledged. Make sure in-tree callers (for example Car Settings NewUserDisclaimerActivity, per index search) hold MANAGE_USERS or INTERACT_ACROSS_USERS, or acknowledging the disclaimer will start failing with SecurityException."
     ],
     "confidence": "high",
     "unknowns": [
      "Why the bulletin does not list Android 16 even though the patched region is present at android-security-16.0.0_r8.",
      "Whether the Car Settings caller holds one of the required permissions on all affected builds.",
      "Whether access to ICarDevicePolicyService is otherwise restricted (SELinux, Car API gating) in the affected releases.",
      "Whether other methods in CarDevicePolicyService call DevicePolicyManager using the service's identity (the full file is not shown)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32348": {
     "cve": "CVE-2025-32348",
     "summary": "Stops treating a process whose only visible activity is in pinned (PIP) windowing mode as \"has a visible window / foreground task\" for background activity launch (BAL) exemptions. It does this by adding VisibleActivityProcessTracker.hasVisibleNotPinnedActivity and an inPinnedWindow parameter to BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed.",
     "component": "platform/frameworks/base, WindowManager / ActivityTaskManager background activity launch (BAL) control",
     "weakness": "Overly broad exemption in an authorization check (visible-window BAL exemption granted to pinned/PIP-only processes)",
     "root_cause": "BackgroundActivityStartController computed callingUidHasAnyVisibleWindow / realCallingUidHasAnyVisibleWindow via mService.hasActiveVisibleWindow(uid). Per index, ActivityTaskManagerService.hasActiveVisibleWindow returns true when mVisibleActivityProcessTracker.hasVisibleActivity(uid) or mActiveUids.hasNonAppVisibleWindow(uid) is true. hasVisibleActivity matched any visible process of the uid, including one whose activity is in pinned (picture-in-picture) windowing mode, so such a caller got BAL_ALLOW_VISIBLE_WINDOW. Likewise, BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed returned BAL_ALLOW_FOREGROUND whenever hasActivityInVisibleTask was true, without checking the process's windowing mode. A pinned-window process therefore also qualified via the \"activity in foreground task\" path, so a PIP window enabled background activity starts.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java: adds hasVisibleNotPinnedActivity(int uid), which calls match(uid, wpc -> !wpc.inPinnedWindowingMode()). hasVisibleActivity now passes an ALWAYS_TRUE predicate, and match() no longer accepts a null predicate (predicate.test(wpc) is unconditional).",
      "services/core/java/com/android/server/wm/BackgroundActivityStartController.java (region computing callingUidHasAnyVisibleWindow): replaces mService.hasActiveVisibleWindow(callingUid) with mService.mVisibleActivityProcessTracker.hasVisibleNotPinnedActivity(callingUid) || mService.mActiveUids.hasNonAppVisibleWindow(callingUid). The same substitution is made for realCallingUidHasAnyVisibleWindow when callingUid != realCallingUid. Non-app visible windows (e.g. live wallpaper) keep their exemption; only the activity-based part now excludes pinned processes.",
      "services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java:areBackgroundActivityStartsAllowed: adds a boolean parameter inPinnedWindow after hasActivityInVisibleTask. The foreground-task exemption becomes (isCheckingForFgsStart || !inPinnedWindow) && hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW. A pinned-window process therefore no longer gets BAL_ALLOW_FOREGROUND for activity starts, but still does when the check is for a foreground service start. The app-switch condition is rewritten from (== APP_SWITCH_ALLOW || == APP_SWITCH_FG_ONLY) to != APP_SWITCH_DISALLOW, with the import changed to match; the Android 15 base already used != APP_SWITCH_DISALLOW.",
      "services/core/java/com/android/server/wm/WindowProcessController.java:areBackgroundActivityStartsAllowed(int, boolean): passes inPinnedWindowingMode() as the new argument.",
      "services/tests/wmtests/src/com/android/server/wm/VisibleActivityProcessTrackerTests.java: new tests asserting that a WINDOWING_MODE_PINNED process is reported by hasVisibleActivity but not by hasVisibleNotPinnedActivity.",
      "Follow-up on Android 14: commits 58a95019e245 (android-security-14.0.0_r24) and 4e7b14350ece (android-security-14.0.0_r25), both with Change-Id Iff954bf96857cdf5cafdc7bcf28908c1c6cb20a7, fully revert the change, citing an app compatibility regression. The code shown at android-security-14.0.0_r29 nevertheless contains the fixed logic: hasVisibleNotPinnedActivity in BackgroundActivityStartController, ALWAYS_TRUE in the tracker, the inPinnedWindowingMode() argument in WindowProcessController, and the APP_SWITCH_DISALLOW import. The test file at r29 also has extra imports (doReturn, when). This points to a later re-land whose commit is not in the material."
     ],
     "completeness": [
      "Covers both the callingUid and realCallingUid visible-window exemptions in BackgroundActivityStartController and the foreground-task exemption in BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed.",
      "By design, the foreground-task exemption still applies to pinned processes when isCheckingForFgsStart is true (the WindowProcessController.areBackgroundFgsStartsAllowed path). Pinned windows therefore still count toward permission to start a foreground service from the background.",
      "The pinned check is per process (WindowProcessController.inPinnedWindowingMode()), not per activity. The material does not show how a process with both a pinned and a non-pinned visible activity is classified.",
      "Per index, ActivityTaskManagerService.hasActiveVisibleWindow itself is unchanged and still uses hasVisibleActivity. Only the BAL controller call sites were switched to the not-pinned variant, so other users of hasActiveVisibleWindow keep the old behavior.",
      "On Android 15 the fix also covers both branches of the real-caller check (balImproveRealCallerVisibilityCheck true and false). The 15 metrics call now passes mCallingUidHasVisibleActivity / mRealCallingUidHasVisibleActivity (hasVisibleActivity only, which still counts pinned activities but no longer counts non-app visible windows)."
     ],
     "behavior_changes": [
      "An app whose only visible activity is in picture-in-picture (pinned) mode is now blocked (BAL_BLOCK) on the visible-window and foreground-task paths, unless another exemption applies. The commit was reverted once on Android 14 for an app compatibility regression, so PIP apps that start activities will see a change.",
      "BAL verdict messages: on 16, the caller and real-caller messages become \"callingUid has visible non-pinned window\" / \"realCallingUid has visible non-pinned window\". On 15, only the caller message changes. On 15 and 16, BalState.toString gains the callingUidHasVisibleNotPinnedActivity and realCallingUidHasVisibleNotPinnedActivity fields.",
      "VisibleActivityProcessTracker.match no longer tolerates a null predicate. It would throw a NullPointerException if any other caller passed null; no such caller is shown in the material.",
      "The signature of BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed changes (new inPinnedWindow parameter), which affects any out-of-tree callers or tests."
     ],
     "preconditions": [
      "An activity start (or PendingIntent send) that goes through the BAL check in BackgroundActivityStartController, from a caller not otherwise exempt (not the ROOT/SYSTEM/NFC uid, the home app, the IME, a persistent process, or another exemption).",
      "The calling (or real calling) uid has a process whose visible activity is in pinned (PIP) windowing mode, and no other visible non-pinned activity or non-app visible window.",
      "App switch state is not APP_SWITCH_DISALLOW.",
      "For the BackgroundLaunchProcessController path: WindowProcessController.areBackgroundActivityStartsAllowed is invoked with isCheckingForFgsStart false. Per index, the FGS variant areBackgroundFgsStartsAllowed is intentionally not restricted."
     ],
     "per_major": {
      "14": "Same fix (RESTRICT AUTOMERGE cherry-pick 4ec3e4cea49d) in android-security-14.0.0_r22. Fully reverted in r24 (58a95019e245) and r25 (4e7b14350ece) for an app compatibility regression. The code at android-security-14.0.0_r29 shows the fixed logic present again (7/8 patched lines); the re-land commit is not in the material.",
      "15": "Adapted fix (483fba0b4777, RESTRICT AUTOMERGE) in android-security-15.0.0_r10. BalState gains mCallingUidHasVisibleNotPinnedActivity / mRealCallingUidHasVisibleNotPinnedActivity, and both the flagged and unflagged real-caller checks use the not-pinned variant. inPinnedWindow is added to BackgroundLaunchProcessController, a BalVerdict(int,String) constructor is added, and many tests are updated. 7/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix (b96d7a515e9e, not RESTRICT AUTOMERGE) in android-security-16.0.0_r1. Same BalState changes. The foreground-task exemption is gated on checkConfiguration.isCheckingForFgsStart || !inPinnedWindow, together with checkConfiguration.checkOtherExemptions. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Unclear. The resolver lists 17 as resolved (base, android-17.0.0_r1), but the linked commit 58822cb0d7e4 is itself a Revert of \"Ignore pinned Windows\". It touches ActivityStarter.java and a boolean-returning BackgroundLaunchProcessController, a code shape unlike 14/15/16, and the patched region is 0/8 present at android-security-17.0.0_r1. The material does not show whether 17 carries the fix in another form."
     },
     "backport_notes": [
      "The change has an app compatibility history: reverted on Android 14 at r24/r25, then present again at r29 with a test file that differs from the original. Align a backport with the android-security-14.0.0_r29 code rather than the original commit, and expect PIP apps that start activities to be affected.",
      "Apply as one unit: the VisibleActivityProcessTracker change removes null-predicate support in match(), and the BackgroundLaunchProcessController signature change must be mirrored in WindowProcessController.areBackgroundActivityStartsAllowed and in any tests that call it (on 15/16, BackgroundLaunchProcessControllerTests pass mInPinnedWindoMode)."
     ],
     "confidence": "medium",
     "unknowns": [
      "The commit that re-landed the fix on Android 14 after the r24/r25 reverts, and what was changed to address the app compatibility regression.",
      "The actual fix state on Android 17: the resolver says resolved, but only a revert commit is linked and the patched region is absent at android-security-17.0.0_r1.",
      "Which of the 8 patched-region lines differs on 14 r29 and 15 r17 (7/8 present).",
      "The exact enclosing method name in BackgroundActivityStartController for the Android 14 hunk (the hunk header shows only the class).",
      "The bulletin lists the type as DoS; the material does not show the concrete impact scenario."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48570": {
     "cve": "CVE-2025-48570",
     "summary": "The fix adds a 3-second check to PipTaskOrganizer#onTaskAppeared in WM Shell: if no PiP-entering transition state change follows a pinned task appearing (the commit attributes this to a SystemUI reboot), tasks whose top activity matches that component are removed.",
     "component": "platform/frameworks/base, WindowManager Shell PiP (libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java)",
     "weakness": "Stale state not cleaned up after a component restart: an orphaned pinned (PiP) task left behind by a SystemUI reboot. The fix's test line links this to background Activity launch.",
     "root_cause": "When SystemUI restarts, PipTaskOrganizer#onTaskAppeared can get a pinned task with no PiP enter transition after it. Before the fix, nothing detected or removed such a stale pinned task, so it stayed in place. The commit's test line (\"make sure no background Activity launch\") ties this state to a background Activity launch.",
     "fix_analysis": [
      "PipTaskOrganizer.java adds CRASH_RECOVERY_CHECK_DELAY_MS = 3000 and a field, mRemoveStaledPinnedTaskRunnable.",
      "PipTaskOrganizer#onTaskAppeared builds a runnable and posts it with mMainExecutor.executeDelayed after 3000 ms. The runnable reads info.topActivity, calls ActivityTaskManager.getService().getTasks(10, false, false, Display.DEFAULT_DISPLAY), and calls removeTask(taskId) on every task whose topActivity matches. A RemoteException is logged.",
      "The PipTaskOrganizer constructor registers an OnPipTransitionStateChangedListener on mPipTransitionState. When mPipTransitionState.isEnteringPip() is true, it removes the pending runnable from mMainExecutor and clears it, so a normal PiP entry cancels the cleanup.",
      "Commit history: original 670f2df6bf5e (android-security-14.0.0_r24); revert 94929458bc26 (reason b/458676344); reland c0f88bec3bf0, which adds null checks on info and info.topActivity (addressing the NPE in b/459832318), returns early if null, and logs the component. The reland is the final form."
     ],
     "completeness": [
      "The cleanup is a timeout heuristic. It only fires if no transition state reaches isEnteringPip within 3 s.",
      "Only the first 10 tasks returned by getTasks for Display.DEFAULT_DISPLAY are checked. A stale pinned task on another display, or outside those 10, is not removed.",
      "Tasks are matched by topActivity ComponentName, not by task id or windowing mode. Any non-PiP task with the same top activity among those 10 would also be removed.",
      "If onTaskAppeared runs again before the timeout, the field is overwritten without cancelling the earlier posted runnable, so that one still runs.",
      "The fix is entirely in WM Shell. No change to system_server task or activity-start code appears in this material."
     ],
     "behavior_changes": [
      "About 3 s after a pinned task appears with no PiP-entering transition state change following, the shell removes tasks whose top activity matches that component and logs \"SystemUI reboot detected, remove staled PiP task %s\". Nothing is removed if info or info.topActivity is null.",
      "Extra binder calls (getTasks and removeTask) from SystemUI to ActivityTaskManager."
     ],
     "preconditions": [
      "A pinned task reaches PipTaskOrganizer#onTaskAppeared with no PiP-entering transition following (the commit attributes this to a SystemUI reboot).",
      "Code path: com.android.wm.shell.pip.PipTaskOrganizer in WM Shell."
     ],
     "per_major": {
      "14": "Fixed. Original commit 670f2df6bf5e in android-security-14.0.0_r24; its revert (94929458bc26) and the reland with null checks (c0f88bec3bf0) are both in android-security-14.0.0_r28. All 8 lines of the patched region are present at r29.",
      "15": "Not affected per the bulletin (not listed). All 8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Not affected per the bulletin (not listed). All 8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Not affected per the bulletin (not listed). All 8 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Backport the reland c0f88bec3bf0 (with the info/topActivity null checks), not the original 670f2df6bf5e. The original was reverted (b/458676344), and the reland addresses an NPE (b/459832318). If the original is already applied, apply the revert first and then the reland."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact attack trigger and how the stale pinned task leads to a background activity launch.",
      "The content of b/458676344, the reason for the revert.",
      "Why Android 15 to 17 are not affected, given that the patched-region lines are present there.",
      "Whether any fix was also made on the server (system_server) side."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-opus-5-5",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48595": {
     "cve": "CVE-2025-48595",
     "summary": "Google fixed CVE-2025-48595 by moving the bundled SQLite library to a patched upstream point release instead of applying a single targeted patch. On Android 14 that meant 3.42.0 to 3.42.1, after a first 3.39.6 update was reverted. On Android 15 and 16 it meant RELEASE_PACKAGE_LIBSQLITE3 flag changes (Android 15: trunk_staging flag file deleted; Android 16: ap3a moved to 3.44.5).",
     "component": "platform/external/sqlite (libsqlite3 amalgamation dist/sqlite3.c, dist/orig/sqlite3.c, shell.c); platform/build/release RELEASE_PACKAGE_LIBSQLITE3 flag on Android 15/16",
     "weakness": "unknown",
     "root_cause": "The material does not isolate the vulnerable code. The fix is a cumulative upstream SQLite patch-release update, and the bulletin only states High severity EoP. The 3.42.0 to 3.42.1 diff in effect on Android 14 contains several memory-safety and corruption-handling hardenings in the amalgamation:\n- defragmentPage copied only part of the page into temp space.\n- lockBtree set BTS_PAGESIZE_FIXED before validating the page size.\n- loadStatTbl padded stat4 samples with only 2 zero bytes.\n- sessionReadRecord read 8-byte INTEGER/FLOAT values without checking the remaining input.\n- fts5DoSecureDelete copied nPrefix2-nPrefix bytes from pSeg->term.p without checking nPrefix2 against pSeg->term.n, and recomputed page-index offsets differently.\n- fts5DlidxLvlNext and fts5WriteDlidxAppend did signed rowid delta arithmetic.\n- findOrCreateAggInfoColumn stored iAgg as (i16)k without masking and had no early return for an already-registered expression.\n- valueNew did not reset the returned aMem slot to NULL.\nThe material does not show which of these corresponds to the CVE.",
     "fix_analysis": [
      "Commit 2abdb383b72c (android-security-14.0.0_r24) updates dist/orig/sqlite3.c, dist/sqlite3.c, dist/orig/sqlite3.h and dist/sqlite3.h from 3.39.2 to 3.39.6 (SQLITE_SOURCE_ID 2025-08-01 642be27a38). It adds LICENSE and NOTICE, rewrites METADATA and README.version, and rebases the hunk offsets in dist/Android.patch. Notable content:\n- a bounds check on pCell+4 in modifyPagePointer\n- a rewrite of the balance_nonroot page-number sort, with sqlite3PcacheMove dropping a colliding cached page\n- unixGetTempname and the temp_store_directory / data_store_directory pragmas now run under SQLITE_MUTEX_STATIC_TEMPDIR\n- sqlite3FinishTrigger refuses triggers that write to shadow tables\n- the codeEqualityTerm aiMap array is sized MAX(nEq,n)\n- OP_MustBeInt is emitted before OP_Filter\n- FTS3 allocations move to sqlite3_malloc64/realloc64 with i64 sizes\n- iAgg is masked with 0x7fff in analyzeAggregate\n- NULL guards in sqlite3_strglob/sqlite3_strlike and dbpageUpdate\n- NULL guards in shell.c eqp_append and dump_callback",
      "Commit ba40d842b44f (android-security-14.0.0_r25) fully reverts the 3.39.6 update. Its message says the branch was missing the mandatory udc patch aosp/2622587, which upgraded sqlite to 3.40.0, and that CVE fixes must be applied on top of that patch.",
      "Commit 3b55568966b4 (android-security-14.0.0_r25) is the effective fix. It updates the amalgamation and both sqlite3.h copies from 3.42.0 to 3.42.1 (SQLITE_SOURCE_ID 2025-08-06 4a8bf3c146), updates the METADATA version and archive, adds NOTICE, and rebases dist/Android.patch. Code changes in sqlite3.c:\n- defragmentPage does memcpy(temp, data, usableSize) for the whole page.\n- lockBtree sets BTS_PAGESIZE_FIXED only after the page-size checks.\n- sqlite3BtreeIntegrityCheck sets sCheck.v0 for each root.\n- valueNew calls sqlite3VdbeMemSetNull on the returned slot.\n- vdbeRecordCompareDebug tolerates corrupt short integer fields.\n- findOrCreateAggInfoColumn returns early if pCol->pCExpr==pExpr and masks iAgg with 0x7fff.\n- loadStatTbl allocates pSample->n + 8 bytes.\n- sqlite3SrcListLookup sets fg.notCte.\n- EP_Unlikely is cleared, and sqlite3ExprTruthValue uses sqlite3ExprSkipCollateAndLikely.\n- sessionReadRecord returns SQLITE_CORRUPT_BKPT when fewer than 8 bytes remain.\n- fts5DlidxLvlNext and fts5WriteDlidxAppend use u64 deltas.\n- fts5MultiIterDoCompare no longer copies bDel.\n- fts5DoSecureDelete handles bDel, sets FTS5_CORRUPT when nPrefix2>pSeg->term.n, and recomputes key offsets.\n- fts5FlushOneHash handles bDel entries under secure-delete.\nshell.c changes: recoverIsValidPage rejects nByte<4; .dump quotes names via printf('\"%w\"'); -nonce uses cmdline_option_value.",
      "Android 16 (platform/build/release b11607ea3ec7): flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto changes from 3440300 to 3440500.",
      "Android 15 (platform/build/release df01bf9a7498): deletes flag_values/trunk_staging/RELEASE_PACKAGE_LIBSQLITE3.textproto, which held 3440300.",
      "Android 17 (platform/external/sqlite e13117e12f88, android-17.0.0_r1): removes the obsolete dist/sqlite-autoconf-3500100 (3.50.1) source tree."
     ],
     "completeness": [
      "On Android 14 the effective change is the whole 3.42.0 to 3.42.1 upstream delta. The 3.39.6 delta was reverted. The material does not show whether the 3.42.0 baseline already contained the hardenings from the 3.39.6 update.",
      "One change appears in both the 3.39.6 and 3.42.1 deltas: masking pExpr->iAgg with 0x7fff. The material does not say this is the CVE fix.",
      "Both dist/sqlite3.c and dist/orig/sqlite3.c are updated, and dist/Android.patch is rebased onto the new line offsets.",
      "At android-security-14.0.0_r29 the amalgamation and headers are 3.42.1, but METADATA and README.version still read 3.42.0 and NOTICE is absent.",
      "Android 15 and 16 are fixed only through the RELEASE_PACKAGE_LIBSQLITE3 flag. The material does not show the source delta of the selected package."
     ],
     "behavior_changes": [
      "On Android 14, SQLITE_VERSION / SQLITE_VERSION_NUMBER become 3.42.1 / 3042001 with a 2025-08-06 source id. The fts5 source id string changes to match.",
      "Session extension: sessionReadRecord returns SQLITE_CORRUPT for input that ends inside an 8-byte INTEGER/FLOAT value.",
      "FTS5 secure-delete: FTS5_CORRUPT is set when the prefix length exceeds the term length, and handling of deleted entries changes in fts5FlushOneHash and fts5DoSecureDelete.",
      "Btree: BTS_PAGESIZE_FIXED is set only after the page-size checks pass, and defragmentPage copies the whole usable page into temp space.",
      "stat4 sample buffers get 8 bytes of zero padding instead of 2.",
      "sqlite3 shell: .recover rejects free blocks smaller than 4 bytes, .dump quotes table and column names, and the -nonce argument goes through cmdline_option_value.",
      "Android 16 ap3a selects sqlite package 3440500 instead of 3440300. Android 15 trunk_staging no longer pins 3440300."
     ],
     "preconditions": [
      "unknown: the material does not identify the triggering input. The Lightrion index search returned no relevant sqlite callers (only liblog matches)."
     ],
     "per_major": {
      "14": "Adapted fix in several steps. 2abdb383b72c (r24) updated sqlite from 3.39.2 to 3.39.6. ba40d842b44f (r25) reverted it because the mandatory aosp/2622587 (sqlite 3.40.0 upgrade) was missing. 3b55568966b4 (r25) then applied the 3.42.0 to 3.42.1 update. At android-security-14.0.0_r29, 3/8 lines of the tracked patched region are present; dist/sqlite3.c and sqlite3.h are 3.42.1, while METADATA and README.version still say 3.42.0.",
      "15": "Adapted fix via build flag. platform/build/release df01bf9a7498 deletes flag_values/trunk_staging/RELEASE_PACKAGE_LIBSQLITE3.textproto (3440300). 2/2 lines of the patched region are present at android-security-15.0.0_r17. The resulting sqlite version is not shown.",
      "16": "Adapted fix via build flag. platform/build/release b11607ea3ec7 changes flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto from 3440300 to 3440500. 2/2 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "Fixed in the base release (android-17.0.0_r1). platform/external/sqlite e13117e12f88 removes the obsolete dist/sqlite-autoconf-3500100 (3.50.1) tree, and the patched files are absent at android-security-17.0.0_r1. The shipped version is not shown."
     },
     "backport_notes": [
      "Android 14: the effective commit is 3b55568966b4. It applies on top of a 3.42.0 baseline; per the revert message, the branch must carry the mandatory aosp/2622587 sqlite upgrade (3.40.0). Keep dist/Android.patch rebased so the Android-specific edits stay applied.",
      "Android 15/16: the fix is a RELEASE_PACKAGE_LIBSQLITE3 flag change (Android 16 ap3a: 3440500; Android 15: trunk_staging flag file deleted). It depends on the matching sqlite package being present in the tree."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which upstream SQLite change corresponds to CVE-2025-48595, and what input triggers it.",
      "How the vulnerable code is reached on Android (for example, which SQL or database content).",
      "Whether the 3.42.0 baseline on Android 14 already contained the hardenings from the reverted 3.39.6 update.",
      "Why METADATA and README.version at android-security-14.0.0_r29 still state 3.42.0, and why NOTICE is absent there.",
      "Which sqlite version Android 15 trunk_staging resolves to after the flag file deletion, and which version Android 17 ships.",
      "The content of the sqlite 3.44.3 to 3.44.5 delta selected on Android 16."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48612": {
     "cve": "CVE-2025-48612",
     "summary": "In Settings' NFC default payment app selector, the picker key used to be a \"flattenedComponentName userId\" string that was parsed back on selection. The fix replaces it with an opaque hash key looked up in a map of PaymentAppInfo entries, so the component and user passed to setDefaultPaymentApp always come from an enumerated candidate instead of a string parse.",
     "component": "platform/packages/apps/Settings, NFC default payment app picker (com.android.settings.nfc.DefaultPaymentSettings / PaymentBackend)",
     "weakness": "Improper input handling: a composite string key was parsed back into a ComponentName and user id without checking it against the enumerated candidates (described by the commit as string injection)",
     "root_cause": "DefaultPaymentSettings.getDefaultKey() built the picker key as componentName.flattenToString() + \" \" + userHandle.getIdentifier(). setDefaultKey(String key) rebuilt the target with key.split(\" \"), ComponentName.unflattenFromString(keys[0]) and Integer.parseInt(keys[1]). Because the key was parsed back instead of being mapped to an enumerated candidate, the ComponentName and user id passed to PaymentBackend.setDefaultPaymentApp were never checked against the candidates obtained from getPaymentAppInfos(). The commit calls this string injection.",
     "fix_analysis": [
      "src/com/android/settings/nfc/PaymentBackend.java: PaymentAppInfo gains getKey(), which returns Integer.toString(hashCode()). hashCode() is Objects.hash(componentName, userHandle), with a matching equals() on componentName and userHandle. The key no longer embeds the flattened component string.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java: onAttach() now stores mAppInfos as a Map<String, PaymentAppInfo>, built from mPaymentBackend.getPaymentAppInfos() with Collectors.toMap(PaymentAppInfo::getKey, Function.identity()).",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java: getDefaultKey() returns defaultAppInfo.getKey(), or null if there is no default. The flattenToString concatenation is removed.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java: setDefaultKey(String key) looks the key up in mAppInfos. If the key is absent it returns true and does nothing. Otherwise it calls mPaymentBackend.setDefaultPaymentApp(appInfo.componentName, appInfo.userHandle.getIdentifier()) with the stored candidate's fields. The split/unflattenFromString/parseInt parsing and the android.content.ComponentName import are removed.",
      "DefaultPaymentSettings.getCandidates() iterates mAppInfos.values() instead of the list. Nothing else in it changes.",
      "On Android 14 the change went through three commits: c90fee1f1ea4 (r24); a full revert, da3689792a45 (r28, 'patch needs to be updated to fix regression'); then Revert^2 4b903dacbc6c (r28). The reland's hunks are the same as the original's, but they apply to a different DefaultPaymentSettings.java blob (d75844bb0ff -> b5057ed64f2, versus ddac08beb00 -> 440f09e3d9b originally)."
     ],
     "completeness": [
      "Covers the only parse site shown. setDefaultKey can now only select a component/user pair that PaymentBackend.getPaymentAppInfos() returned at onAttach time.",
      "The key is Integer.toString(Objects.hash(componentName, userHandle)). It is opaque but not guaranteed collision-free. Collectors.toMap throws on duplicate keys, so two candidates with colliding hashes would cause an exception in onAttach rather than a wrong selection.",
      "The diff fills mAppInfos only in onAttach. It does not show whether the map is refreshed when payment apps change while the fragment is attached. If it is not, a key for a newly appearing app would be ignored.",
      "PaymentBackend.setDefaultPaymentApp itself is not changed; the fix sits only at the Settings picker layer.",
      "The new equals() dereferences componentName and userHandle without null checks, and getDefaultKey carries @SuppressWarnings(\"NullAway\"). The material does not show what happens when these fields are null."
     ],
     "behavior_changes": [
      "Picker keys are now numeric hash strings instead of 'pkg/cls userId'. Any key in the old format no longer matches and is ignored.",
      "setDefaultKey with an unknown key does nothing and still returns true. Before, keys with fewer than two space-separated parts were ignored and all others were parsed and applied.",
      "getCandidates() now iterates the collected map's values, so the order may differ from the original list order. Whether candidates are sorted afterwards is not shown in the material.",
      "Duplicate (componentName, userHandle) pairs from getPaymentAppInfos() would now throw in onAttach because of Collectors.toMap."
     ],
     "preconditions": [
      "The Settings DefaultPaymentSettings picker (default NFC payment app selection screen) is reachable.",
      "At least one payment app is returned by PaymentBackend.getPaymentAppInfos() and shown as a candidate.",
      "A key reaches DefaultPaymentSettings.setDefaultKey through the DefaultAppPickerFragment flow. Per index search, onAttach is a Fragment lifecycle callback; no users outside Settings were found among the top results."
     ],
     "per_major": {
      "14": "same fix: c90fee1f1ea4 in android-security-14.0.0_r24 (backwards-compatible port of ag/35084316). It was reverted by da3689792a45 and relanded with the same hunks by 4b903dacbc6c, both in android-security-14.0.0_r28. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "same fix: adac3d79c0f3 in android-security-15.0.0_r12, same diff. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "same fix: aa744e8988f0 in android-security-16.0.0_r3, same diff. 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "same fix in base: 1553d28f0f57 in android-17.0.0_r1, same diff. 8/8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The hunks are the same on 14 through 17 and self-contained (two files, java.util imports only). The 15, 16 and 17 commits apply to DefaultPaymentSettings.java blob ddac08beb00; the Android 14 reland applies to blob d75844bb0ff. PaymentBackend.java is 021d673e152 everywhere. Check the surrounding context in your tree.",
      "On Android 14 the first landing was reverted for a regression and then relanded with the same hunks. Take the final Revert^2 state (4b903dacbc6c). The material does not describe the regression."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe the regression that led to the Android 14 revert da3689792a45. The reland has the same hunks.",
      "Where the componentName in PaymentAppInfo comes from (for example, whether an installed app controls it) is not shown in the material.",
      "The shape of input that made the old parse select a different component or user is not shown.",
      "The material does not show how DefaultAppPickerFragment feeds keys into setDefaultKey (UI selection only, or also persisted or external values).",
      "Whether getCandidates() sorts candidates after iterating, and whether mAppInfos is refreshed outside onAttach, is not shown.",
      "The behavior of PaymentBackend.setDefaultPaymentApp for an arbitrary component or user id (the EoP consequence) is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48615": {
     "cve": "CVE-2025-48615",
     "summary": "Adds a 1024-character cap on media button receiver component names in MediaButtonReceiverHolder, first for components resolved from a PendingIntent (skipped with a warning) and then for ComponentName broadcast receivers (rejected with IllegalArgumentException).",
     "component": "platform/frameworks/base, system_server media session service (services/core/java/com/android/server/media/MediaButtonReceiverHolder.java)",
     "weakness": "Missing length/bounds validation on app-supplied component name",
     "root_cause": "MediaButtonReceiverHolder accepted a media button receiver component name of any length. In getComponentName(PendingIntent, componentType) the ComponentName was built directly from componentInfo.packageName and componentInfo.name of the resolved PendingIntent target, and create(int userId, ComponentName broadcastReceiver) stored the caller-provided ComponentName as-is. The class also flattens the receiver to a string for persistence across boot (see unflattenFromString and its javadoc), so an overly long package or class name entered system_server state without any size check. The first commit message says the fix cannot be tested on main because PackageManager prevents it, so the input relied on a constraint that is not enforced at this layer. The material does not show the concrete consequence of an oversized name.",
     "fix_analysis": [
      "Commit a1bbb371980e (MediaButtonReceiverHolder.java): introduces private static final int MAX_COMPONENT_NAME_LENGTH = 1024. In getComponentName(PendingIntent, componentType), inside the loop over resolved components and after the existing creator-package and null checks, it computes componentInfo.packageName.length() + componentInfo.name.length() + 1. If the result is greater than 1024, it logs 'detected and ignored component name with overly long package or name' and continues to the next candidate instead of returning a ComponentName.",
      "Commit e20813dba23a (MediaButtonReceiverHolder.java): adds private static boolean componentNameTooLong(ComponentName), which applies the same packageName + className + 1 > MAX_COMPONENT_NAME_LENGTH test. create(int userId, ComponentName broadcastReceiver) now calls it first and throws IllegalArgumentException(\"receiver name too long\") before constructing the holder. getComponentName(PendingIntent, ...) is refactored to build the ComponentName first and call componentNameTooLong on it; the skip-and-log behavior is unchanged. Per the commit message, this closes the setMediaButtonBroadcastReceiver(ComponentName) path that the first change missed."
     ],
     "completeness": [
      "After both commits, both create overloads that take an app-supplied receiver are checked: the PendingIntent path via getComponentName(PendingIntent, componentType) and the ComponentName path via create(int, ComponentName).",
      "The first commit alone covers only the PendingIntent path. The ComponentName broadcast receiver path is covered only by the second commit.",
      "unflattenFromString(Context, String), which restores the receiver from persisted storage, is not modified. It still calls ComponentName.unflattenFromString(tokens[0]) without a length check, so previously persisted values are not re-validated by this diff.",
      "The fallback constructor MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName), used when the PendingIntent component cannot be resolved, is unchanged. It stores sessionPackageName without a length check (the javadoc says the session package name is assumed to be already validated).",
      "The check counts Java String length (chars), not bytes."
     ],
     "behavior_changes": [
      "PendingIntent media button receivers whose resolved package name + class name + 1 exceeds 1024 characters are skipped with a Log.w. If no other candidate matches, create(int, PendingIntent, String) falls back to the component-less holder (COMPONENT_TYPE_INVALID) with only the session package name, as it already did for unresolvable intents.",
      "create(int userId, ComponentName broadcastReceiver) now throws IllegalArgumentException(\"receiver name too long\") for component names over 1024 characters, where it previously always succeeded.",
      "New constant MAX_COMPONENT_NAME_LENGTH = 1024 and new private helper componentNameTooLong(ComponentName)."
     ],
     "preconditions": [
      "An app owning a MediaSession sets a media button receiver via MediaSession.setMediaButtonReceiver(PendingIntent) or MediaSession.setMediaButtonBroadcastReceiver(ComponentName). The second commit message names these paths. The call chain into MediaButtonReceiverHolder.create is implied but not shown.",
      "The receiver's package name plus class name plus one exceeds 1024 characters. For the PendingIntent path, the component must also resolve to the PendingIntent creator's package (existing check).",
      "The first commit says the fix can't be tested on main because PackageManager prevents it, so reachability depends on how an oversized name could get onto the device. The material does not show this."
     ],
     "per_major": {
      "14": "same fix, both commits: a1bbb371980e in android-security-14.0.0_r24 (PendingIntent path) and e20813dba23a in android-security-14.0.0_r25 (ComponentName path plus helper). At android-security-14.0.0_r29, 8/8 lines of the patched region are present.",
      "15": "same fix for the first commit: 8f8c16cee29b in android-security-15.0.0_r12, identical diff to Android 14. At android-security-15.0.0_r17, 8/8 lines are present. The material does not show whether the second commit (ComponentName path) was applied on 15.",
      "16": "same fix for the first commit: a5795fc0cf1f in android-security-16.0.0_r3, identical diff to Android 14. At android-security-16.0.0_r8, 8/8 lines are present. The material does not show whether the second commit (ComponentName path) was applied on 16.",
      "17": "resolved in base: the second commit 013bb57034ee is in android-17.0.0_r1, and its parent blob (ffa2bcbd6fd2) is the file after the first commit, so both changes are present. At android-security-17.0.0_r1, 6/8 lines of the patched region are present. The second commit rewrites three of the first commit's added lines; the material does not explain the exact 6/8 count."
     },
     "backport_notes": [
      "Apply the two commits in order: e20813dba23a depends on a1bbb371980e for MAX_COMPONENT_NAME_LENGTH and modifies lines that a1bbb371980e introduced. Applying only the first leaves create(int, ComponentName) unchecked.",
      "Both diffs are confined to MediaButtonReceiverHolder.java and add no new imports (TextUtils and Log were already imported), so they should apply cleanly wherever that class has the same getComponentName loop and create(int, ComponentName) overload."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the concrete effect of an overly long component name, that is, what fails in system_server or in the persisted-state path and how that leads to the bulletin's EoP rating.",
      "Whether the second commit (ComponentName broadcast receiver path) was merged to the Android 15 and 16 security branches. Only the first commit is shown for those majors.",
      "How the IllegalArgumentException from create(int, ComponentName) propagates to the calling app. The MediaSessionRecord/MediaSessionService call sites are not in the material.",
      "Why the patched-region check reports 6/8 lines on Android 17 but 8/8 on Android 14 r29, although both contain the second commit.",
      "Why 1024 was chosen as the limit and whether it matches PackageManager's own component name constraint."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48616": {
     "cve": "CVE-2025-48616",
     "summary": "KeyguardViewMediator now queries KeyguardUpdateMonitor.isUserInLockdown instead of LockPatternUtils.isUserInLockdown at the three places that decide whether lockdown forces the keyguard to show or prevents it from being hidden.",
     "component": "platform/frameworks/base, SystemUI keyguard (KeyguardViewMediator / KeyguardUpdateMonitor)",
     "weakness": "Security decision (lockdown check) based on a state source that may be out of sync with the authoritative strong auth tracker; bulletin type ID (information disclosure)",
     "root_cause": "KeyguardViewMediator decided lockdown handling with mLockPatternUtils.isUserInLockdown(...). Its onStrongAuthStateChanged callback is invoked by KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged (per index search), and per the commit message the LockPatternUtils view of lockdown may be out of sync with the KeyguardUpdateMonitor's strong auth tracker. When they disagreed, onStrongAuthStateChanged would not call doKeyguardLocked, setKeyguardEnabled(false) would not be overridden by lockdown, and the keyguard show path would skip showing because the keyguard was externally disabled. The commit's test scenario is pinning an app, locking down the device and checking that the keyguard always shows (repeated 10 times).",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java: the KeyguardUpdateMonitorCallback's onStrongAuthStateChanged now calls mUpdateMonitor.isUserInLockdown(current user) instead of mLockPatternUtils.isUserInLockdown before doKeyguardLocked(null).",
      "KeyguardViewMediator.java: setKeyguardEnabled path (the '!enabled && mShowing' branch) now uses mUpdateMonitor.isUserInLockdown to decide to log 'keyguardEnabled(false) overridden by user lockdown' and return without hiding.",
      "KeyguardViewMediator.java: the 'if another app is disabling us, don't show' condition (doKeyguard path) now uses mUpdateMonitor.isUserInLockdown so lockdown still forces the keyguard even when mExternallyEnabled is false.",
      "packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java: javadoc of onStrongAuthStateChanged updated to direct implementers to use KeyguardUpdateMonitor (e.g. isUserInLockdown) as the source of truth. No code change.",
      "packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java: tests onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally and doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally now mock mUpdateMonitor.isUserInLockdown instead of mLockPatternUtils.isUserInLockdown."
     ],
     "completeness": [
      "All three lockdown checks in the diffed regions of KeyguardViewMediator (onStrongAuthStateChanged, setKeyguardEnabled, doKeyguard path) are switched to mUpdateMonitor; no mLockPatternUtils.isUserInLockdown call remains in the diffed regions.",
      "The fix does not change how either LockPatternUtils or KeyguardUpdateMonitor computes lockdown state, nor does it make the two sources consistent; it only uses the monitor as the source in this class.",
      "Other KeyguardUpdateMonitorCallback implementers that may consult LockPatternUtils for lockdown are addressed only by the javadoc guidance; the material does not show whether any exist.",
      "Other mLockPatternUtils uses in KeyguardViewMediator shown in context (isSecure, getDevicePolicyManager in the biometric callbacks) are unchanged and are not lockdown checks."
     ],
     "behavior_changes": [
      "Lockdown is now evaluated from KeyguardUpdateMonitor when onStrongAuthStateChanged fires, intended so the keyguard shows when lockdown starts even while the keyguard is externally disabled (e.g. app pinned, per commit test).",
      "setKeyguardEnabled(false) is refused (with the existing log line) whenever the monitor reports lockdown, independent of LockPatternUtils state.",
      "No new APIs, flags, or error codes; Flag: EXEMPT CVE_FIX."
     ],
     "preconditions": [
      "User triggers lockdown while the keyguard is externally disabled, e.g. an app is pinned (commit test steps).",
      "onStrongAuthStateChanged is reached via KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged iterating registered callbacks (per index search).",
      "LockPatternUtils.isUserInLockdown and the monitor's strong auth tracker are out of sync (commit message says they 'may be out of sync')."
     ],
     "per_major": {
      "14": "same fix: 991998d5464f in android-security-14.0.0_r24, three call sites use KeyguardUpdateMonitor.getCurrentUser(); the shown code context at android-security-14.0.0_r29 contains the fixed onStrongAuthStateChanged and test lines; resolver reports 6/8 lines of the patched region present.",
      "15": "adapted fix: 9adff3df0285 in android-security-15.0.0_r12, same three sites but user id comes from mSelectedUserInteractor.getSelectedUserId() with wrapped lines; same javadoc and test changes; 6/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: aa9fbb1b74eb in android-security-16.0.0_r3, same shape as the Android 15 version (mSelectedUserInteractor.getSelectedUserId()); 6/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix as the 14 variant: 52a6d6dfda83 (RESTRICT AUTOMERGE) in android-17.0.0_r1, using KeyguardUpdateMonitor.getCurrentUser() call sites; resolver reports 6/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit: replace mLockPatternUtils.isUserInLockdown with mUpdateMonitor.isUserInLockdown at the three sites in KeyguardViewMediator and update the two unit tests' mocks; on branches using mSelectedUserInteractor.getSelectedUserId() follow the 15/16 variant."
     ],
     "confidence": "high",
     "unknowns": [
      "Why LockPatternUtils and the strong auth tracker get out of sync (the implementation of either isUserInLockdown is not in the material).",
      "Whether other KeyguardUpdateMonitorCallback implementers still use LockPatternUtils for lockdown decisions.",
      "Exact reason the resolver reports 6/8 lines present in the latest tags."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48648": {
     "cve": "CVE-2025-48648",
     "summary": "NotificationManagerService stops relying on PackageManagerInternal.isSameApp (which, per the commit message, carries an SDK sandbox exception) for its caller-owns-package enforcement and instead compares the caller uid directly against the package uid, and additionally rejects SDK sandbox uids in areNotificationsEnabledForPackage and checks system/SystemUI/shell identity before canNotifyAsPackage in getConversationNotificationChannel.",
     "component": "platform/frameworks/base, NotificationManagerService (services/core/java/com/android/server/notification)",
     "weakness": "Improper caller identity / ownership check (same-app enforcement accepted uids that do not own the package, specifically SDK sandbox uids per the commit message)",
     "root_cause": "The private same-app enforcement helper in NotificationManagerService.java (the method that throws SecurityException \"Package <pkg> is not owned by uid <uid>\", after the ROOT_UID/ROOT_PKG early return) delegated to mPackageManagerInternal.isSameApp(pkg, uid, userId). The commit message states this check included an SDK sandbox exception, so a caller whose uid is not the package's uid could still pass the same-package enforcement (likely reached via enforceSystemOrSystemUIOrSamePackage, though the call chain is not shown in the material). areNotificationsEnabledForPackage(pkg, uid) had no explicit rejection of sandbox uids, and getConversationNotificationChannel evaluated canNotifyAsPackage(callingPkg, targetPkg, userId) before the system/SystemUI/shell identity check. The bulletin classifies the result as DoS (High); the material does not show the concrete failure mode.",
     "fix_analysis": [
      "NotificationManagerService.java, same-app enforcement helper (hunk around line 10231): replaces `!mPackageManagerInternal.isSameApp(pkg, uid, userId)` with `!UserHandle.isSameApp(uid, mPackageManagerInternal.getPackageUid(pkg, 0L, userId))`. The package's uid in the given user is resolved with PackageManagerInternal.getPackageUid and compared to the supplied uid with UserHandle.isSameApp; no sandbox exception is applied. If the package is not found, getPackageUid returns a negative value and the comparison fails, so the SecurityException is thrown (general platform knowledge).",
      "NotificationManagerService.java:areNotificationsEnabledForPackage(String pkg, int uid): adds an early `if (Process.isSdkSandboxUid(uid)) return false;` before enforceSystemOrSystemUIOrSamePackage, so a sandbox uid is reported as notifications-disabled without reaching the enforcement or the cross-user permission check.",
      "NotificationManagerService.java:getConversationNotificationChannel(...): swaps the condition order to `isCallerIsSystemOrSystemUiOrShell() || canNotifyAsPackage(callingPkg, targetPkg, userId)`, so system/SystemUI/shell callers are accepted before canNotifyAsPackage runs on the caller-supplied callingPkg. The helper is also renamed from isCallerIsSystemOrSysemUiOrShell to isCallerIsSystemOrSystemUiOrShell (typo fix).",
      "Tests: UiServiceTestCase gains mUid/mUserId/mUser/mPkg and per-user UID_N_MR1/UID_O/UID_P/UID_R, and stubs mPmi.getPackageUid for the test packages; NotificationManagerServiceTest drops the blanket `mPackageManagerInternal.isSameApp(...)` answer that made any same-app check pass for mUid, and rewrites tests to use the shared mPmi mock, enqueueNotificationInternal with explicit uids, and shell identity where a system caller is required. PreferencesHelperTest expectations are updated for the new per-user test uids."
     ],
     "completeness": [
      "The Android 14 commit changes exactly three production sites: the shared same-app enforcement helper, areNotificationsEnabledForPackage, and the condition order in getConversationNotificationChannel. Callers of the patched helper benefit from the change; the material does not show the full set of callers (e.g. whether canNotifyAsPackage and enforceSystemOrSystemUIOrSamePackage route through it).",
      "The Android 15 and 16 commits additionally convert two direct `mPackageManagerInternal.isSameApp` calls to isCallerSameApp (the config_systemWellbeing AutomaticZenRule type check and getEffectsSuppressor's suppressor-package check) and change resolveNotificationUid so that USER_ALL maps to UserHandle.getUserId(callingUid) instead of USER_SYSTEM. None of these three hunks are present in the Android 14 commit; the material does not show whether the corresponding Android 14 code exists or still calls mPackageManagerInternal.isSameApp directly.",
      "The sandbox-uid early return is added only to areNotificationsEnabledForPackage; other binder entry points rely on the helper change rather than an explicit Process.isSdkSandboxUid check."
     ],
     "behavior_changes": [
      "areNotificationsEnabledForPackage returns false for any uid for which Process.isSdkSandboxUid is true, before any permission enforcement (no SecurityException for that case).",
      "The same-app enforcement now throws SecurityException whenever the supplied uid does not match the appId of the package's uid in the given user as reported by PackageManagerInternal.getPackageUid, including for callers that previously passed via the PackageManagerInternal.isSameApp sandbox exception.",
      "getConversationNotificationChannel no longer runs canNotifyAsPackage for system, SystemUI, shell or root callers; the callingPkg argument is irrelevant for those callers.",
      "Android 15/16 only: resolveNotificationUid with userId == USER_ALL resolves against the calling uid's user rather than USER_SYSTEM; the wellbeing and effects-suppressor checks go through isCallerSameApp."
     ],
     "preconditions": [
      "Caller reaches an INotificationManager method that invokes the same-app enforcement or areNotificationsEnabledForPackage with a pkg it does not own but for which PackageManagerInternal.isSameApp returned true (per the commit message, the SDK sandbox case).",
      "Entry points seen in the index: areNotificationsEnabledForPackage is called from Settings NotificationBackend.getNotificationsBanned and from ServiceRecord via NotificationManagerInternal; getConversationNotificationChannel is called from NotificationManager.getNotificationChannel(channelId, conversationId) with the app's own package names (per index search).",
      "The bulletin rates the issue High / DoS; the specific triggering condition is not shown in the material."
     ],
     "per_major": {
      "14": "Same fix: commit 699e4677367e in android-security-14.0.0_r25 (cherry-pick of f5605ae25b15, Change-Id I2be1537803a83cf44cc605f2850a3bb705b9bf32); helper change, areNotificationsEnabledForPackage sandbox check, getConversationNotificationChannel reorder, plus large test refactor that introduces mUid/mUserId/UID_* in UiServiceTestCase. 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "Adapted fix: commit 3d502bcba495 in android-security-15.0.0_r13; same three core hunks, plus conversion of the wellbeing AutomaticZenRule check and getEffectsSuppressor to isCallerSameApp, and resolveNotificationUid USER_ALL now maps to the caller's user. UID_* test constants moved from static values to per-user values. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix: commit b88956f3198d in android-security-16.0.0_r4, with the same production hunks as the Android 15 variant (helper, sandbox check, reorder, wellbeing/getEffectsSuppressor via isCallerSameApp, resolveNotificationUid user mapping); test changes differ slightly. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Same fix: commit 44ee96106f25 in android-17.0.0_r1 (base); the production hunks match the Android 14 variant (helper, sandbox check, reorder, isCallerIsSystemOrSysemUiOrShell rename), and the commit additionally adds compatibility-device-util-axt-minus-dexmaker to services/tests/uiservicestests/Android.bp and a mUmi UserManagerInternal mock to UiServiceTestCase. 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The production change is small and self-contained (three hunks in NotificationManagerService.java); the bulk of the commit is test refactoring that removes the blanket isSameApp stub. When backporting, grep NotificationManagerService.java for any remaining direct `mPackageManagerInternal.isSameApp` callers (the 15/16 commits converted getEffectsSuppressor and the wellbeing check to isCallerSameApp) and route them through the patched helper if present.",
      "The updated NotificationManagerServiceTest uses com.android.compatibility.common.util.SystemUtil; the Android 17 commit adds `compatibility-device-util-axt-minus-dexmaker` to services/tests/uiservicestests/Android.bp, so a tree without that static_lib needs the same Android.bp change for the tests to compile."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact name of the patched same-app enforcement helper and its full set of callers (the hunk header does not show it).",
      "The concrete DoS mechanism and which binder entry point the reporter used; the material only shows the bulletin type and the sandbox-exception explanation from the commit message.",
      "Whether Android 14's NotificationManagerService has other direct mPackageManagerInternal.isSameApp call sites (e.g. getEffectsSuppressor) that the 15/16 commits changed but the 14 commit did not.",
      "Whether services/tests/uiservicestests/Android.bp on Android 14 already includes the compatibility util dependency needed by the updated tests."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48649": {
     "cve": "CVE-2025-48649",
     "summary": "Threads a restorePregrantedPermissions flag from ActivityManager through ActivityManagerService, PackageManagerService and PermissionManagerServiceImpl so that an app clearing its own data (public ActivityManager.clearApplicationUserData()) no longer gets default- or role-granted runtime permissions re-granted by the system, while callers holding CLEAR_APP_USER_DATA keep the old restoring behaviour.",
     "component": "platform/frameworks/base, ActivityManagerService / PackageManagerService / permission (resetRuntimePermissions)",
     "weakness": "Improper privilege management: an unprivileged self data-clear path triggered a Process.SYSTEM_UID re-grant of default/role-granted runtime permissions",
     "root_cause": "ActivityManagerService.clearApplicationUserData permits a caller without CLEAR_APP_USER_DATA when the target package has the caller's own uid (applicationInfo.uid == uid). That path calls IPackageManager.clearApplicationUserData after Binder.clearCallingIdentity, which in PackageManagerService.clearApplicationUserDataLIF calls mPermissionManager.resetRuntimePermissions(pkg, userId). In PermissionManagerServiceImpl.resetRuntimePermissionsInternal, every permission carrying FLAG_PERMISSION_GRANTED_BY_DEFAULT or FLAG_PERMISSION_GRANTED_BY_ROLE was unconditionally re-granted via grantRuntimePermissionInternal(..., Process.SYSTEM_UID, ...). As a result the public, unprivileged ActivityManager.clearApplicationUserData() let an app have previously pregranted runtime permissions restored for itself, which the bulletin rates as a High EoP.",
     "fix_analysis": [
      "core/java/android/app/ActivityManager.java: clearApplicationUserData() (self, public API) now calls a new private overload with restorePregrantedPermissions=false, which invokes the new binder method IActivityManager.clearApplicationUserDataWithoutPermissionReset; the hidden two-arg clearApplicationUserData(packageName, observer) keeps restoring (true) and uses the original binder method.",
      "core/java/android/app/IActivityManager.aidl: adds clearApplicationUserDataWithoutPermissionReset(packageName, keepState, observer, userId).",
      "services/core/java/com/android/server/am/ActivityManagerService.java: clearApplicationUserData (binder) delegates with true, clearApplicationUserDataWithoutPermissionReset delegates with false, to a new private clearApplicationUserData(..., restorePregrantedPermissions). The own-uid exemption is tightened to (applicationInfo.uid == uid && !restorePregrantedPermissions); otherwise CLEAR_APP_USER_DATA is required. The flag is forwarded to pm.clearApplicationUserData(packageName, localObserver, resolvedUserId, restorePregrantedPermissions).",
      "core/java/android/content/pm/IPackageManager.aidl and services/core/java/com/android/server/pm/PackageManagerService.java: the binder clearApplicationUserData in PackageManagerService gains the boolean (still enforces CLEAR_APP_USER_DATA) and passes it to clearApplicationUserDataLIF, which passes it to mPermissionManager.resetRuntimePermissions(pkg, userId, restorePregrantedPermissions). core/java/android/app/ApplicationPackageManager.java passes true; core/java/android/content/pm/PackageManager.java adds @RequiresPermission(CLEAR_APP_USER_DATA) on the abstract method.",
      "services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java: resetRuntimePermissions and both resetRuntimePermissionsInternal overloads take restorePregrantedPermissions; the grantRuntimePermissionInternal call for FLAG_PERMISSION_GRANTED_BY_DEFAULT / FLAG_PERMISSION_GRANTED_BY_ROLE permissions is now executed only when the flag is true, otherwise the grant state is left unchanged. resetRuntimePermissionsForUser and the uninstall-with-keep-data path pass true.",
      "Interface plumbing: PermissionManagerServiceInterface, PermissionManagerServiceInternal, PermissionManagerService (Internal), PermissionManagerServiceLoggingDecorator, LegacyPermissionManagerService (passes true) and PermissionService.kt (signature only; body is a TODO stub on 14) updated to the new 3-arg signature; CrossUserPackageVisibilityTests updated to pass true."
     ],
     "completeness": [
      "Covers the unprivileged self-clear path: the public ActivityManager.clearApplicationUserData() and the new clearApplicationUserDataWithoutPermissionReset binder are the only paths where the own-uid exemption still applies, and they never re-grant pregranted permissions.",
      "Calling the original IActivityManager.clearApplicationUserData binder (restore=true) for one's own package now requires CLEAR_APP_USER_DATA, since the own-uid exemption is conditioned on !restorePregrantedPermissions.",
      "IPackageManager.clearApplicationUserData and ApplicationPackageManager.clearApplicationUserData keep restoring (true) but are gated by CLEAR_APP_USER_DATA in PackageManagerService, so they are not reachable by unprivileged apps.",
      "Privileged callers (Settings AppStorageSettings.initiateClearUserData and Car Settings via the hidden two-arg ActivityManager method, per index search; LegacyPermissionManagerService.resetRuntimePermissions, resetRuntimePermissionsForUser, uninstall-keep-data path) keep the previous restore behaviour by passing true.",
      "On Android 14 only the legacy PermissionManagerServiceImpl implementation is changed behaviourally; PermissionService.kt.resetRuntimePermissions is a TODO stub in this branch, so no Kotlin policy change was needed (15/16 additionally change AppIdPermissionPolicy.kt).",
      "The code block after pm.clearApplicationUserData in ActivityManagerService that the diff context describes as 'Restore already established notification state and permission grants' is not modified by this fix; the material does not show its contents."
     ],
     "behavior_changes": [
      "An app clearing its own data via ActivityManager.clearApplicationUserData() no longer has permissions flagged GRANTED_BY_DEFAULT or GRANTED_BY_ROLE re-granted; their grant state is left as it was before the clear.",
      "A caller without CLEAR_APP_USER_DATA invoking the hidden IActivityManager.clearApplicationUserData for its own package is no longer permitted (permitted evaluates to false), where previously it was permitted; the resulting failure reporting is not shown in the diff.",
      "New hidden binder method IActivityManager.clearApplicationUserDataWithoutPermissionReset; IPackageManager.clearApplicationUserData, PermissionManagerServiceInternal.resetRuntimePermissions and PermissionManagerServiceInterface.resetRuntimePermissions change signature (extra boolean).",
      "PermissionManagerServiceLoggingDecorator log line now includes shouldRestorePregrants."
     ],
     "preconditions": [
      "Caller is a normal (non-isolated) app invoking the public ActivityManager.clearApplicationUserData() (or the hidden IActivityManager.clearApplicationUserData binder) for its own package; no permission required on the vulnerable code.",
      "The app holds a runtime permission whose flags include FLAG_PERMISSION_GRANTED_BY_DEFAULT or FLAG_PERMISSION_GRANTED_BY_ROLE and that is currently not granted (for example revoked by the user), per PermissionManagerServiceImpl.resetRuntimePermissionsInternal.",
      "The target package passes the isPackageDataProtected check in ActivityManagerService.clearApplicationUserData (exact gating not fully shown in the material)."
     ],
     "per_major": {
      "14": "Fixed by c1f49d2c2cac (RESTRICT AUTOMERGE) in android-security-14.0.0_r25; 15 files; AMS in this branch has no isRestore overload or LocalService.clearApplicationUserData hunk; PermissionService.kt only gets a signature change (body is a TODO). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Adapted fix 452f0beb15a9 in android-security-15.0.0_r13: same core change plus threading through the isRestore overload and LocalService.clearApplicationUserData (passes true), updates to PermissionManagerServiceTestingShim and PermissionManagerServiceTracingDecorator, AppIdPermissionPolicy.kt.resetRuntimePermissions honours restorePregrants (RUNTIME_GRANTED only or'ed in when true), PermissionService.kt forwards the flag, tests updated. 8/8 present at android-security-15.0.0_r17.",
      "16": "Adapted fix 7b9a906980a3 in android-security-16.0.0_r4, same shape as 15 (AppIdPermissionPolicy.kt condition also includes isSystemOrInstalled). 8/8 present at android-security-16.0.0_r8.",
      "17": "Resolved in base android-17.0.0_r1 (23da448adbb0, RESTRICT AUTOMERGE title); the diff provided for it has the same content as the Android 14 diff, though some pre-image blob hashes differ (PackageManager.java, ActivityManagerService.java, PermissionManagerService.java). 8/8 present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "If the target 14 tree already has the isRestore overload of ActivityManagerService.clearApplicationUserData and LocalService.clearApplicationUserData (present at android-14.0.0_r75 per index search), use the Android 15 AMS hunks (LocalService passes restorePregrantedPermissions=true) instead of the 14 security-branch hunks; also update every implementor of PermissionManagerServiceInterface present in the tree (the 15/16 diffs additionally touch PermissionManagerServiceTestingShim and PermissionManagerServiceTracingDecorator) and, if AppIdPermissionPolicy.kt.resetRuntimePermissions is a real implementation rather than a TODO, port the Kotlin hunk from 15.",
      "Since the IPackageManager and IActivityManager AIDL signatures change, any out-of-tree callers of IPackageManager.clearApplicationUserData(pkg, observer, userId) must be updated to pass the boolean."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether the Android 17 fix differs materially from the 14 commit (the provided 17 diff has the same content but different blob hashes for some files, suggesting a slightly different base).",
      "Contents of the AMS block that restores 'notification state and permission grants' after pm.clearApplicationUserData, and whether it interacts with pregranted permissions.",
      "The exact gating of the isPackageDataProtected branch around the permitted computation (context truncated).",
      "How a denied (permitted == false) call is reported to the caller.",
      "Whether the Android 14 security branch contains PermissionManagerServiceTracingDecorator/TestingShim (not touched by the 14 diff)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0036": {
     "cve": "CVE-2026-0036",
     "summary": "Adds a shell-side check in StageCoordinator.startAnimation. When split is not active, it detects a split-enter transition whose opening tasks belong to only one split stage and starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea. Previously such tasks were left to the default transition handler, which showed them outside of split.",
     "component": "platform/frameworks/base, libs/WindowManager/Shell split screen (StageCoordinator / shell transitions)",
     "weakness": "Missing validation of an inconsistent window-hierarchy state. A malformed split-enter transition (tasks in a split stage but no second stage) was not recognized, so a task parented to a split stage was handled by the default transition handler and shown without split being active. No flaw class beyond this state-handling error is evident from the diff.",
     "root_cause": "Per the commit message and the pre-fix code in StageCoordinator.startAnimation: when two tasks are launched immediately one after another and the second is an adjacent launch, both launches are collected into the same transition. The transition request reports the first task as the trigger task, so StageCoordinator is not resolved as the handler for that transition. In startAnimation, a transition that was not pending in mSplitTransitions and arrived while !mMainStage.isActive() simply did 'return false' without inspecting its contents. The transition fell through to the default transition handler, which showed the adjacent task (in a split stage) on its own without split being entered. This left the hierarchy in a broken state that the shell did not correct.",
     "fix_analysis": [
      "New file libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitTransitionUtils.java: handleMalformedEnterTransition(TransitionInfo info, Function<RunningTaskInfo, StageTaskListener> stageResolver) iterates info.getChanges(). A change is recorded when its getTaskInfo() is non-null, stageResolver maps it to a StageTaskListener, and its getMode() is TRANSIT_OPEN or TRANSIT_TO_FRONT (isOpeningMode). The task goes into leafSplitTasks and its stage into a HashSet parentStages. The transition is deemed malformed when leafSplitTasks is non-empty and parentStages.size() <= 1. In that case the method logs 'Malformed enter transition=%d detected' and returns a WindowContainerTransaction that calls wct.reparent(task.token, null /* parent */, true /* onTop */) for each leaf split task. Otherwise it returns null.",
      "StageCoordinator.java:startAnimation: in the non-pending-transition branch, a new ProtoLog 'startAnimation: transition=%d isSplitActive=%b' is added. The former one-liner 'if (!mMainStage.isActive()) return false;' becomes a block that calls SplitTransitionUtils.handleMalformedEnterTransition(info, taskInfo -> getStageOfTask(taskInfo)). If a WCT is returned, it calls mTransitions.startTransition(TRANSIT_CLOSE, wct, null). The method still returns false, so the original transition is still animated by another handler; the correction happens in a separate follow-up transition.",
      "StageCoordinator.java: adds a 'private final Transitions mTransitions' field, assigned in both constructors ('mTransitions = transitions; mTransitions.addHandler(this);'), so startAnimation can start the corrective transition. Also adds the TRANSIT_CLOSE static import and the SplitTransitionUtils import.",
      "StageTaskListener.java: class visibility changed from package-private to public so SplitTransitionUtils in package com.android.wm.shell.common.split can reference it.",
      "New test SplitTransitionUtilsTests.java. Two changes resolving to two stages but with no opening mode set return null. One opening task in one stage returns a non-null WCT. Tasks resolving to no stage return null. A non-opening mode returns null. A change without task info returns null."
     ],
     "completeness": [
      "The check runs only on the StageCoordinator.startAnimation path, for transitions not pending in mSplitTransitions, and only when split is not active (!mMainStage.isActive() on 14/15, !isSplitActive() on 16). Transitions arriving while split is already active go through the existing house-keeping code and are not checked by the new helper.",
      "Only changes with mode TRANSIT_OPEN or TRANSIT_TO_FRONT are considered (on 14/15/17 via the local helper). Other modes for tasks inside a stage are ignored by handleMalformedEnterTransition.",
      "The fix corrects the state after the fact rather than preventing it. startAnimation still returns false, so the malformed transition is animated by another handler, and the reparent happens in a subsequently started TRANSIT_CLOSE transition. The behaviour described in the commit message (the trigger task reported as the first launch when two launches merge into one transition) is not changed in this commit, which only touches Shell files.",
      "The malformed condition is parentStages.size() <= 1 with at least one leaf split task. A transition whose opening tasks all land in the same single stage is therefore also treated as malformed, and its tasks are restored to the TDA."
     ],
     "behavior_changes": [
      "When StageCoordinator receives a non-pending transition while split is inactive, and that transition contains opening tasks resolving to at most one split stage, it now starts a new TRANSIT_CLOSE transition that reparents those tasks to the TaskDisplayArea on top. Previously nothing was done.",
      "Two new WM_SHELL_SPLIT_SCREEN ProtoLog debug lines: 'startAnimation: transition=%d isSplitActive=%b' and 'Malformed enter transition=%d detected'.",
      "StageTaskListener becomes a public class (14/15/17 diffs); StageCoordinator keeps a reference to Transitions in a new mTransitions field.",
      "On the Android 16 variant, the previous 'startAnimation: transition=%d' ProtoLog was reached only when split was active. It is removed in favour of the new combined log, which is emitted for every non-pending transition."
     ],
     "preconditions": [
      "StageCoordinator is registered as a shell transition handler (mTransitions.addHandler(this)).",
      "Split screen is not active (mMainStage.isActive() false / isSplitActive() false) when the transition is handed to StageCoordinator.startAnimation, and the transition is not one pending in mSplitTransitions.",
      "Per the commit message: two task launches issued immediately one after the other, the second being an adjacent launch, such that both are collected into one transition whose request reports the first task as trigger. The material does not show who can issue such launches or from which context."
     ],
     "per_major": {
      "14": "Same fix: commit 3ef64c4444d5 in android-security-14.0.0_r28 (cherry-pick of 3be8f7ff61ba). Adds the mTransitions field, makes StageTaskListener public, uses a local isOpeningMode helper, and guards the check with !mMainStage.isActive(). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Same fix (fa1c418fd8aa in android-security-15.0.0_r16). SplitTransitionUtils.java and the StageTaskListener change are identical. StageCoordinator hunks are adapted to different surrounding fields and constructors; the guard is !mMainStage.isActive(). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix (04814cb8a86e in android-security-16.0.0_r7). SplitTransitionUtils uses com.android.wm.shell.shared.TransitionUtil.isOpeningMode and com.android.internal.protolog.ProtoLog instead of a local helper. The StageCoordinator diff does not add mTransitions or the TRANSIT_CLOSE import (they appear to already exist), and the guard is !isSplitActive(). The diff does not change StageTaskListener. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Resolved via base commit f60c7771358c in android-17.0.0_r1; 7/8 patched lines present at android-security-17.0.0_r1. The hunks shown are textually identical to the Android 14 commit (same line offsets, mMainStage.isActive() guard). The StageCoordinator blob indices differ from 14, while the StageTaskListener blob indices match."
     },
     "backport_notes": [
      "Single commit. StageCoordinator needs a stored Transitions reference (add mTransitions in every constructor if absent), and StageTaskListener must be public for the new common/split helper. Pick the branch-appropriate split-active predicate (mMainStage.isActive() on 14/15 vs isSplitActive() on 16) and ProtoLog import (com.android.internal.protolog.common.ProtoLog on 14/15 vs com.android.internal.protolog.ProtoLog on 16). Use TransitionUtil.isOpeningMode where it exists instead of the local helper."
     ],
     "confidence": "medium",
     "unknowns": [
      "What information is disclosed and to whom. The bulletin rates it High/ID, but the material does not describe the attacker-visible effect beyond the adjacent task being shown outside split.",
      "Whether the behaviour that merges the two launches into one transition and misreports the trigger task was fixed separately.",
      "Why the Android 17 hunks shown are textually identical to the Android 14 commit despite different StageCoordinator blob indices, and which line is missing in the 7/8 count at android-security-17.0.0_r1.",
      "Whether the new reparent-to-TDA correction can fire for legitimate single-stage opening transitions when split is inactive. The test suite only covers synthetic TransitionInfo objects.",
      "Whether TransitionUtil.isOpeningMode on 16 covers exactly the same modes as the local helper (TRANSIT_OPEN, TRANSIT_TO_FRONT)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0045": {
     "cve": "CVE-2026-0045",
     "summary": "In the Bluetooth stack's btm_sec_is_upgrade_possible (system/stack/btm/btm_sec.cc), the fix makes the function return true when a link key is known, the service requires authentication in the relevant direction and the peer's bond is temporary; the code comment says this is meant to trigger pairing.",
     "component": "platform/packages/modules/Bluetooth, BTM security manager (system/stack/btm/btm_sec.cc); on 15/16 also BTA JV RFCOMM client (system/bta/jv/bta_jv_act.cc)",
     "weakness": "Improper authentication: a temporary bond with a known link key was not treated as needing an upgrade for a service that requires authentication",
     "root_cause": "btm_sec_is_upgrade_possible() sets is_possible = false as soon as the device record has BTM_SEC_LINK_KEY_KNOWN. It sets it back to true only when a MITM upgrade is both required (BTM_SEC_OUT_MITM / BTM_SEC_IN_MITM) and feasible given the link key type and IO capabilities. It never looked at the bond type. So if the peer was bonded only temporarily (p_dev_rec->is_bond_type_temporary()) and the service's security_required contained BTM_SEC_OUT_AUTHENTICATE or BTM_SEC_IN_AUTHENTICATE, the function still returned false (no upgrade possible). On Android 16 a partial check already existed, but it was gated behind the aconfig flag upgrade_temp_bonding_on_auth_req and tested only BTM_SEC_OUT_AUTHENTICATE, which left the responder (incoming) direction unhandled.",
     "fix_analysis": [
      "system/stack/btm/btm_sec.cc:btm_sec_is_upgrade_possible (Android 14 backport 2e2a7bf09a3f): inside the BTM_SEC_LINK_KEY_KNOWN branch, after the existing MITM-upgrade check, adds a second, independent condition: if (security_required & (is_originator ? BTM_SEC_OUT_AUTHENTICATE : BTM_SEC_IN_AUTHENTICATE)) and p_dev_rec->is_bond_type_temporary(), set is_possible = true. The function then reports that an upgrade is possible, which according to the added comment triggers pairing.",
      "Android 15 (c1a809d2333e): the same btm_sec.cc check, written with the p_dev_rec->sec_rec.security_required / sec_rec.is_bond_type_temporary() field layout. It also removes from bta_jv_act.cc:bta_jv_rfcomm_connect the BTM_SetSecurityLevel(true, \"RFC_MUX\", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0) call that was gated by rfcomm_always_use_mitm(), so the RFCOMM client no longer registers a MUX-level security record carrying the client's sec_mask.",
      "Android 16 (2ad8e1e0e0e5, 'RFCOMM MUX connection request must not start bonding'): in btm_sec.cc:btm_sec_is_upgrade_possible, drops the com::android::bluetooth::flags::upgrade_temp_bonding_on_auth_req() gate and replaces the originator-only BTM_SEC_OUT_AUTHENTICATE test with the direction-aware (is_originator ? BTM_SEC_OUT_AUTHENTICATE : BTM_SEC_IN_AUTHENTICATE). It removes the upgrade_temp_bonding_on_auth_req flag from flags/security.aconfig and removes the unconditional BTM_SetSecurityLevel RFC_MUX call from bta_jv_act.cc:bta_jv_rfcomm_connect.",
      "Android 17 (92611bfc4d26 in android-17.0.0_r1): the same btm_sec.cc addition as shown for 14."
     ],
     "completeness": [
      "The new check covers both directions (originator via BTM_SEC_OUT_AUTHENTICATE, responder via BTM_SEC_IN_AUTHENTICATE), unlike the earlier flagged variant on Android 16, which covered the originator only.",
      "The check sits only inside the BTM_SEC_LINK_KEY_KNOWN branch. When no link key is known, is_possible is already true, so that path needs no change.",
      "The check looks only at the authenticate bit. MITM requirements are still handled by the existing MITM logic. A requirement that has neither bit (for example encryption only) is not affected by this change.",
      "The Android 14 backport changes only btm_sec.cc. The bta_jv_act.cc RFC_MUX BTM_SetSecurityLevel removal that is part of the 15 and 16 fixes is not in it, and the material does not show whether that code exists on 14."
     ],
     "behavior_changes": [
      "When a peer is temporarily bonded, a link key is known and the service requires authentication in the relevant direction, btm_sec_is_upgrade_possible now returns true. According to the code comment, this is meant to trigger pairing instead of continuing on the existing temporary bond.",
      "On Android 15 and 16, bta_jv_rfcomm_connect no longer sets a BTM_SEC_SERVICE_RFC_MUX security record with the client's sec_mask (16 commit title: 'RFCOMM MUX connection request must not start bonding').",
      "On Android 16 the aconfig flag upgrade_temp_bonding_on_auth_req is deleted, so the behaviour no longer depends on a flag."
     ],
     "preconditions": [
      "A peer with an existing device record whose sec_flags include BTM_SEC_LINK_KEY_KNOWN and whose bond type is temporary (is_bond_type_temporary()).",
      "A service access whose security_required includes BTM_SEC_OUT_AUTHENTICATE (local device is originator) or BTM_SEC_IN_AUTHENTICATE (local device is responder).",
      "The code is reached through the link-key upgrade decision in btm_sec.cc. The context shows btm_sec_check_upgrade directly after it, described as checking whether the existing link key needs an upgrade, but the material does not show its body or any other callers."
     ],
     "per_major": {
      "14": "Adapted fix in btm_sec.cc only, using the old field names p_dev_rec->security_required / p_dev_rec->is_bond_type_temporary() and BTM_TRACE_DEBUG logging. Commit 2e2a7bf09a3f in android-security-14.0.0_r28, explicitly a backport of ag/31042219. 4/4 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: the same btm_sec.cc check using p_dev_rec->sec_rec.* fields, plus removal of the rfcomm_always_use_mitm()-gated RFC_MUX BTM_SetSecurityLevel block in bta_jv_act.cc:bta_jv_rfcomm_connect. Commit c1a809d2333e in android-security-15.0.0_r16. Only 3/8 lines of the patched region are present at android-security-15.0.0_r17; the material does not explain the partial match.",
      "16": "Adapted fix: the check already existed behind flag upgrade_temp_bonding_on_auth_req and covered the originator only. Commit 2ad8e1e0e0e5 in android-security-16.0.0_r7 removes the flag (from the code and from flags/security.aconfig), adds the BTM_SEC_IN_AUTHENTICATE direction, and removes the unconditional RFC_MUX BTM_SetSecurityLevel call in bta_jv_rfcomm_connect. Only 4/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "The same btm_sec.cc addition is recorded as commit 92611bfc4d26 in the android-17.0.0_r1 base. However, 0/4 patched lines are present at android-security-17.0.0_r1, so the material does not show the current form of the check on 17."
     },
     "backport_notes": [
      "The field layout differs by release. 14 (and the 17 diff as shown) use p_dev_rec->security_required and p_dev_rec->is_bond_type_temporary(); 15 and 16 use p_dev_rec->sec_rec.security_required and p_dev_rec->sec_rec.is_bond_type_temporary(). If a tree already carries the flagged upgrade_temp_bonding_on_auth_req variant, the change needed is to drop the flag gate and replace BTM_SEC_OUT_AUTHENTICATE with the OUT/IN bit selected by is_originator.",
      "The bta_jv_act.cc RFC_MUX BTM_SetSecurityLevel removal is part of the 15 and 16 fixes but not of the 14 backport. Check whether the target tree has that block in bta_jv_rfcomm_connect before deciding whether to carry it."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not show the caller(s) of btm_sec_is_upgrade_possible or how is_possible turns into starting pairing.",
      "Why the Android 15 and 16 checks show only partial matches (3/8, 4/8), and why Android 17 shows 0/4 at android-security-17.0.0_r1 even though it is resolved in the base. The 17 diff also uses the Android 14 field names, which the material does not reconcile.",
      "Whether Android 14's bta_jv_rfcomm_connect contains the RFC_MUX BTM_SetSecurityLevel block removed on 15/16.",
      "Whether the affected path is limited to BR/EDR; the material does not state the transport.",
      "The concrete trigger beyond what the diff implies (a temporary bond plus a service that requires authentication)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0046": {
     "cve": "CVE-2026-0046",
     "summary": "The fix makes the Letterbox input window (InputInterceptor in Letterbox.java) report the surrounded app's pid/uid as its owner instead of system_server's, so the input layer's obscured-touch (tapjacking) checks no longer treat the letterbox as a trusted system window.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/Letterbox.java)",
     "weakness": "Incorrect ownership/trust attribution of an input window (the letterbox InputWindowHandle was owned by the system UID), which bypassed obscured-touch protection",
     "root_cause": "In the Letterbox.InputInterceptor constructor, the InputWindowHandle created for the letterbox black bars was given ownerPid = WindowManagerService.MY_PID and ownerUid = WindowManagerService.MY_UID, with inputConfig NOT_FOCUSABLE | SLIPPERY and layoutParamsType TYPE_INPUT_CONSUMER. Per the commit message, the letterbox window was therefore considered a trusted system window. A touch could slip out of the letterbox bar and reach activities underneath that the user could not see because the letterbox covered them, and the existing tapjacking prevention did not work correctly for those touches.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/Letterbox.java : InputInterceptor(String namePrefix, WindowState win): replaces mWindowHandle.ownerPid = WindowManagerService.MY_PID / ownerUid = WindowManagerService.MY_UID with mWindowHandle.ownerPid = win.mSession.mPid / ownerUid = win.mSession.mUid. The letterbox input window now takes on the identity of the app window it surrounds.",
      "With the app's uid as owner, the letterbox is no longer treated as a trusted system window for input security checks, so the obscured-touch logic applies (per the commit message and code comment).",
      "No other fields change: token, layoutParamsType, dispatchingTimeoutMillis, scaleFactor and inputConfig (NOT_FOCUSABLE | SLIPPERY) are untouched. The TapEventReceiver is still created with the WindowManagerService context."
     ],
     "completeness": [
      "Covers the single place where the letterbox InputWindowHandle is constructed (the Letterbox.InputInterceptor constructor). All letterbox surfaces created through this interceptor get the change.",
      "The fix changes only ownership attribution. It does not change the SLIPPERY input config, so touches can still slip out of the letterbox. The mitigation relies on the downstream obscured-touch logic now treating the letterbox as an app window.",
      "Relies on win.mSession being non-null for the WindowState passed to InputInterceptor. No null handling was added."
     ],
     "behavior_changes": [
      "The letterbox input window is now reported to the input system with the surrounded app's pid/uid rather than system_server's, so trust-based input decisions (obscured/untrusted-touch handling) treat it as belonging to that app.",
      "The material does not show which other consumers of ownerPid/ownerUid for this handle are affected."
     ],
     "preconditions": [
      "An activity is displayed with letterboxing (black bars), so Letterbox.InputInterceptor creates an input window over regions that cover other windows.",
      "Another activity's window lies beneath the letterbox region and a touch slips out of the letterbox (SLIPPERY input config), per the commit message.",
      "Requires user interaction (touch), as described by the commit message. The material does not indicate any special permission."
     ],
     "per_major": {
      "14": "same fix: 8284ae9c703a in android-security-14.0.0_r28, a single-file change in Letterbox.java; 7/7 lines present at android-security-14.0.0_r29.",
      "15": "same fix: 8eb831ece18d in android-security-15.0.0_r16, identical hunk at a different line offset; 7/7 lines present at android-security-15.0.0_r17.",
      "16": "same fix in different context: 678475b0c23c in android-security-16.0.0_r7. Same ownerPid/ownerUid replacement, but the surrounding lines differ (layoutParamsType is a conditional TYPE_APPLICATION_OVERLAY/TYPE_INPUT_CONSUMER, and inputConfig uses Flags.scrollingFromLetterbox() ? SPY : SLIPPERY); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 5/7 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-commit, two-line functional change: in the Letterbox.InputInterceptor constructor, replace WindowManagerService.MY_PID/MY_UID with win.mSession.mPid/win.mSession.mUid. On Android 16 the surrounding layoutParamsType/inputConfig lines differ (flagged SPY vs SLIPPERY), but the change touches the same two assignments.",
      "The three Cherrypick-From references in the commit message point to internal changes. The AOSP commit contains only the Letterbox.java hunk, so the material shows no additional dependency."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream input check (in InputDispatcher or WindowManager) that uses ownerUid to decide trusted vs obscured is not shown in the material.",
      "Whether the three internal Cherrypick-From changes included anything beyond the Letterbox.java hunk.",
      "State of the ownerPid/ownerUid assignment in Android 17 (only 5/7 lines of the region present at android-security-17.0.0_r1).",
      "Whether other system-created input-consumer handles also use WindowManagerService.MY_PID/MY_UID with comparable exposure (the index search failed).",
      "Whether the change affects ANR/dispatch-timeout attribution for the letterbox channel."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0048": {
     "cve": "CVE-2026-0048",
     "summary": "WindowState.hide() now hides a force-hidden window (non-system overlay, suspended owner, app-op hidden, or permanently hidden) immediately when that window is already playing an exit animation, and cancels any in-progress animation, instead of letting the hide wait for the previous exit animation to finish.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Logic error in visibility policy enforcement: a forced hide was deferred behind an already-running exit animation, so the window could stay visible until that animation finished.",
     "root_cause": "In WindowState.hide(boolean doAnimation, boolean requestAnim), when doAnimation was true the code only cleared doAnimation if !mToken.okToAnimate(). Per the commit message, if the window was already playing an exit animation, WSA#applyAnimationLocked skipped the hide animation and the hide waited for the previous exit animation to finish, which could take a long time. As a result, a window that policy had decided to hide (the commit's test uses an activity launched with the hide-overlay flag) stayed on screen during that time.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowState.java:hide(): inside the existing `if (doAnimation)` block, after the okToAnimate check, a new block runs when any of mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility or mPermanentlyHidden is set.",
      "If isAnimating() is true and mAnimatingExit is true, doAnimation is set to false, so `current` is taken from isLegacyPolicyVisibility() and the window is hidden immediately rather than through a hide animation.",
      "Whenever isAnimating() is true (exit animation or not), cancelAnimation() is called; for non-exit animations doAnimation remains true.",
      "Single commit, one file, 10 added lines; identical diff on all branches."
     ],
     "completeness": [
      "Covers only WindowState.hide() and only when one of the four policy-hide flags is set; hide() calls for other reasons with an in-progress exit animation keep the old behavior (whether that matters is not shown).",
      "Immediate hide (doAnimation=false) is applied only when mAnimatingExit is true; for other running animations the fix only calls cancelAnimation() and keeps doAnimation.",
      "The new block is inside `if (doAnimation)`, so callers that already pass doAnimation=false are unaffected.",
      "show() is not changed; per the surrounding context it already returns early for all four flags, so the fix addresses the hide side only.",
      "No test file is changed in the commit; the Test: line describes a manual scenario."
     ],
     "behavior_changes": [
      "A window that is being force-hidden while playing an exit animation is hidden immediately with no hide animation.",
      "Any running animation on a window being hidden for those four policy reasons is cancelled via cancelAnimation(), which may cut short an exit or other animation.",
      "No new error codes or API changes."
     ],
     "preconditions": [
      "WindowState.hide() is invoked with doAnimation=true and mToken.okToAnimate() true.",
      "One of mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility or mPermanentlyHidden is set on the window (the commit title and test target the non-system overlay case, with an activity launched using the hide-overlay flag).",
      "The window is currently animating (isAnimating()) and mAnimatingExit is true, e.g. a window being removed with a long exit animation, per the commit's Test: description."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 7c5437456c5d in android-security-14.0.0_r28 (cherry-pick, Merged-In I218fcb6eabe0033646f8a99e75a05409ed12c19f); 4/4 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: c9bc382318dd in android-security-15.0.0_r16, identical diff at a different line offset; 4/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: 36a774d72399 in android-security-16.0.0_r7, identical diff; 4/4 lines present at android-security-16.0.0_r8.",
      "17": "same fix: 6ca1d6b26237 in android-17.0.0_r1, identical diff; 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Self-contained single-hunk change in WindowState.hide(), inserted after the `if (!mToken.okToAnimate()) { doAnimation = false; }` check. The four policy flags are already used in show() on Android 14. The diff is identical on 14 through 17 (only line offsets differ). Confirm that mAnimatingExit, the no-argument isAnimating() and cancelAnimation() are available in your fork; the provided context does not show them."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers of hide() with doAnimation=true were involved in the reported issue (the material shows no callers).",
      "Whether, for non-exit animations, the hide animation is now applied after cancelAnimation(); not shown.",
      "Whether cancelAnimation() on an exit-animating window has further side effects (e.g. completing removal of the window); not shown in the material.",
      "Original bug report details (bug 463364410) and exact user-visible impact beyond the bulletin's EoP/High rating."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0055": {
     "cve": "CVE-2026-0055",
     "summary": "PackageInstallerService.createSessionInternal now checks the caller-supplied SessionParams.volumeUuid with StorageManager.convert() when INSTALL_FORCE_VOLUME_UUID is set. Invalid values are rejected with an IllegalArgumentException when the session is created, instead of passing into later install handling.",
     "component": "platform/frameworks/base, services/core PackageInstallerService (package installer session creation)",
     "weakness": "Missing input validation of a caller-controlled volume UUID string (SessionParams.volumeUuid)",
     "root_cause": "In services/core/java/com/android/server/pm/PackageInstallerService.java, createSessionInternal picks the install location in one of three branches. In the INSTALL_FORCE_VOLUME_UUID branch, the code only OR-ed INSTALL_INTERNAL into params.installFlags. It kept params.volumeUuid exactly as the caller supplied it and never checked that it was a well-formed volume UUID. The commit message's test output shows a value such as \"../../../data\" now being rejected. Per the commit message, before the fix such a value was used later in the stack and crashed the system (\"fails early instead of crashing system later\").",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/PackageInstallerService.java: in createSessionInternal, inside the `else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0)` branch and right after `params.installFlags |= PackageManager.INSTALL_INTERNAL;`, the fix calls `StorageManager.convert(params.volumeUuid)` inside a try block.",
      "If StorageManager.convert throws IllegalArgumentException, the fix throws a new IllegalArgumentException with the message \"Invalid volumeUuid value in session params: \" + params.volumeUuid. createSessionInternal then stops before the session is allocated and the synchronized(mSessions) block runs.",
      "The result of the conversion is discarded. The call serves only as a validity check. StorageManager.convert(String) parses a volume UUID string into a UUID and throws IllegalArgumentException on malformed input (general platform knowledge)."
     ],
     "completeness": [
      "Covers only the INSTALL_FORCE_VOLUME_UUID branch of createSessionInternal. The check is a single call added there, and no other file is changed.",
      "The INSTALL_INTERNAL branch (lines 869-872) still keeps params.volumeUuid as the caller supplied it and does not run the new check. The material does not show whether that value is used downstream.",
      "The final else branch overwrites params.volumeUuid with InstallLocationUtils.resolveInstallVolume, so caller input does not reach later code on that path.",
      "The downstream code that crashed is not changed. The fix rejects bad input early at the entry point; it does not harden the later use.",
      "The whole location-selection block sits inside an enclosing conditional that closes at line 897. The material does not show what guards it, so a path that skips the block would not be covered."
     ],
     "behavior_changes": [
      "Creating a session with INSTALL_FORCE_VOLUME_UUID and a volumeUuid that StorageManager.convert rejects now fails immediately with IllegalArgumentException(\"Invalid volumeUuid value in session params: <value>\") instead of succeeding. The commit message shows the `install` shell command reporting this exception.",
      "No change for valid UUIDs or for sessions that do not use INSTALL_FORCE_VOLUME_UUID."
     ],
     "preconditions": [
      "The caller reaches PackageInstallerService.createSessionInternal (for example through PackageInstaller session creation; the commit's test trace shows the `install` shell command path).",
      "SessionParams has INSTALL_FORCE_VOLUME_UUID set and INSTALL_INTERNAL not set, with a caller-controlled volumeUuid string.",
      "The material does not show whether INSTALL_FORCE_VOLUME_UUID requires a permission or is limited to specific callers."
     ],
     "per_major": {
      "14": "same fix: commit 06fb4af542b7 in android-security-14.0.0_r28 (cherry-pick of fe2d4eb0555a), identical 8-line hunk at line 874; 4/4 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 613fa6465e4d in android-security-15.0.0_r16, identical hunk at line 945; 4/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 62ec466efd80 in android-security-16.0.0_r7, identical hunk at line 956; 4/4 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit c17553943026 in android-17.0.0_r1 (base), identical hunk; 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk in createSessionInternal. The diff adds no import, so StorageManager must already be imported in PackageInstallerService.java on the target branch; verify this on your tree. Line numbers differ per branch, so anchor on the `params.installFlags |= PackageManager.INSTALL_INTERNAL;` line inside the INSTALL_FORCE_VOLUME_UUID branch."
     ],
     "confidence": "high",
     "unknowns": [
      "Which downstream code used the invalid volumeUuid and crashed the system (the material only says it crashed 'later').",
      "Whether unprivileged callers can set INSTALL_FORCE_VOLUME_UUID or whether it is permission-gated.",
      "What conditional encloses the location-selection block (closing brace at line 897) and whether any session-creation path bypasses it.",
      "Whether the INSTALL_INTERNAL branch ever uses the caller-supplied volumeUuid downstream.",
      "Whether StorageManager is already imported in PackageInstallerService.java on each branch (imports are not shown)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0056": {
     "cve": "CVE-2026-0056",
     "summary": "Adds two validations of ResStringPool_header.styleCount in ResStringPool::setTo (libs/androidfw/ResourceTypes.cpp) so that the declared style offset array cannot overflow its size computation or extend past stringsStart, returning BAD_TYPE for such chunks.",
     "component": "platform/frameworks/base, libs/androidfw (ResStringPool string pool parser)",
     "weakness": "Missing bounds check on a count from the input (styleCount) that can lead to out-of-bounds reads beyond the string pool chunk; the fix also guards against a potential integer overflow in styleCount * sizeof(uint32_t).",
     "root_cause": "Before the fix, ResStringPool::setTo did not validate the header's styleCount against the chunk layout. The visible context shows checks on stringCount (headerSize + stringCount*sizeof(uint32_t) <= size), stringsStart and stylesStart, but none relating styleCount to the space between the style offsets (mEntryStyles) and stringsStart. Per the commit message, this could allow out-of-bounds reads beyond the bounds of the string pool chunk. The exact read site is not shown in the material.",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo, right after an existing stylesStart BAD_TYPE check and before mStylePoolSize is computed: the pool is rejected with mError = BAD_TYPE if mHeader->styleCount > numeric_limits<decltype(styleCount)>::max() / sizeof(uint32_t), so the following multiplication cannot overflow (log: 'potential integer overflow when finding style entries').",
      "Same function: computes styleOffsetsStart = mEntryStyles (as uint8_t) - mHeader (as uint8_t), the byte offset of the style offsets inside the chunk, and rejects with BAD_TYPE if mHeader->styleCount * sizeof(uint32_t) > (mHeader->stringsStart - styleOffsetsStart). The declared style offset array must therefore fit before stringsStart (log: 'style offsets extend past style data start').",
      "libs/androidfw/tests/ResourceTypes_test.cpp (new) and libs/androidfw/Android.bp: adds two tests to libandroidfw_tests. Each builds a valid one-style pool with StringPool::FlattenUtf8, sets styleCount to UINT32_MAX in one test and to 2 in the other, and asserts that setTo returns BAD_TYPE."
     ],
     "completeness": [
      "Covers two cases: an overflow of styleCount * sizeof(uint32_t), and a styleCount that declares more offset entries than fit before stringsStart. Both are rejected when setTo parses the pool.",
      "The second check uses an unsigned subtraction (mHeader->stringsStart - styleOffsetsStart). The material does not show an explicit check that stringsStart >= styleOffsetsStart, so it is not visible whether earlier validation guarantees that order.",
      "The fix does not change how the style data area is sized (mStylePoolSize derived from header.size - stylesStart), and it leaves the existing stylesStart checks as they were."
     ],
     "behavior_changes": [
      "ResStringPool::setTo now returns BAD_TYPE (and sets mError) for string pool chunks whose styleCount exceeds the type maximum / 4 or whose style offset array would extend past stringsStart. Two new ALOGW messages are logged.",
      "Resource data containing such malformed string pools is rejected; the commit's manual test notes that an APK with an invalid styleCount failed to install.",
      "New test file tests/ResourceTypes_test.cpp added to the libandroidfw_tests cc_test."
     ],
     "preconditions": [
      "A string pool chunk with a nonzero styleCount parsed by ResStringPool::setTo, for example while installing an APK (per the commit's manual test).",
      "The chunk's styleCount must not match the actual layout: more declared style offsets than fit before stringsStart, or a value large enough to overflow the size computation."
     ],
     "per_major": {
      "14": "same fix: f1c0bc9dd174 in android-security-14.0.0_r28, ResourceTypes.cpp hunk at @@ -652 plus the test file; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: e80f76ded67a in android-security-15.0.0_r16, identical code change; only the hunk offsets differ (ResourceTypes.cpp @@ -670); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: d5a52f737cab in android-security-16.0.0_r7, identical code change (@@ -673); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: d69b0c6010e5 in android-17.0.0_r1, diff identical to Android 14; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. The ResourceTypes.cpp hunk is identical on 14 through 17 and anchors on the existing stylesStart BAD_TYPE check just before the mStylePoolSize assignment in ResStringPool::setTo. The new test uses androidfw/StringPool.h, androidfw/Util.h and libgmock (libgmock is already in libandroidfw_tests static_libs). If a target branch lacks these, the ResourceTypes.cpp change can be taken alone."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how mEntryStyles is computed or where style offsets are read (e.g. styleAt), so the exact out-of-bounds read site is not shown.",
      "The enclosing condition of the new checks (for example whether they run only when styleCount > 0) is not visible in the provided context.",
      "Whether earlier validation guarantees that stringsStart is at or after the end of the style offset area (which would make the unsigned subtraction in the new check safe) is not visible.",
      "Which caller paths (install-time parsing vs. runtime resource loading) were the reported trigger is not stated, apart from the commit's manual test note about APK installation."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0059": {
     "cve": "CVE-2026-0059",
     "summary": "Adds a remaining-buffer-length parameter and check to sdpu_build_attrib_seq, and adds buffer accounting to process_service_attr_rsp, so the SDP client can no longer write an attribute ID sequence past the end of the SDP_DATA_BUF_SIZE request buffer.",
     "component": "platform/packages/modules/Bluetooth, system/stack/sdp (SDP client request builder: sdp_discovery.cc / sdp_utils.cc)",
     "weakness": "Missing bounds check leading to a heap out-of-bounds write: the packet builder writes an unbounded attribute sequence into a fixed-size BT_HDR buffer",
     "root_cause": "sdpu_build_attrib_seq(p_out, p_attr, num_attrs) wrote a 2- or 3-byte data element header and then num_attrs * 3 bytes (one 3-byte entry per attribute filter) into p_out without knowing how much space remained. Its callers in sdp_discovery.cc, process_service_attr_rsp and process_service_search_attr_rsp, build the request into a fixed BT_HDR buffer of SDP_DATA_BUF_SIZE allocated with osi_malloc. process_service_attr_rsp did no bytes_left accounting at all. process_service_search_attr_rsp tracked bytes_left for its base header but did not pass it to the attribute sequence builder. With a large p_ccb->p_db->num_attr_filters (plus the continuation-state bytes taken from the peer reply, *p_reply), the sequence could be written past the end of the allocated buffer.",
     "fix_analysis": [
      "system/stack/sdp/sdpint.h and sdp_utils.cc:sdpu_build_attrib_seq: the signature gains uint16_t& bytes_left. The function now computes content_len (5 for the wildcard case, otherwise num_attrs * 3) and header_len (3 if content_len > 255, otherwise 2). If bytes_left < content_len + header_len, it hits DCHECK(0) and returns p_out unchanged without writing anything. Otherwise it subtracts header_len + content_len from bytes_left and emits the header and entries as before. content_len and header_len are int, so num_attrs * 3 cannot wrap the way the old uint16_t xx could for large num_attrs.",
      "sdp_discovery.cc:process_service_attr_rsp: adds uint16_t bytes_left = SDP_DATA_BUF_SIZE and computes base_bytes = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 3 (PDU header) + 2 (param len) + 4 (handle) + 2 (max attr count) + 1 + continuation length (*p_reply if p_reply is set). If base_bytes > bytes_left, it calls sdp_disconnect(p_ccb, tSDP_STATUS::SDP_INVALID_CONT_STATE), frees p_msg and returns. Otherwise it subtracts base_bytes and passes bytes_left to both sdpu_build_attrib_seq calls (filtered and wildcard).",
      "sdp_discovery.cc:process_service_search_attr_rsp: the existing base_bytes expression is rewritten as 3u + 2u + 2u + 1u + cont, which gives the same total as the old 3u + 2u + 3u + cont, and the comments are corrected. The bytes_left that was already there is now passed to both sdpu_build_attrib_seq calls.",
      "sdp_discovery.cc:sdp_snd_service_search_req: the base_bytes expression is only reformatted and commented. The total is unchanged, and this function already passed bytes_left to sdpu_build_uuid_seq.",
      "sdp_utils.cc: the rest of the diff (sdpu_find_ccb_by_cid, sdpu_find_ccb_by_db, sdpu_allocate_ccb, sdpu_get_active_ccb_cid, sdpu_process_pend_ccb_same_cid, sdpu_process_pend_ccb_new_cid, sdpu_clear_pend_ccb, sdpu_is_base_uuid, sdpu_get_attrib_seq_len) only moves the uint16_t xx loop variable into the for statement. No functional change."
     ],
     "completeness": [
      "Both callers of sdpu_build_attrib_seq in sdp_discovery.cc (process_service_attr_rsp and process_service_search_attr_rsp) are updated. Because the header prototype changes, any other caller fails to compile instead of silently bypassing the check.",
      "The check in sdpu_build_attrib_seq is a DCHECK plus an early return. In builds where DCHECK is a no-op, the function returns p_out unchanged and the callers shown in the diff do not test for failure. Request assembly therefore continues without the attribute sequence. The material does not show the send path.",
      "In process_service_attr_rsp, the new base_bytes check calls sdp_disconnect and frees the buffer. The equivalent failure inside sdpu_build_attrib_seq does not disconnect.",
      "The diff adds no upper bound on num_attr_filters where the discovery DB is populated. It only prevents the overflow at serialisation time."
     ],
     "behavior_changes": [
      "process_service_attr_rsp now disconnects the SDP connection with SDP_INVALID_CONT_STATE if the fixed request header plus the continuation state cannot fit in SDP_DATA_BUF_SIZE. Before the fix there was no check.",
      "When the attribute filter sequence does not fit in the remaining buffer, sdpu_build_attrib_seq writes nothing (and hits the DCHECK in debug builds). The callers carry on building the ServiceAttribute or ServiceSearchAttribute request without the attribute ID list instead of overflowing the buffer.",
      "Internal API change: sdpu_build_attrib_seq takes an extra uint16_t& bytes_left argument."
     ],
     "preconditions": [
      "The device is acting as an SDP client: a local discovery was started with a discovery DB (p_ccb->p_db), and the code runs in process_service_search_attr_rsp or process_service_attr_rsp. Both build the next request while handling a peer's ServiceSearchAttribute or ServiceAttribute response, and process_service_search_attr_rsp also builds the first request.",
      "How far the write overflows depends on p_ccb->p_db->num_attr_filters (set by the local discovery DB) and on the continuation-state length read from the peer reply (*p_reply). The material does not say which values exceed SDP_DATA_BUF_SIZE."
     ],
     "per_major": {
      "14": "Same fix: ce4be454822b in android-security-14.0.0_r28, a [RESTRICT AUTOMERGE] cherry-pick of ce1de9ef78ca. It touches sdp_discovery.cc, sdp_utils.cc and sdpint.h.",
      "15": "Same fix (59f3f429e37e in android-security-15.0.0_r16). The logic is identical. The hunks follow the 15 file layout, with process_service_search_attr_rsp before process_service_attr_rsp, and sdpu_allocate_ccb uses *p_ccb = {} on this branch.",
      "16": "Adapted fix (94566a29d020 in android-security-16.0.0_r7). The same logic is applied to reformatted code (braced if/else, tSDP_STATE:: enum, tSDP_STATUS::SDP_INVALID_CONT_STATE in both callers). It also applies the cosmetic loop change to sdpu_dump_all_ccb.",
      "17": "Same fix (5288e3e85ba9 in android-17.0.0_r1, [RESTRICT AUTOMERGE]). The sdp_discovery.cc and sdpint.h hunks and blob ids match the 14 patch. The sdp_utils.cc blob ids and line offsets differ, but the logic is the same."
     },
     "backport_notes": [
      "Single commit with no dependency. The parts that matter are the sdpu_build_attrib_seq signature and body change, the sdpint.h prototype, the two call sites, and the new bytes_left/base_bytes block in process_service_attr_rsp. The loop-variable refactors across sdp_utils.cc can be dropped. Because the prototype changes, any out-of-tree callers (tests, fuzzers) must be updated to pass a bytes_left reference."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not give the value of SDP_DATA_BUF_SIZE or the maximum num_attr_filters the discovery DB allows, so it does not establish exactly when the old code overflowed.",
      "The material does not say whether a remote peer alone can trigger this or whether it needs a local client that requests many attribute filters. The bulletin classifies it as RCE, High.",
      "The send path after sdpu_build_attrib_seq returns early is not shown, so it is unclear whether a request without the attribute list is actually transmitted.",
      "The meaning of the per-major figures ('5/8', or '6/8' on 16, lines of the patched region present at the latest security tags) is not explained. The resolver marks all four majors as resolved."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0060": {
     "cve": "CVE-2026-0060",
     "summary": "Gates the Developer Options \"Enable ANGLE\" switch in Settings behind a debug system property (debug.graphics.angle.developeroption.enable), keeps the switch usable when ANGLE is already active so it can be turned off, relabels the option as experimental, and rewrites the summary and reboot-dialog warnings (back up data; a failed restart needs a factory reset).",
     "component": "platform/packages/apps/Settings, Developer Options graphics driver (GraphicsDriverEnableAngleAsSystemDriverController)",
     "weakness": "Missing safeguard on a dangerous experimental configuration toggle (developer option tied to the system OpenGL ES driver property persist.graphics.egl); the bulletin classifies the impact as DoS",
     "root_cause": "In GraphicsDriverEnableAngleAsSystemDriverController.updateState, the \"Enable ANGLE\" SwitchPreference was left enabled on any device where ro.gfx.angle.supported equals \"true\"; its checked state reflects whether persist.graphics.egl is \"angle\". Per the reboot dialog string, changing the system OpenGL ES driver requires a reboot. The option had no additional guard, and the UI text (\"Enable ANGLE\") did not say the feature was experimental. The replacement strings state that if restarting fails after the change, a factory reset is needed to use the device.",
     "fix_analysis": [
      "Commit 2097b24534af, src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java: adds PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION = \"debug.graphics.angle.developeroption.enable\" and a private isAngleDeveloperOptionEnabled() that reads it via mSystemProperties.getBoolean(key, false). At the end of updateState() it calls mPreference.setEnabled(false) whenever the property is not true, regardless of ANGLE support state. It also adds a Log.v of the current persist.graphics.egl value next to where mShouldToggleSwitchBackOnRebootDialogDismiss is set to true.",
      "Commit 2097b24534af, GraphicsDriverSystemPropertiesWrapper.java: adds boolean getBoolean(String key, boolean def) to the wrapper interface. The Injector's anonymous implementation in the controller forwards to SystemProperties.getBoolean. Robolectric and JUnit tests are updated to set or mock the property to true so existing tests keep exercising the switch.",
      "Commit 2097b24534af, res/values/strings.xml: retitles enable_angle_as_system_driver to \"Experimental: Enable ANGLE\" and rewrites enable_angle_as_system_driver_summary as a warning about experimental status and camera/video app compatibility.",
      "Commit 468f22c59b52, GraphicsDriverEnableAngleAsSystemDriverController.updateState: relaxes the guard to `if (!isAngleDeveloperOptionEnabled() && !((SwitchPreference) mPreference).isChecked()) mPreference.setEnabled(false);`. When persist.graphics.egl is already \"angle\", the switch stays enabled and can be turned off without setting the debug property (which, per the commit message, resets on reboot). When ANGLE is off and the property is unset, the switch stays disabled. Adds JUnit test updateState_DeveloperOptionPropertyIsFalse covering both states.",
      "Commit 72801bd6f244, res/values/strings.xml: rewrites an intermediate version of enable_angle_as_system_driver_summary and reboot_dialog_enable_angle_as_system_driver. The summary now tells the user to back up important data; the dialog says that if restarting fails, a factory reset will be needed to use the device."
     ],
     "completeness": [
      "The guard is applied only to the preference's enabled state in updateState(). No change to the code path that actually sets persist.graphics.egl appears in these diffs, so protection relies on the preference being disabled in the UI.",
      "The debug property check defaults to false, so on a stock device the switch is disabled unless the property is set (for example with adb setprop). Per commit 468f22c59b52's message, the debug property does not persist across reboot.",
      "Commit 468f22c59b52 deliberately leaves the switch enabled when ANGLE is already the active driver, so it can be turned off. onDeveloperOptionsSwitchDisabled still resets persist.graphics.egl via toggleAngleAsSystemDriver(false) when developer options are turned off.",
      "Commit 72801bd6f244 changes only strings and has no functional effect."
     ],
     "behavior_changes": [
      "The \"Enable ANGLE\" developer option is greyed out by default unless debug.graphics.angle.developeroption.enable is true or ANGLE is already active (persist.graphics.egl == \"angle\" on an ANGLE-supported device).",
      "The title changes to \"Experimental: Enable ANGLE\". The summary and reboot dialog now warn to back up data and say that a failed restart requires a factory reset.",
      "New verbose log line \"Value of persist.graphics.egl is: ...\" under tag GraphicsEnableAngleCtrl.",
      "GraphicsDriverSystemPropertiesWrapper gains a getBoolean method; every implementation of the interface must provide it."
     ],
     "preconditions": [
      "Developer options enabled and the Settings developer options screen reachable (the controller is constructed with a DevelopmentSettingsDashboardFragment).",
      "Device reports ro.gfx.angle.supported == \"true\" (otherwise the switch was already disabled).",
      "After the fix, enabling ANGLE while it is off additionally requires debug.graphics.angle.developeroption.enable to be true."
     ],
     "per_major": {
      "14": "Same fix: three cherry-picks into android-security-14.0.0_r28 (2097b24534af debug-property guard and strings, 468f22c59b52 relaxed guard, 72801bd6f244 warning strings). An intermediate strings change (the backup and factory-reset wording seen in 72801bd6f244's pre-image) is not in the material. 5/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Resolved by bug_id at android-security-15.0.0_r16 with 2b5834d47f28, a translations import of the intermediate backup and factory-reset warning wording; no Java change is in the shown diff. 6/8 lines of the patched region are present at android-security-15.0.0_r17. Whether the Java guard is present on this branch is not shown.",
      "16": "Resolved by bug_id at android-security-16.0.0_r7 with 32c6cb147d3e, the string-only \"Update enable ANGLE warning message\" change (same content as 72801bd6f244). Its pre-image already has the \"Experimental: Enable ANGLE\" title and the intermediate backup-warning summary. 6/8 lines are present at android-security-16.0.0_r8. The Java guard commits for this branch are not shown.",
      "17": "Resolved as base at android-17.0.0_r1 with 1c2cf9256912, a translations import of the intermediate backup and factory-reset wording only. 4/8 lines of the patched region are present at android-security-17.0.0_r1. Which Java commits are in this branch is not shown."
     },
     "backport_notes": [
      "Apply in order: 2097b24534af (adds getBoolean to GraphicsDriverSystemPropertiesWrapper, the property constant and the updateState guard), then 468f22c59b52 (rewrites the guard condition). Then apply the strings: 72801bd6f244's pre-image includes an intermediate backup and factory-reset wording that is not in 2097b24534af, so expect a conflict in strings.xml. Any custom or test implementation of GraphicsDriverSystemPropertiesWrapper must implement getBoolean; Robolectric tests need ShadowSystemProperties.override(PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION, \"true\") as in the patch.",
      "The strings.xml hunks sit at different line offsets per branch (about 10511 to 10513 on 14, 12304 on 16), and translated values-*/strings.xml files are updated by separate translation-import commits. Backports without the translation imports fall back to the English default strings (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact failure mode behind the bulletin's DoS rating is not described. The new strings only say that if restarting fails after the change, a factory reset is needed.",
      "Whether the Java guard commits (2097b24534af, 468f22c59b52) landed on the 15, 16 and 17 branches, and under which commit ids. Only string and translation commits are shown for those branches.",
      "Which commit introduced the intermediate backup and factory-reset strings that form 72801bd6f244's pre-image on Android 14.",
      "The enclosing method of the added Log.v call (the context shows only the mShouldToggleSwitchBackOnRebootDialogDismiss = true line).",
      "The content of onPreferenceChange, and whether any path other than this preference can set persist.graphics.egl to \"angle\"."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0061": {
     "cve": "CVE-2026-0061",
     "summary": "WindowState now applies the server-side visibility policy (hide/show) to the WindowState's own mSurfaceControl through the pending transaction, in addition to the existing mWinAnimator handling, so policy visibility no longer depends only on a surface the client can access.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Policy enforced on a client-accessible resource: the server visibility policy did not control the server-owned WindowState surface (improper enforcement / missing server-side control)",
     "root_cause": "In WindowState, the legacy policy visibility (LEGACY_POLICY_VISIBILITY) was applied through mWinAnimator.hide(...) / mWinAnimator.applyAnimationLocked(...), and the WindowState's own surface (mSurfaceControl) was never hidden or shown by the policy code in checkPolicyVisibilityChange(), show() or hide(). Per the commit message, the client side can access the ViewRootImpl surface and change its visibility, so the server-side policy should control the surface the client cannot access (the WindowState surface). The commit's test scenario is an overlay window that should not be visible when Settings is launched.",
     "fix_analysis": [
      "WindowState.java:checkPolicyVisibilityChange(): after mWinAnimator.hide(...) when !isVisibleByPolicy(), additionally call getPendingTransaction().hide(mSurfaceControl) if mSurfaceControl != null.",
      "WindowState.java:show(): after setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY) and mLegacyPolicyVisibilityAfterAnim = true, call getPendingTransaction().show(mSurfaceControl) if mSurfaceControl != null, before the optional TRANSIT_ENTER animation.",
      "WindowState.java:hide(): inside the hide block, after the mFocusMayChange bookkeeping ('WindowState.hideLw: setting mFocusMayChange true'), call getPendingTransaction().hide(mSurfaceControl) if mSurfaceControl != null.",
      "WindowStateTests.java:testIsOnScreen_hiddenByPolicy: adds verify(mTransaction).hide(window.mSurfaceControl) after hide(false,false), clearInvocations(mTransaction), and verify(mTransaction).show(window.mSurfaceControl) after show(false,false)."
     ],
     "completeness": [
      "Adds mSurfaceControl hide/show in three places in WindowState: hide(), checkPolicyVisibilityChange() (whose debug message refers to policy visibility changing after animation), and show().",
      "The test only verifies the new surface calls for the non-animated hide/show; the animated hide path is not checked for the mSurfaceControl hide.",
      "All three additions are guarded by mSurfaceControl != null; the material does not show how a WindowState whose surface is created after a policy hide is handled.",
      "Only these three methods are changed; any other code that sets or clears policy visibility flags directly is not touched in the diff.",
      "The client-side ability to change the ViewRootImpl surface visibility is not removed; per the commit message the fix moves policy control onto the WindowState surface instead."
     ],
     "behavior_changes": [
      "Policy-hidden windows now also have their WindowState surface (mSurfaceControl) hidden in the pending transaction.",
      "Policy show now explicitly issues a show on mSurfaceControl in the pending transaction.",
      "Additional hide/show calls on the WindowState SurfaceControl per policy transition; no new error codes or API changes in the diff."
     ],
     "preconditions": [
      "A window whose client can change the visibility of its ViewRootImpl surface (per commit message).",
      "The server applies a policy hide to that window (commit test scenario: an overlay window that should not be visible when Settings is launched).",
      "mSurfaceControl of the WindowState is non-null at the time of the policy transition for the new code to act."
     ],
     "per_major": {
      "14": "same fix: b53981d87f80 in android-security-14.0.0_r28 (Cherrypick-From 52bfa5a16ecc, Change-Id I326fcaf71f901a11cf4aee3d734494090a1d0fb3); context uses mWinAnimator.hide(getGlobalTransaction(), ...). 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: 0922d84b1c64 in android-security-15.0.0_r16; same three code hunks, surrounding context uses getPendingTransaction() for mWinAnimator.hide and the test context uses ANIMATION_TYPE_TOKEN_TRANSFORM. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: 4518f78f8b34 in android-security-16.0.0_r7; same three code hunks at different line offsets. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: e58fd9cd11e4 in android-17.0.0_r1 (base); same three code hunks. At android-security-17.0.0_r1 the resolver reports 7/8 lines of the patched region present; the material does not show which line differs."
     },
     "backport_notes": [
      "Single self-contained commit; the three code hunks are identical across 14 to 17 apart from line offsets. Surrounding context differs only in whether mWinAnimator.hide() takes getGlobalTransaction() (14, 17) or getPendingTransaction() (15, 16); the new lines always use getPendingTransaction().",
      "The test change adds verify/clearInvocations on mTransaction in testIsOnScreen_hiddenByPolicy; the test context differs on 15/16 (ANIMATION_TYPE_TOKEN_TRANSFORM)."
     ],
     "confidence": "high",
     "unknowns": [
      "Exactly how the client side changes the ViewRootImpl surface visibility is not shown (only stated in the commit message).",
      "Which policy path triggers hide() in the reported overlay/Settings scenario is not shown in the diff.",
      "Whether the hide() hunk is in the animated or non-animated branch, and whether checkPolicyVisibilityChange() covers the animated hide case for mSurfaceControl.",
      "The surface hierarchy between mSurfaceControl and the client surface.",
      "Why the Android 17 security tag shows 7/8 rather than 8/8 lines of the patched region.",
      "Whether a surface created after a policy hide (mSurfaceControl null at hide time) inherits the hidden state."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0067": {
     "cve": "CVE-2026-0067",
     "summary": "In external/dng_sdk, libdng_sdk stops using the Soong-managed ubsan_minimal runtime and instead enables the signed/unsigned integer-overflow sanitizers via cflags with -fno-sanitize-link-runtime, plus a bundled ubsan_throwing_runtime.cpp whose __ubsan_handle_*_overflow handlers throw std::runtime_error, so detected overflows become C++ exceptions rather than a hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and UBSan runtime replacement",
     "weakness": "Integer overflow (signed/unsigned) in dng_sdk arithmetic, detected by UBSan integer-overflow checks and turned into a process abort (DoS). The specific overflowing expression(s) are not in the diff.",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] (Android.bp lines 144-149), and the libdng_sdk module used the ubsan_minimal runtime, which the commit message describes as a hard abort. The library is built with -fexceptions and rtti: true (lines 141-142), and the commit notes that C++ exceptions are enabled, but a detected overflow aborted the process instead. The commit lists seven bug IDs. The fix does not change any arithmetic site; it changes what happens when an overflow check fires.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true }, which the inline comment says is meant to disable the ubsan_minimal runtime so a custom one can be injected.",
      "Android.bp, cc_library libdng_sdk: cflags gain -fsanitize=signed-integer-overflow,unsigned-integer-overflow to enable the same two overflow checks directly, plus -fno-sanitize-link-runtime so no sanitizer runtime is linked.",
      "Android.bp, cc_library libdng_sdk: srcs gains ubsan-replacement/ubsan_throwing_runtime.cpp.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines five extern \"C\" hidden-visibility handlers (__ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow, __ubsan_handle_divrem_overflow), each of which throws std::runtime_error(\"ubsan: <kind>-overflow\").",
      "OWNERS: metadata only (bug component, include of the frameworks/base graphics OWNERS file, owner annotations); no functional effect."
     ],
     "completeness": [
      "The change works at the runtime-handler level rather than per expression, so it applies to any overflow check in libdng_sdk that resolves to one of the five defined handlers.",
      "It does not fix the underlying arithmetic: the overflow is still detected, and it is now reported by an exception. Whether the eventual catch site handles std::runtime_error is not visible in the material.",
      "sanitize.never: true turns off all Soong-managed sanitizers for libdng_sdk, not only the two overflow checks; only the explicitly listed -fsanitize flags remain (general platform knowledge).",
      "An exception thrown from a context that cannot unwind (for example a noexcept function or a destructor) would still terminate the process; the diff does not audit for such contexts.",
      "libdng_sdk-defaults still lists misc_undefined (code context lines 144-149). Only the libdng_sdk module is changed; whether other modules use those defaults is not shown."
     ],
     "behavior_changes": [
      "Signed or unsigned integer overflow detected inside libdng_sdk now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting.",
      "libdng_sdk no longer uses the ubsan_minimal runtime; the five __ubsan_handle_*_overflow symbols are provided inside the module with hidden visibility.",
      "Other Soong-applied sanitizers for libdng_sdk are disabled by sanitize.never: true (general platform knowledge)."
     ],
     "preconditions": [
      "Input processed by libdng_sdk that triggers an arithmetic overflow caught by the enabled checks (the exact field or operation is not shown in the material).",
      "The input is processed through a path that uses libdng_sdk. The commit's test, ImageDecoderTest#testInterestingDngs, points to DNG decoding through ImageDecoder. The index search hits (android_hardware_camera2_DngCreator.cpp, img_utils DngUtils.cpp) do not show that libdng_sdk is used and do not establish reachability."
     ],
     "per_major": {
      "14": "Same fix: commit 42e65547eb33 in android-security-14.0.0_r28 (linked by change_id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). The Android.bp hunk and the runtime file are identical to 15 and 16; the OWNERS diff differs only in its prior content. The code context at android-security-14.0.0_r29 shows the patched block, but the per-major facts report only 3 of 5 lines of the patched region matched.",
      "15": "Same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16, with an identical Android.bp hunk and runtime file. 3 of 4 lines of the patched region matched at android-security-15.0.0_r17.",
      "16": "Same fix: commit 74a5a91d169a in android-security-16.0.0_r7, with an identical Android.bp hunk and runtime file. 3 of 5 lines of the patched region matched at android-security-16.0.0_r8.",
      "17": "Adapted fix: commit 81ded662eee0 in android-17.0.0_r1, resolved by related bug. It is the commit the others list as their first Cherrypick-From. Android.bp has a different layout there: the sanitize/cflags hunk is at line 105, and the new source is added to the existing explicit srcs list (around line 220). OWNERS is not touched, and ubsan_throwing_runtime.cpp is the same. 5 of 6 lines of the patched region matched at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. The runtime file is identical on every branch; only the Android.bp hunk placement varies (14/15/16 replace the one-line cflags in the libdng_sdk module; 17 has a separate srcs hunk). The change relies on the module already having cppflags -fexceptions and rtti: true, which libdng_sdk-defaults provides."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which expression(s) in dng_sdk overflow; the seven referenced bugs are not described in the material.",
      "Where the thrown std::runtime_error is caught, and therefore whether the result is a clean decode failure in all callers.",
      "Whether the five defined handlers cover every handler symbol the compiler references under this flag set (for example _abort or other variants); this is not shown in the material.",
      "Which processes host libdng_sdk on each release, and therefore which process the pre-fix abort brought down.",
      "Why the per-major facts report only partial matches of the patched region in the latest security tags."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0070": {
     "cve": "CVE-2026-0070",
     "summary": "DevicePolicyManagerService.setApplicationHidden() now refuses (returns false) to hide packages listed in a new framework resource array application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings), because per the commit message hiding these critical apps in the work profile may cause failure to boot.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy) plus core/res resources",
     "weakness": "Missing restriction on a privileged management API: setApplicationHidden had no exemption for critical system packages (DoS)",
     "root_cause": "setApplicationHidden() in DevicePolicyManagerService only skipped packages returned by listPolicyExemptAppsUnchecked(mContext). In AOSP the policy_exempt_apps array shown in policy_exempt_apps.xml is empty. Nothing in the shown guard prevented a device policy caller from hiding critical system packages such as com.android.nfc, com.android.settings or com.android.providers.settings. Per the commit message, hiding these in the work profile may cause failure to boot.",
     "fix_analysis": [
      "core/res/res/values/policy_exempt_apps.xml: adds a new non-translatable string-array application_hidden_policy_exempt_apps containing com.android.nfc, com.android.settings and com.android.providers.settings. Its comment describes them as apps that will never be affected by DevicePolicyManager.setApplicationHidden.",
      "core/res/res/values/symbols.xml: registers <java-symbol type=\"array\" name=\"application_hidden_policy_exempt_apps\" /> so the array can be referenced as R.array.application_hidden_policy_exempt_apps.",
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java: adds private isPackageExemptFromHiding(String packageName). It reads R.array.application_hidden_policy_exempt_apps from mContext.getResources() and returns true on an exact String.equals match.",
      "DevicePolicyManagerService.java:setApplicationHidden: the existing guard `if (exemptApps.contains(packageName))` becomes `if (exemptApps.contains(packageName) || isPackageExemptFromHiding(packageName))`. On a match it logs \"setApplicationHidden(): ignoring %s as it's on policy-exempt list\" and returns false.",
      "DevicePolicyManagerService.java:dumpResources: the new array is added to the dump output via dumpResources(pw, mContext, \"application_hidden_policy_exempt_apps\", R.array.application_hidden_policy_exempt_apps)."
     ],
     "completeness": [
      "The new check is added only to setApplicationHidden. No other DevicePolicyManagerService entry point is changed in the diff.",
      "Protection is a static, exact-match list of three package names in AOSP. The dump lists it under \"Other overlayable app resources\", next to policy_exempt_apps and vendor_policy_exempt_apps.",
      "The added condition has no user-type check of its own. Whether the method filters by user elsewhere is not shown.",
      "The diff contains no migration or unhide step for packages that were already hidden before the update."
     ],
     "behavior_changes": [
      "setApplicationHidden() now returns false and emits a Slogf.d log for com.android.nfc, com.android.settings and com.android.providers.settings instead of hiding them. This is the same return path used for policy-exempt apps.",
      "DevicePolicyManagerService dump output gains an application_hidden_policy_exempt_apps entry.",
      "A new framework resource symbol, application_hidden_policy_exempt_apps, is added."
     ],
     "preconditions": [
      "The caller must be able to invoke DevicePolicyManagerService.setApplicationHidden. The Android 15/16 context shows enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, ...) right before the exempt check. The Android 14/17 hunk context does not show the permission check.",
      "The target package is one of the critical system packages. The commit message ties boot failure to hiding them in the work profile."
     ],
     "per_major": {
      "14": "same fix: commit 987b313aefa0 in android-security-14.0.0_r28. Same three-file change, with 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 060fe5399350 in android-security-15.0.0_r16. Same changed lines; only offsets and surrounding context differ (the setApplicationHidden hunk context shows the enforcePermission call, and the symbols.xml context differs). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit dc3785a739bc in android-security-16.0.0_r7. Same change, with 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit 4a97f5239d57 in android-17.0.0_r1 (base). Same change, with 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply all three files together. The Java change references R.array.application_hidden_policy_exempt_apps, which needs both the array declaration in policy_exempt_apps.xml and the java-symbol entry in symbols.xml (general platform knowledge). The setApplicationHidden hunk anchors on the existing listPolicyExemptAppsUnchecked(mContext) guard, and the same hunks appear on the 14, 15, 16 and 17 branches."
     ],
     "confidence": "high",
     "unknowns": [
      "Why hiding these specific packages causes boot failure. Only the commit message statement is available.",
      "Whether critical packages hidden before the fix are restored on updated devices.",
      "The permission or ownership check on the Android 14 and 17 branches, which is outside the shown hunk context.",
      "What listPolicyExemptAppsUnchecked returns, and the contents of vendor_policy_exempt_apps."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0074": {
     "cve": "CVE-2026-0074",
     "summary": "Adds process-level and thread-level default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and registers a Launcher (Quickstep) process default listener that rejects non-allowlisted image mime types and downscales images whose estimated decoded size exceeds a configured memory budget (300 MB).",
     "component": "platform/frameworks/base (graphics/java/android/graphics/ImageDecoder.java) and platform/packages/apps/Launcher3 (quickstep, QuickstepProcessInitializer / LauncherProcessImageListener)",
     "weakness": "Missing resource limit on image decoding in the Launcher process (uncontrolled memory consumption; bulletin type DoS)",
     "root_cause": "Before the fix, ImageDecoder.callHeaderDecoded only invoked the listener passed at decode time, so there was no way to enforce default decode constraints for a whole process or thread. Judging from what the fix adds, images decoded via ImageDecoder in the Launcher process had no cap on decoded size and no mime-type restriction. The new code comment says a \"Remote size may have returned a giant image, so we need to defensively limit it\". An oversized image could therefore drive excessive memory allocation in the Launcher process. The bulletin rates this as a High severity DoS. The specific Launcher decode call sites are not shown.",
     "fix_analysis": [
      "frameworks/base graphics/java/android/graphics/ImageDecoder.java: adds the static fields sDefaultProcessListener (guarded by sListenerLock) and sDefaultThreadListener (ThreadLocal), plus the @hide static setters and getters setDefaultThreadListener, getDefaultThreadListener, setDefaultProcessListener and getDefaultProcessListener.",
      "frameworks/base ImageDecoder.callHeaderDecoded: now builds the ImageInfo if any of the decode-time listener, the thread default or the process default is non-null. It runs the thread default if set, otherwise the process default, and always runs the decode-time listener last. Defaults can therefore call setTargetSize before the caller's listener runs.",
      "Launcher3 quickstep/src/com/android/quickstep/LauncherProcessImageListener.kt (new): implements ImageDecoder.OnHeaderDecodedListener. onHeaderDecoded throws IOException when the mime type is null or its lowercase form is not in allowedMimeTypes. Then getPreferredSize computes width*height*8 bytes (BYTES_PER_PIXEL = 8, worst-case RGBA_F16, long arithmetic). If that exceeds maxMemoryBytes, it computes a sqrt-based uniform scale, rounds (x*scale+0.5) with a minimum of 1 pixel, and calls decoder.setTargetSize.",
      "Launcher3 quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java: init() now calls setupImageDecoder(context). That method reads R.integer.max_launcher_memory_mb and R.array.allowed_image_mime_types, then calls ImageDecoder.setDefaultProcessListener(new LauncherProcessImageListener(maxMemoryBytes, allowedMimeTypes)).",
      "Launcher3 quickstep/res/values/config.xml: adds max_launcher_memory_mb=300 and an allowlist of image/png, image/jpeg, image/webp, image/gif, image/bmp, image/x-ico, image/vnd.wap.wbmp, image/heif, image/heic and image/avif.",
      "Tests: LauncherProcessImageListenerTest.kt in Launcher3, plus new CtsGraphicsTestCases testDefaultProcessListener, testDefaultThreadListener and testDefaultsAndLocalListener (per the commit message)."
     ],
     "completeness": [
      "The framework change is only a hook and enforces nothing by itself. Protection exists only in processes that register a default listener. In this fix that is the Quickstep Launcher process, via QuickstepProcessInitializer.",
      "Only decodes that go through android.graphics.ImageDecoder reach callHeaderDecoded. Image decoding in the Launcher process through other APIs (e.g. BitmapFactory, which does not use OnHeaderDecodedListener (general platform knowledge)) is not covered by this listener.",
      "If any code sets a thread-level default listener on a thread, the process-level listener is skipped on that thread. A decode-time listener always runs after the defaults and can call setTargetSize again.",
      "The memory check is an estimate (8 bytes per pixel of the source dimensions). It downscales rather than rejects, so an allowlisted image is still decoded, just smaller. The listener does not consider animation frame count or other per-format costs.",
      "QuickstepProcessInitializer extends MainProcessInitializer and only the Quickstep subclass is modified, so non-Quickstep Launcher3 builds are not shown to receive a default listener.",
      "The commit messages cite Flag: com.android.graphics.hwui.flags.image_decoder_default_listener, but neither diff contains a flag check. setDefaultProcessListener is called unconditionally in the shown code."
     ],
     "behavior_changes": [
      "In the Launcher (Quickstep) process, ImageDecoder decodes of images whose mime type is null or not in the allowlist now fail with IOException(\"Image mime type (...) is not allowed.\").",
      "In the Launcher process, images whose width*height*8 exceeds 300 MB (about 39.3 megapixels) are decoded at a reduced target size that preserves aspect ratio.",
      "ImageDecoder.callHeaderDecoded now builds an ImageInfo and invokes default listeners even when the caller passed no listener, if a thread or process default is set.",
      "New @hide static APIs on ImageDecoder: setDefaultThreadListener, getDefaultThreadListener, setDefaultProcessListener and getDefaultProcessListener. The process default is set and read under sListenerLock."
     ],
     "preconditions": [
      "The Launcher (Quickstep) process decodes, via android.graphics.ImageDecoder, an image whose content or dimensions come from outside the Launcher (the code comment refers to a remote size returning a giant image). The exact decode call sites are not shown in the material.",
      "The protection applies only after QuickstepProcessInitializer.init has run in that process."
     ],
     "per_major": {
      "14": "same fix: frameworks/base bf47585e8618 (ImageDecoder default listeners) and Launcher3 c466fd8d9215 (LauncherProcessImageListener + QuickstepProcessInitializer.setupImageDecoder) in android-security-14.0.0_r28. 8/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "adapted fix: Launcher3 8fd9af41d56b in android-security-15.0.0_r16. Same logic with context differences (config.xml taskbar_icon_size 44; QuickstepProcessInitializer has a BinderTracker block before setupImageDecoder). 8/8 lines are present at android-security-15.0.0_r17. The frameworks/base counterpart for 15 is not shown in the material.",
      "16": "same fix: frameworks/base 4abc0b8da1e9 (ImageDecoder default listeners, identical diff at a different line offset) in android-security-16.0.0_r7. 7/7 lines are present at android-security-16.0.0_r8. The Launcher3 counterpart for 16 is not shown in the material.",
      "17": "adapted fix: Launcher3 56e4dc5d2b9a in android-17.0.0_r1 (base). Same logic with QuickstepProcessInitializer context differences. 7/8 lines of the patched region are present at android-security-17.0.0_r1 (the differing line is not shown). The frameworks/base counterpart for 17 is not shown in the material."
     },
     "backport_notes": [
      "Two-repo dependency: the Launcher3 commit calls the @hide ImageDecoder.setDefaultProcessListener, which is added by the frameworks/base commit (Change-Id I694d45deb8755aab5e994c12d2627fe08b5ce78c). Without that commit, Launcher3 references a method that does not exist, so apply frameworks/base first or in the same build. Both commits share Bug 468061774.",
      "The Launcher3 change adds a Kotlin source and new resources in quickstep/. The shown code contains no runtime flag check despite the Flag: line."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which Launcher code path decodes the attacker-influenced image (the specific ImageDecoder call site and image source).",
      "Whether the aconfig flag image_decoder_default_listener gates anything at runtime outside the shown diffs.",
      "Whether the frameworks/base commit landed on 15 and 17 and the Launcher3 commit on 16. This is not shown in the material, although the resolver marks all majors resolved.",
      "What the single differing line in the Android 17 patched region (7/8) is."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0075": {
     "cve": "CVE-2026-0075",
     "summary": "When the caller lacks the provider's READ_PERMISSION (query; READ_CONTACTS according to the commit message) or WRITE_PERMISSION (insert/update/delete), ContactsProvider2 now replaces any SQLiteException whose message contains \"json\" with one carrying the fixed message \"Stripped exception message\". It also calls cursor.getCount() inside the try block of query so that deferred SQLite errors are caught and filtered too.",
     "component": "platform/packages/providers/ContactsProvider, ContactsProvider2 (CP2) query/insert/update/delete entry points",
     "weakness": "Information exposure through error messages (side channel via SQLite exception text returned to unprivileged callers)",
     "root_cause": "ContactsProvider2.query/insert/update/delete caught any Exception, logged it and rethrew it unchanged to the caller. According to the commit message, CP2 applied no strict SQL checks to callers. A caller without READ_CONTACTS could therefore send malformed queries and run a side channel attack by parsing the strings returned in the resulting SQLite exception. The code comment ties this to certain \"json\" tokens. In query(), cursor.getCount() was only called in the finally block, so an exception raised when the cursor was materialized never reached the catch block.",
     "fix_analysis": [
      "src/com/android/providers/contacts/ContactsProvider2.java: adds a new private helper, maybeStripAndThrowSQLiteExceptionWithJson(Exception e, String permission, LogFields.Builder logBuilder). It acts only when all three hold: e is a SQLiteException, getContext().checkCallingPermission(permission) is not PERMISSION_GRANTED, and e.getMessage() lower-cased with Locale.ROOT contains \"json\". It then builds new SQLiteException(\"Stripped exception message\"), records it in logBuilder and throws it in place of the original. The code comment says checkCallingPermission is used on purpose instead of checkCallingOrSelfPermission, so that non-binder or self calls made after clearing identity do not get the raw message.",
      "ContactsProvider2.insert, ContactsProvider2.update, ContactsProvider2.delete: each catch (Exception e) block now calls maybeStripAndThrowSQLiteExceptionWithJson(e, WRITE_PERMISSION, logBuilder) before the existing logBuilder.setException(e); throw e;.",
      "ContactsProvider2.query: after queryInternal(...) returns a non-null cursor, the try block now calls logBuilder.setResultCount(cursor.getCount()). An SQLite error deferred until the cursor is materialized is therefore thrown inside the try and reaches the catch. The catch calls maybeStripAndThrowSQLiteExceptionWithJson(e, READ_PERMISSION, logBuilder). The finally block now only logs logBuilder.build() and no longer computes the result count.",
      "Adds the import android.database.sqlite.SQLiteException."
     ],
     "completeness": [
      "Covers the four entry points patched in ContactsProvider2 (query, insert, update, delete). For query it covers both the immediate exception path and the path where the error appears when the cursor is materialized.",
      "The filter is keyword based. Only SQLiteException messages containing \"json\" (case-insensitive) are stripped, and any other SQLite error text still reaches unprivileged callers unchanged. The fix adds no SQL validation and does not limit which SQLite functions callers may use, even though the commit message describes the problem more broadly.",
      "Only SQLiteException instances are handled. Other exception types pass through unchanged.",
      "Callers that hold the checked permission (READ_PERMISSION for query, WRITE_PERMISSION for insert/update/delete) still get full error messages. The commit message justifies this because callers with READ_CONTACTS can read the whole database anyway.",
      "This diff does not touch other provider entry points (e.g. bulkInsert, applyBatch, call, openAssetFile, or paths in AbstractContactsProvider). The material does not show whether they can return SQLite error text."
     ],
     "behavior_changes": [
      "Unprivileged callers that trigger a SQLiteException whose message contains \"json\" now get a SQLiteException with the generic message \"Stripped exception message\". The original text is also no longer written to the CP2 LogFields log, which records the stripped exception instead.",
      "query() now calls cursor.getCount() on a non-null cursor inside the try block before returning. Errors raised at that point are now caught, logged and subject to stripping, instead of being thrown from the finally block.",
      "query() no longer sets the result count in finally. The count is set only on success, so failed queries are logged without a result count."
     ],
     "preconditions": [
      "The caller can reach ContactsProvider2.query/insert/update/delete without the permission named by READ_PERMISSION (query) or WRITE_PERMISSION (insert/update/delete), as checked by checkCallingPermission on the binder caller.",
      "The caller controls SQL text that reaches SQLite (selection, sortOrder, projection or similar) and triggers a SQLiteException whose message contains \"json\".",
      "The material does not show whether apps without contacts permissions can reach CP2 on a given URI."
     ],
     "per_major": {
      "14": "Same fix: commit 7d344b678bde in android-security-14.0.0_r28 (cherry-pick of 6044ca5db93f). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "Same fix: commit 930817423647 in android-security-15.0.0_r16, identical hunks at slightly different line offsets. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: commit bb9f36b3ff6d in android-security-16.0.0_r7, identical hunks (query hunk around line 6020). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Same fix: commit 819ebe4a7c68 is in base android-17.0.0_r1. 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A single self-contained commit in ContactsProvider2.java. It relies on things already present in the catch/finally blocks of query/insert/update/delete: the WRITE_PERMISSION/READ_PERMISSION constants, LogFields.Builder.setException/setResultCount and the LogUtils logging wrappers. A tree without the LogFields logging scaffolding would need the helper's logBuilder parameter adapted."
     ],
     "confidence": "high",
     "unknowns": [
      "Which CP2 URIs or paths callers can reach without the read/write permissions (the material does not show path permissions or URI grants).",
      "Which permission WRITE_PERMISSION resolves to (the material names only READ_CONTACTS).",
      "Whether other entry points (bulkInsert, applyBatch, call, file open paths) can return SQLite error text, and whether a separate change addressed them.",
      "Which exact SQLite json functions or messages were used, and what information the error text exposed. The material only indicates the keyword \"json\"."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0076": {
     "cve": "CVE-2026-0076",
     "summary": "Adds a minimum-size check on ResXMLTree_attrExt::attributeSize in ResXMLTree::validateNode so binary XML nodes declaring an attribute size smaller than sizeof(ResXMLTree_attribute) are rejected with BAD_TYPE instead of being parsed.",
     "component": "platform/frameworks/base, libs/androidfw (ResourceTypes.cpp, binary XML parser ResXMLTree)",
     "weakness": "missing bounds check on an attacker-controlled element size leading to out-of-bounds read (improper validation of a specified quantity in input)",
     "root_cause": "ResXMLTree::validateNode (libs/androidfw/ResourceTypes.cpp) validated a START_ELEMENT node's attribute block only by checking that attributeStart + (attributeSize * attributeCount) fits within the node (size - headerSize). It never checked that attributeSize itself was at least sizeof(ResXMLTree_attribute). Per the commit message, the code that later reads attributes uses the minimum sizes rather than the specified size, so a node declaring attributeSize smaller than that structure passes the product check while the actual accesses extend past the validated range, reading beyond the end of memory.",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp : ResXMLTree::validateNode: inside the existing branch that confirms the node is large enough to hold a ResXMLTree_attrExt, a new check is inserted before the attrSize product computation: if dtohs(attrExt->attributeSize) < sizeof(ResXMLTree_attribute), log 'Bad XML block: attribute size %d is smaller than min expected %d' via ALOGW and return BAD_TYPE.",
      "The pre-existing checks are kept unchanged: the node must be >= headerSize + sizeof(ResXMLTree_attrExt), attrExt must lie after node, and attributeStart + attributeSize*attributeCount must be <= size - headerSize. The new check makes the product check meaningful because each element is now guaranteed to be at least the minimum structure size.",
      "libs/androidfw/tests/ResourceTypes_test.cpp: new test ResXMLTree_ValidateNode_SmallAttributeSize builds a mock RES_XML_TYPE chunk with an empty string pool and one START_ELEMENT node whose attributeSize is sizeof(ResXMLTree_attribute) - 1 and attributeCount 1, with a node size padded so the old product check would pass, and asserts ResXMLTree::setTo returns BAD_TYPE."
     ],
     "completeness": [
      "Covers the specific gap: attributeSize below sizeof(ResXMLTree_attribute) is now rejected for every RES_XML_START_ELEMENT_TYPE node that passes through validateNode.",
      "The product attributeSize * attributeCount is computed in size_t from two 16-bit values, so it cannot overflow; combined with the new lower bound, the declared attribute range now fits inside the node's declared size.",
      "attributeStart has no lower bound check (it is only bounded above through attributeStart + attrSize <= size - headerSize); a small attributeStart would place attributes overlapping the attrExt header but still within the node, so this is not an out-of-bounds condition from the shown code.",
      "Only validateNode is changed; the material does not show the attribute accessor code, so whether any path reads attributes without first going through validateNode cannot be judged from the material."
     ],
     "behavior_changes": [
      "ResXMLTree::setTo / node validation now returns BAD_TYPE for binary XML whose START_ELEMENT attrExt declares attributeSize < sizeof(ResXMLTree_attribute); such documents fail to parse instead of being accepted.",
      "A new ALOGW message 'Bad XML block: attribute size %d is smaller than min expected %d' is emitted when the check fails."
     ],
     "preconditions": [
      "Attacker-controlled binary (compiled) XML is parsed by ResXMLTree (ResXMLTree::setTo reaches validateNode, as the new test shows); the document must contain a RES_XML_START_ELEMENT_TYPE node whose attrExt is in bounds but declares attributeSize smaller than sizeof(ResXMLTree_attribute) while attributeStart + attributeSize*attributeCount still fits the node size.",
      "The specific caller or entry point (which process parses the XML) is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 40ed3bb3016e in android-security-14.0.0_r28, identical 5-line check plus test; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base b4f448b356b1 in android-security-15.0.0_r16, identical diff content (hunk at line 1986 instead of 1915); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base a91d16a02b51 in android-security-16.0.0_r7, identical diff content (hunk at line 1989); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base 881e565347a5 landed in android-17.0.0_r1 (base release), identical diff; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit: a 5-line insertion inside ResXMLTree::validateNode between the existing attrExt bounds branch and the attrSize computation; the diff context is identical across 14 through 17 (only the hunk line number differs), so it should apply with a line offset to branches with the same validateNode code.",
      "The new test is appended to ResourceTypes_test.cpp, which has the same pre-image blob 3d4a88eda247 on all four majors, and uses util::HostToDevice16/32 from androidfw/Util.h."
     ],
     "confidence": "high",
     "unknowns": [
      "The attribute accessor code that reads using the minimum ResXMLTree_attribute size (referenced by the commit message) is not shown, so the exact out-of-bounds read site is not confirmed from the material.",
      "Which process or entry point parses attacker-supplied binary XML to make this an EoP, and the privilege boundary crossed, are not stated in the material.",
      "Whether any code path reads attributes of a node without a prior validateNode call is not shown.",
      "Whether any legitimately produced binary XML uses attributeSize below sizeof(ResXMLTree_attribute) is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0078": {
     "cve": "CVE-2026-0078",
     "summary": "DevicePolicyManagerService.setGlobalProxy now rejects proxySpec and exclusionList strings whose modified-UTF-8 encoding exceeds 65535 bytes (via PolicySizeVerifier.enforceMaxStringLength) before storing them in the admin's policy state.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (device policy / global proxy)",
     "weakness": "Missing input length validation on admin-supplied strings persisted into device policy state",
     "root_cause": "In DevicePolicyManagerService.setGlobalProxy, in the else branch (the branch that does not clear the fields), the service set admin.specifiesGlobalProxy = true and stored the caller-supplied proxySpec and exclusionList into admin.globalProxySpec and admin.globalProxyExclusionList without any size check. The new PolicySizeVerifier.countBytes documents the limit: a string whose modified-UTF-8 form is longer than 65535 bytes cannot have its length represented by a short (UTFDataFormatException \"String more than 65535 UTF bytes long\"). The material does not show the downstream serialization failure path itself.",
     "fix_analysis": [
      "core/java/android/app/admin/PolicySizeVerifier.java (new file in the 14 and 17 commits): adds enforceMaxStringLength(String str, String argName), which calls a private countBytes(s, shortLength=true) that sums modified-UTF-8 byte lengths (1 byte for U+0001..U+007F, 2 bytes for U+0000 and up to U+07FF, 3 bytes otherwise) and throws UTFDataFormatException once the total exceeds 65535; enforceMaxStringLength converts that into IllegalArgumentException(argName + \" too long\").",
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java: imports android.app.admin.PolicySizeVerifier; in setGlobalProxy's else branch, before admin.specifiesGlobalProxy/globalProxySpec/globalProxyExclusionList are assigned, calls PolicySizeVerifier.enforceMaxStringLength(proxySpec, \"proxySpec\") and, if exclusionList != null, PolicySizeVerifier.enforceMaxStringLength(exclusionList, \"exclusionList\"). Because the checks precede these assignments, an oversized value does not reach those fields.",
      "services/tests/servicestests/res/xml/device_admin_sample.xml: adds <set-global-proxy /> to the test admin's uses-policies (likely so the test admin holds the global proxy policy).",
      "services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java: adds testSetGlobalProxy_tooLongStrings, asserting IllegalArgumentException for a 65536-char proxy host and for a 65536-char exclusion list entry."
     ],
     "completeness": [
      "Covers exactly the two strings stored by setGlobalProxy (proxySpec and exclusionList) in the branch that stores them; the other branch only clears the fields to null.",
      "The limit enforced is 65535 modified-UTF-8 bytes, matching the short-length constraint described in countBytes; values at or below that are accepted unchanged.",
      "exclusionList is null-guarded before the check; proxySpec is passed without a guard. The if condition selecting the else branch is not shown in the diff, so whether proxySpec is always non-null there is not visible.",
      "The new PolicySizeVerifier on 14/17 contains only enforceMaxStringLength and countBytes; the imports of ModifiedUtf8, ComponentName, Bundle, Parcelable, PersistableBundle, ArrayDeque and Queue are unused by the shown code.",
      "Whether other string-valued device policies in DevicePolicyManagerService receive the same validation is not shown in the material."
     ],
     "behavior_changes": [
      "DevicePolicyManager.setGlobalProxy now throws IllegalArgumentException (\"proxySpec too long\" or \"exclusionList too long\") when either string exceeds 65535 modified-UTF-8 bytes; before the fix no such check existed on this path.",
      "On rejection the admin's specifiesGlobalProxy, globalProxySpec and globalProxyExclusionList fields are not assigned by this branch."
     ],
     "preconditions": [
      "Caller must be an admin permitted to call setGlobalProxy; the test sets a device owner and adds the set-global-proxy policy to the admin's uses-policies.",
      "The call must reach the storing (else) branch of setGlobalProxy with proxySpec or exclusionList longer than 65535 modified-UTF-8 bytes."
     ],
     "per_major": {
      "14": "same fix: commit fa2c8531ae6f in android-security-14.0.0_r28 adds core/java/android/app/admin/PolicySizeVerifier.java as a new file plus the import and DevicePolicyManagerService check (hunk at line ~8403) and tests; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "adapted fix: commit 71bc705a2542 in android-security-15.0.0_r16 changes only DevicePolicyManagerService (hunk at line ~8576) and the two test files; it adds neither PolicySizeVerifier.java nor the import, implying both already exist on that branch (not shown); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: commit 50cbf4422ac8 in android-security-16.0.0_r7 changes only DevicePolicyManagerService (hunk at line ~8643) and the two test files, adding neither PolicySizeVerifier.java nor the import (presumably pre-existing, not shown); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit cf8446b51ae1 in android-17.0.0_r1 includes the new PolicySizeVerifier.java (same blob 3a1c34309bbc as the 14 version), the import, the DevicePolicyManagerService check (hunk at line ~8406) and tests; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "If the target branch lacks core/java/android/app/admin/PolicySizeVerifier.java, carry the 14 version of the class with the service change; only enforceMaxStringLength and the private countBytes are used, and the unused com.android.modules.utils.ModifiedUtf8 import can be dropped if that library is not available. The <set-global-proxy /> addition to device_admin_sample.xml accompanies the new test."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact downstream failure caused by an oversized stored proxySpec/exclusionList (which serializer consumes these ActiveAdmin fields and how it fails) is not shown; only the 65535-byte modified-UTF-8 limit is evidenced by countBytes and the test comment.",
      "The if condition guarding the else branch in setGlobalProxy is not shown, so whether proxySpec can be null when passed to enforceMaxStringLength is not visible.",
      "Why the 14 and 17 commits add PolicySizeVerifier.java as a new file while the 15 and 16 commits do not is not explained by the material.",
      "Whether other DevicePolicyManagerService string policies are validated with PolicySizeVerifier is not visible in the provided context."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0079": {
     "cve": "CVE-2026-0079",
     "summary": "libdng_sdk stops linking the aborting ubsan-minimal runtime and instead ships its own hidden __ubsan_handle_*_overflow handlers that throw std::runtime_error, so integer overflows detected in the library become C++ exceptions rather than a process abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG processing arithmetic turned into an unconditional process abort by the ubsan-minimal runtime (uncontrolled abort, DoS)",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"]. Per the comment added in the fix, Soong's handling links the ubsan-minimal runtime, which does a hard abort on a detected error. So any signed or unsigned integer overflow in libdng_sdk arithmetic while processing a DNG file killed the hosting process. The commit references seven bugs. The library is built with rtti: true and -fexceptions, and the commit message says exceptions are \"a much, much better solution for ubsan errors than a hard abort is\".",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so Soong does not apply its sanitizer configuration or link the ubsan-minimal runtime for this module. The comment notes that Soong has no dedicated flag for this.",
      "Android.bp, cc_library libdng_sdk: re-enables instrumentation explicitly with the cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow and adds -fno-sanitize-link-runtime, so overflow checks are still emitted but no sanitizer runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs. On 14/15/16 this is a new srcs property; on 17 the file is appended to the existing srcs list.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each throws std::runtime_error with a descriptive message, so a detected overflow unwinds as a C++ exception instead of aborting.",
      "OWNERS (14/15/16 only): adds a bug component line and includes the frameworks/base graphics OWNERS; not security relevant."
     ],
     "completeness": [
      "Defines five handlers (add, sub, mul, negate and divrem overflow). Only signed-integer-overflow and unsigned-integer-overflow are instrumented, which matches what libdng_sdk-defaults enabled before. The material does not show whether the compiler can emit any other handler for these checks.",
      "The fix does not correct any specific overflowing computation in the dng_sdk sources. It changes the failure mode generically from abort to exception, and the arithmetic sites behind the seven referenced bugs are not changed by this commit.",
      "The handlers have hidden visibility and are compiled only into libdng_sdk. Other modules using libdng_sdk-defaults, if any, keep the default Soong sanitizer behaviour.",
      "The material does not show whether every exception thrown from a handler is caught before it reaches a caller boundary. An uncaught exception would still terminate the process.",
      "Unsigned-integer-overflow now throws at sites that rely on intentional wraparound. Those sites already aborted in the pre-fix build, so this adds no new failure surface compared with that build."
     ],
     "behavior_changes": [
      "Integer overflow detected by the sanitizer in libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\" etc.) instead of aborting the process.",
      "libdng_sdk no longer links the ubsan-minimal runtime. For this module, sanitizer instrumentation is driven by explicit cflags rather than Soong's sanitize settings.",
      "If a caller catches the exception, a malformed input that previously aborted the process becomes a recoverable failure. The material does not show any caller's exception handling."
     ],
     "preconditions": [
      "A DNG image processed by libdng_sdk whose contents cause a signed or unsigned integer overflow in the library's arithmetic.",
      "The commit's test (ImageDecoderTest#testInterestingDngs) indicates that decoding through ImageDecoder is the exercised path. The exact caller and its exception handling are not shown.",
      "Per index search, the hits were DngCreator (frameworks/base JNI) and img_utils DngUtils.cpp. These are DNG writing code and do not evidently call libdng_sdk, so they are not conclusive as callers."
     ],
     "per_major": {
      "14": "same fix: 42e65547eb33 in android-security-14.0.0_r28 (Android.bp sanitize/cflags/srcs, new ubsan_throwing_runtime.cpp, OWNERS). The code context at android-security-14.0.0_r29 shows the Android.bp and runtime changes, but the per-major facts report only 3/5 lines of the patched region present there.",
      "15": "same fix: 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and runtime file are identical; the OWNERS hunk differs only in its starting content. Per-major facts: 3/4 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: 74a5a91d169a in android-security-16.0.0_r7. Android.bp and runtime file are identical; the OWNERS hunk differs only in its starting content. Per-major facts: 3/5 lines present at android-security-16.0.0_r8.",
      "17": "adapted fix: 81ded662eee0 in android-17.0.0_r1 (one of the Cherrypick-From originals; matched to the CVE by related bug). Same sanitize/cflags change and identical runtime file, but ubsan_throwing_runtime.cpp is appended to the existing srcs list in the libdng_sdk cc_library, and there is no OWNERS change."
     },
     "backport_notes": [
      "Single commit. The target's libdng_sdk must already build with rtti: true and cppflags -fexceptions (in libdng_sdk-defaults at lines 141-142 in the 14 context). Soong must honour sanitize.never together with raw -fsanitize/-fno-sanitize-link-runtime cflags. Where srcs are listed in the cc_library, append the new file to that list as in the Android 17 variant."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific arithmetic sites in the dng_sdk sources overflowed (the seven referenced bugs are not described).",
      "Where in the Android decode stack the thrown std::runtime_error is caught, and whether all libdng_sdk entry points are wrapped in a catch.",
      "Whether the compiler can emit handler symbols beyond the five defined, and whether the parameterless replacement handlers are compatible with the arguments the compiler passes at every call site.",
      "Why the per-major facts report only partial presence of the patched region in the latest security tags (for example 3/5 lines on 14)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0085": {
     "cve": "CVE-2026-0085",
     "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider resolve the ContentValues key case-insensitively, so that a Data column supplied with mismatched case (e.g. \"DaTa1\") is still truncated to the simple-field maximum size instead of bypassing the check.",
     "component": "platform/packages/providers/ContactsProvider, DataRowHandler (data row size enforcement)",
     "weakness": "Improper handling of case sensitivity in an input-size check (validation bypass via case-mismatched column key)",
     "root_cause": "applySimpleFieldMaxSize(ContentValues cv, String column) read the value with cv.getAsString(column), using the exact canonical column name. ContentValues keys are case-sensitive, so a caller that put the value under a differently cased key such as \"daTa1\" got v == null in the check, and the truncation to ContactsDatabaseHelper.getSimpleFieldMaxSize() was skipped. Per the added tests, a value under such a mismatched-case key is still applied to the DATA1 column, so the oversized value could be written to the data row. The bulletin classifies the impact as DoS (High).",
     "fix_analysis": [
      "src/com/android/providers/contacts/DataRowHandler.java:applySimpleFieldMaxSize: instead of cv.getAsString(column), the code now calls the new findKey(cv, column) and reads cv.getAsString(key). If the value is longer than maxSize, it still logs and writes the truncated value with cv.put(column, ...) under the canonical column name.",
      "src/com/android/providers/contacts/DataRowHandler.java:findKey (new, private static): returns column if cv.containsKey(column). Otherwise it iterates cv.keySet() and returns the first key for which key.equalsIgnoreCase(column). If none matches, it returns column, so getAsString yields null and the method returns early as before.",
      "tests/src/com/android/providers/contacts/ContactsProvider2Test.java: adds three tests. testUpdateFailsWithOnlyCaseMismatchedColumn checks that an update with only a case-mismatched key is rejected (0 rows). testUpdateSucceedsWithAtLeastOneCaseSensitiveColumn checks that the mismatched key is applied when an exact-case key is also present. testCaseMismatchingFieldUpdatesAreTruncatedToMaxSize checks that the variants data1/daTa1/DATA1/Data1 all end up truncated to maxSize. The commit also removes the unused field mMockContactsDatabaseHelper."
     ],
     "completeness": [
      "The fix covers every call site of applySimpleFieldMaxSize, because the case-insensitive lookup is inside the helper itself; no call sites are changed.",
      "The oversized value under the mismatched-case key is not removed or rewritten; the truncated value is only added under the canonical column name. The new tests assert that the stored DATA1 value is the truncated one, so the canonical key takes effect in the tested update path, but the ContentValues still carries both keys after the call.",
      "findKey returns only the first case-insensitive match. If several differently cased variants of the same column were present, only one is inspected.",
      "testUpdateFailsWithOnlyCaseMismatchedColumn documents existing behavior (an update that specifies only a mismatched-case key is rejected); the source change in this diff does not introduce that behavior.",
      "Only size enforcement in DataRowHandler is addressed. The material does not show whether other ContentValues lookups in the provider use exact-case keys."
     ],
     "behavior_changes": [
      "Values supplied under a differently cased Data column key are now truncated to getSimpleFieldMaxSize() and a 'Truncating field' warning is logged, where previously they were stored unmodified.",
      "After truncation, the ContentValues also contains the canonical column name (e.g. data1) with the truncated value."
     ],
     "preconditions": [
      "A caller can perform a Data row update through ContactsProvider (the tests use ContentResolver.update on a structured-name Data URI) with the target column given in non-canonical case and a value longer than ContactsDatabaseHelper.getSimpleFieldMaxSize().",
      "Per the added tests, the update is only applied when at least one exact-case Data column key is also present in the ContentValues; a request with only a case-mismatched key returns 0 rows."
     ],
     "per_major": {
      "14": "same fix: adef234467ab in android-security-14.0.0_r28 (cherry-pick, Change-Id I91a2a7263f4017affcf5ed2f9da43bf2df9c1d8d); at android-security-14.0.0_r29, 7/8 lines of the patched region are present",
      "15": "same fix: 7d266fdeb2ec in android-security-15.0.0_r16, identical DataRowHandler.java hunk (b1295c1f..cdb4df79); at android-security-15.0.0_r17, 7/8 lines of the patched region are present",
      "16": "same fix: 0d925d762547 in android-security-16.0.0_r7, identical logic at a one-line different offset (b9becf16..6474c241); the tests carry @Test annotations; at android-security-16.0.0_r8, 7/8 lines of the patched region are present",
      "17": "same fix: d857ac867433 in android-17.0.0_r1 (base), identical hunk; at android-security-17.0.0_r1, 7/8 lines of the patched region are present"
     },
     "backport_notes": [
      "Single self-contained commit. The source change is confined to applySimpleFieldMaxSize plus a new private static findKey in DataRowHandler.java, with the same logic on 14, 15, 16 and 17; only the test file differs across branches (@Test annotations on 16). The removal of the unused mMockContactsDatabaseHelper test field is incidental and can be dropped if it conflicts."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the call sites of applySimpleFieldMaxSize: which DataRowHandler subclasses or columns call it, or whether the insert path is covered in addition to update.",
      "The material does not describe the concrete DoS consequence of storing an oversized value beyond the bulletin type.",
      "The material does not show whether the canonical truncated value overrides the mismatched-case oversized value in all code paths; the tests only cover the structured-name update path.",
      "The material does not show how the mismatched-case key is mapped to the DATA1 column during the update."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0087": {
     "cve": "CVE-2026-0087",
     "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" or hosts ending in \".xyz.com\", instead of any host whose string ends in \"xyz.com\" (e.g. \"abcxyz.com\").",
     "component": "platform/frameworks/base, PackageManager domain verification (services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java)",
     "weakness": "Incorrect wildcard/suffix matching in host authorization: the check compared the host against the wildcard's bare suffix without requiring the \".\" label separator.",
     "root_cause": "In DomainVerificationService.java, the approval-level computation (the method containing the \"host verified by wildcard\" debugApproval branch; method name not shown in the material) iterates the package's verified state map and, for a key starting with \"*.\", approved the host with `host.endsWith(domain.substring(2))`. substring(2) strips both the \"*\" and the \".\", so for \"*.xyz.com\" the comparison suffix was \"xyz.com\". Any host string ending in \"xyz.com\", including unrelated registrable domains such as \"abcxyz.com\", therefore satisfied the check and returned APPROVAL_LEVEL_VERIFIED for that package.",
     "fix_analysis": [
      "DomainVerificationService.java (wildcard branch of the approval-level loop over stateMap): the condition becomes `domain.startsWith(\"*.\") && (host.endsWith(domain.substring(1)) || host.equals(domain.substring(2)))`. substring(1) keeps the leading \".\", so only true subdomains (\"abc.xyz.com\") match by suffix; the explicit equals(substring(2)) preserves matching of the base domain itself (\"xyz.com\").",
      "DomainVerificationPackageTest.kt: adds testWildcardDomain, which verifies a package with verified \"*.xyz.com\" via setStatus(STATE_SUCCESS), then asserts getOwnersForDomain(\"abcxyz.com\") is empty while \"abc.xyz.com\" and \"xyz.com\" return DomainOwner(PKG_ONE, false)."
     ],
     "completeness": [
      "Covers the single wildcard comparison in the verified-state loop of DomainVerificationService; this is the only production code line changed.",
      "The preceding exact-host lookup (stateMap.get(host)) and the later user-enabled-hosts path (userState.getEnabledHosts().contains(host)) are exact-match checks and are untouched; whether any other wildcard-handling code exists elsewhere (e.g. in the collector or user-state handling) is not shown in the material.",
      "Matching remains a plain string suffix comparison (no normalization changes); the fix only adds the separator requirement."
     ],
     "behavior_changes": [
      "Hosts that end in a wildcard's suffix without a preceding \".\" (e.g. \"abcxyz.com\" for \"*.xyz.com\") no longer yield APPROVAL_LEVEL_VERIFIED for that package via the wildcard branch; getOwnersForDomain no longer lists the package as owner for such hosts.",
      "Matching of the bare base domain (\"xyz.com\") and of real subdomains (\"abc.xyz.com\") is unchanged (both matched before and still match)."
     ],
     "preconditions": [
      "A package declares an autoVerify wildcard domain of the form \"*.suffix\" and that entry is in a verified state in the package's domain verification state map.",
      "Domain approval is evaluated for a host whose string ends with the wildcard's bare suffix but is not a subdomain of it; the test reaches this through DomainVerificationService.getOwnersForDomain(host, userId).",
      "The exact-host check earlier in the same method must not already have matched (the wildcard loop runs only after that)."
     ],
     "per_major": {
      "14": "same fix: b035d109935c in android-security-14.0.0_r28 (cherry-pick of 919d0cb125b3); 6/6 patched lines present at android-security-14.0.0_r29; test uses service.addPackage(pkg).",
      "15": "same fix (adapted test): b81e2355caf1 in android-security-15.0.0_r16; identical production change at a different offset (line ~2030); test uses service.addPackage(pkg, null); 6/6 lines present at android-security-15.0.0_r17.",
      "16": "same fix (adapted test): 2e3d71d027b0 in android-security-16.0.0_r7; identical production change at line ~2030, test uses addPackage(pkg, null); 6/6 lines present at android-security-16.0.0_r8.",
      "17": "same fix: c225d09f44e6 in android-17.0.0_r1 (base), diff identical to the Android 14 variant (line ~1887, addPackage(pkg)); 6/6 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-commit fix; the production hunk is the same one-condition change on all four branches (only the line offset differs). Only the test differs: addPackage(pkg) on 14/17 versus addPackage(pkg, null) on 15/16, so pick the test variant matching the branch's DomainVerificationService.addPackage signature."
     ],
     "confidence": "high",
     "unknowns": [
      "The name of the enclosing method in DomainVerificationService that contains the wildcard branch is not shown in the material.",
      "The material does not show the concrete privilege-escalation consequence beyond the bulletin's EoP/High rating, nor which external callers of the approval computation (e.g. intent resolution) are affected.",
      "Whether wildcard domains are handled anywhere else in the domain verification code is not shown."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0088": {
     "cve": "CVE-2026-0088",
     "summary": "CertInstaller.getCallingAppLabel() now gets the calling app's label via ApplicationInfo.loadSafeLabel(pm) instead of loadLabel(pm), so the label shown in the CA certificate redirect dialog is sanitized before display.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA certificate install dialogs)",
     "weakness": "Missing sanitization of an attacker-controlled display string used in a security dialog (UI spoofing / misrepresentation of critical information)",
     "root_cause": "In src/com/android/certinstaller/CertInstaller.java, getCallingAppLabel() looked up the referrer package (mCredentials.getReferrer()) with PackageManager.getApplicationInfo(..., MATCH_DISABLED_COMPONENTS) and returned appInfo.loadLabel(pm). That is the raw application label, which the calling app controls. createRedirectCaCertificateDialog() formats this label into R.string.redirect_ca_certificate_with_app_info_message and sets it as the AlertDialog message. Per the commit message, a crafted label (for example one containing newlines) could push the CA certificate installation warning off-screen, so the user might not see the warning.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java:getCallingAppLabel(): the single return statement changes from appInfo.loadLabel(pm) to appInfo.loadSafeLabel(pm). The rest of the method (null referrer check, getApplicationInfo lookup, NameNotFoundException handling) is unchanged.",
      "loadSafeLabel on PackageItemInfo/ApplicationInfo runs the label through the framework's safe-for-presentation processing, which trims whitespace, keeps only the first line and bounds the label size (general platform knowledge). The commit message states the intent: have the system sanitize the label and strip characters such as newlines, so the dialog shows the app name without displacing the warning.",
      "The consumer, createRedirectCaCertificateDialog(), is not modified. It still builds the message with getString(R.string.redirect_ca_certificate_with_app_info_message, getCallingAppLabel()) and setMessage(message), but now receives the sanitized label."
     ],
     "completeness": [
      "Covers the label source used by getCallingAppLabel(). In the shown context, this is the only place where caller-controlled text goes into the redirect CA certificate dialog (createRedirectCaCertificateDialog()).",
      "getCallingAppLabel() is public. Because the sanitization happens at the source, any other caller inside CertInstaller also benefits. No other callers are shown in the material.",
      "The fix relies only on the framework's loadSafeLabel(pm) behavior. CertInstaller itself adds no extra length cap or layout hardening, such as a fixed message area or ellipsizing.",
      "Other dialogs in CertInstaller (e.g. createSelectCertificateUsageDialog(), SELECT_CERTIFICATE_USAGE_DIALOG) are untouched. The material does not show whether they display any caller-controlled text."
     ],
     "behavior_changes": [
      "The calling app name shown in the CA certificate redirect dialog is now the system-sanitized label. Per the commit message, characters such as newlines are stripped, so apps with multi-line labels will see a changed name in the dialog.",
      "No change to control flow, error handling or return values, other than the content of the returned CharSequence."
     ],
     "preconditions": [
      "An installed app with a crafted application label starts the CertInstaller flow with a CA certificate, so that mCredentials.getReferrer() returns that app's package name.",
      "mCredentials.hasOnlyVpnAndAppsTrustAnchors() must be true. Only then does createRedirectOrSelectUsageDialog() show REDIRECT_CA_CERTIFICATE_DIALOG, whose builder createRedirectCaCertificateDialog() embeds getCallingAppLabel() in the message.",
      "The calling package must be resolvable via getApplicationInfo(callingPkg, MATCH_DISABLED_COMPONENTS). Otherwise getCallingAppLabel() returns null."
     ],
     "per_major": {
      "14": "same fix: commit d90ffd033f10 in android-security-14.0.0_r28, identical one-line diff (883c897..1eff53f). The android-security-14.0.0_r29 context shows loadSafeLabel(pm) at line 417.",
      "15": "same fix: commit d29fcd1eff25 in android-security-15.0.0_r16, identical diff to Android 14.",
      "16": "same fix: commit c935d8d07913 in android-security-16.0.0_r7, identical diff to Android 14.",
      "17": "same fix: commit 3dced430d0a9 in base release android-17.0.0_r1, identical diff. The patched region is present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-line, self-contained change with no dependencies. The same hunk (file index 883c897..1eff53f) appears on 14 through 17, so forks whose getCallingAppLabel() still ends in appInfo.loadLabel(pm) can take it verbatim.",
      "loadSafeLabel(PackageManager) is a system API on PackageItemInfo, not part of the public SDK. CertInstaller can use it because it is a platform-built system app (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether getCallingAppLabel() has callers beyond createRedirectCaCertificateDialog(). Only that caller appears in the provided context.",
      "The material does not show the exact sanitization parameters loadSafeLabel(pm) applies in each release.",
      "The material does not show how the calling app reaches CertInstaller with a CA certificate (the intent path that fills in mCredentials and its referrer)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0091": {
     "cve": "CVE-2026-0091",
     "summary": "WM core now records the remote-animation delegate process on the server-side Transition, strips the IApplicationThread from the RemoteTransition it sends to shell, and changes IActivityTaskManager.setRunningRemoteTransitionDelegate to take a transition token instead of an IApplicationThread.",
     "component": "platform/frameworks/base, WindowManager transitions (ActivityTaskManagerService, TransitionController, WindowOrganizerController) and WM Shell transition handlers",
     "weakness": "Exposure of a privileged binder token outside system_server, plus trust in a process identity supplied by the caller",
     "root_cause": "Before the fix, TransitionController.requestStartTransition put the full RemoteTransition, including its IApplicationThread (getAppThread()), into the TransitionRequestInfo it sent to the transition player. Shell then passed an IApplicationThread back into ActivityTaskManagerService.setRunningRemoteTransitionDelegate(IApplicationThread delegate), which used it to look up a WindowProcessController and mark that process as running a remote transition (mRemotePlayer.reportRunning / update). So system_server sent an IApplicationThread token out of WMCore, and the delegate process was whatever IApplicationThread the caller supplied, not one WM had recorded for the transition. The commit message says the change \"removes the need to send an IApplicationThread token out of WMCore\", and the test is named testActivityManager_stripsAppThreadFromRemoteTransition.",
     "fix_analysis": [
      "Transition.java: adds the package-private field IApplicationThread mRemoteDelegate, which tracks the process that will play the animation.",
      "TransitionController.requestStartTransition: when remoteTransition != null, stores remoteTransition.getAppThread() in transition.mRemoteDelegate and sends shell a new RemoteTransition(getRemoteTransition(), getDebugName()) with no app thread. setRemoteAnimationApp still gets the original app thread on the server side.",
      "WindowContainerTransaction: adds setAnimationDelegate(IBinder) and a new hierarchy op, HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE (18 on 14/17, 19 on 15, 27 on 16), plus a type-to-string case and a toString case.",
      "WindowOrganizerController (hierarchy-op switch): the new op reads the binder (hop.getNewParent() on 14/17, hop.getInsetsFrameOwner() on 15/16) and sets transition.mRemoteDelegate = IApplicationThread.Stub.asInterface(binder). If there is no transition or no binder, it logs an error and skips.",
      "IActivityTaskManager.aidl / ActivityTaskManagerService.setRunningRemoteTransitionDelegate: the parameter is now IBinder transitionToken. The delegate comes from Transition.fromBinder(token).mRemoteDelegate, and the method returns early if fromBinder returns null. The existing CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS check and the check that the caller is itself running a remote transition stay in place.",
      "Shell: Transitions.setRunningRemoteTransitionDelegate takes the transition token. OneShotRemoteHandler, RemoteTransitionHandler and RecentsTransitionHandler pass `transition`. RecentsTransitionHandler.startRecentsTransition and SplitScreenTransitions.startEnterTransition call wct.setAnimationDelegate(appThread.asBinder()). The StageCoordinator boost calls that used adapter.getCallingApplication() are removed."
     ],
     "completeness": [
      "setRunningRemoteTransitionDelegate no longer accepts a process token from the caller. It only boosts the delegate WM recorded on the Transition the token resolves to.",
      "The IApplicationThread is stripped only from the RemoteTransition in the TransitionRequestInfo that requestStartTransition builds. The material does not show whether any other path sends RemoteTransition app threads to the player.",
      "HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE lets whoever applies a WindowContainerTransaction tied to a transition set mRemoteDelegate to any binder. The diff adds no extra check on that op, so it relies on the existing permission checks of the WindowOrganizerController apply path (not shown).",
      "In the StageCoordinator paths that run a RemoteAnimationAdapter runner, the priority boost is removed, not re-plumbed."
     ],
     "behavior_changes": [
      "AIDL signature change: IActivityTaskManager.setRunningRemoteTransitionDelegate(IBinder) replaces (IApplicationThread). Callers built against the old signature break.",
      "The RemoteTransition in TransitionRequestInfo, as seen by the shell/player, no longer carries an app thread.",
      "If the token does not resolve to a Transition (Transition.fromBinder returns null), setRunningRemoteTransitionDelegate returns silently.",
      "The StageCoordinator RemoteAnimationAdapter paths no longer boost the animating process."
     ],
     "preconditions": [
      "Shell transitions in use, with a RemoteTransition carrying an app thread (for example, a launch with a remote animation, recents, or entering split screen).",
      "To call setRunningRemoteTransitionDelegate the caller needs CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS unless the delegate is already reported running (per ActivityTaskManagerService context).",
      "Exact attacker reachability is unknown. The bulletin rates this High EoP."
     ],
     "per_major": {
      "14": "Fixed by caffcf6be82b in android-security-14.0.0_r28. The new hop constant is 18 and the binder is carried in mReparent (setReparentContainer/getNewParent).",
      "15": "Adapted fix f7488ad342c2 in android-security-15.0.0_r16. The constant is 19 (18 is taken by MOVE_PIP_ACTIVITY_TO_PINNED_TASK) and the binder is carried via setInsetsFrameOwner/getInsetsFrameOwner. Two OneShotRemoteHandler call sites are updated, and RemoteTransitionHandler calls the boost after startAnimation.",
      "16": "Adapted fix 40c30bb5613e in android-security-16.0.0_r7. The constant is 27 and is added to the @IntDef. The binder goes via insetsFrameOwner, and the transition is read from chain.mTransition. RecentsTransitionHandler passes appThread into startRealRecentsTransition, and the mAnimApp assignment is removed. StageCoordinator is not touched in this diff.",
      "17": "a29de493b054 in android-17.0.0_r1. The changes are the same as the 14 patch, with the same blob hashes for most files."
     },
     "backport_notes": [
      "The AIDL change, Transition.mRemoteDelegate, the server-side WindowOrganizerController handling and all Shell call sites must land together because the method signature changes. Pick a HIERARCHY_OP constant value and parcel field that are free in the target tree (14 uses 18/mReparent; 15/16 use insetsFrameOwner).",
      "On 16, the new toString case follows HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS, which has no break. The new hierarchy-op case follows a SET_SAFE_REGION_BOUNDS block whose break is not visible. Check fall-through when porting."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact security impact and attacker model behind the High EoP rating.",
      "Which permission gates applying a WindowContainerTransaction with SET_ANIMATION_DELEGATE.",
      "Whether other code paths still send RemoteTransition app threads outside system_server.",
      "Whether Transition.fromBinder returns null for finished transitions.",
      "Whether the StageCoordinator paths whose boost was removed are used with shell transitions.",
      "Content of the test ActivityManagerTest#testActivityManager_stripsAppThreadFromRemoteTransition."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-opus-5-5",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0093": {
     "cve": "CVE-2026-0093",
     "summary": "Replaces applicationInfo.loadLabel() with applicationInfo.loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where Settings renders the requesting app's name in the RequestManageCredentials UI, so an app-controlled label can no longer be used to hide from the user what permissions are requested.",
     "component": "platform/packages/apps/Settings, security / credential management consent UI (RequestManageCredentials, CredentialManagementAppAdapter)",
     "weakness": "Improper neutralization of an app-controlled display string in a security consent UI (unsanitized application label can obscure critical information)",
     "root_cause": "RequestManageCredentials and CredentialManagementAppAdapter inserted the requesting app's label into the title (R.string.request_manage_credentials_title via TextUtils.expandTemplate) and into an app-name view using ApplicationInfo.loadLabel(), with no limit on length, number of lines or whitespace. Per the commit message, this let the app developer hide from the user what permissions the app requests. The files import AppUriAuthenticationPolicy, which suggests that these permissions are app/URI authentication policy entries. That is an inference.",
     "fix_analysis": [
      "src/com/android/settings/security/RequestManageCredentials.java (title population around line 251, in the try block that resolves the ApplicationInfo): applicationInfo.loadLabel(getPackageManager()) is replaced with applicationInfo.loadSafeLabel(getPackageManager(), DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) before TextUtils.expandTemplate into request_manage_credentials_title. Static imports for PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and TextUtils.SAFE_STRING_FLAG_FIRST_LINE / SAFE_STRING_FLAG_TRIM are added.",
      "src/com/android/settings/security/CredentialManagementAppAdapter.java (mTitleView population around line 93): same loadLabel -> loadSafeLabel replacement with the same max size and flags, plus the same static imports.",
      "src/com/android/settings/security/CredentialManagementAppAdapter.java (mAppNameView population around line 156): String.valueOf(applicationInfo.loadLabel(mPackageManager)) becomes String.valueOf(applicationInfo.loadSafeLabel(mPackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE)).",
      "Effect: with these flags the label is trimmed, reduced to its first line and limited to a bounded rendered width (general platform knowledge), which limits how much of the screen it can take up."
     ],
     "completeness": [
      "All three loadLabel() call sites shown in the diff for the two files are converted.",
      "The NameNotFoundException fallback paths are not changed. mAppNameView.setText(appName) remains. The diff is cut off before the argument passed to the title expandTemplate in the catch blocks.",
      "The fix only sanitizes the label text. It does not change the dialog layout or how the requested entries are presented. A SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS import is present in RequestManageCredentials.java and is unrelated to this change.",
      "No automated tests were added (Test: Manual per commit message)."
     ],
     "behavior_changes": [
      "Requesting-app labels shown in the RequestManageCredentials UI are now trimmed, limited to the first line and truncated to DEFAULT_MAX_LABEL_SIZE_PX, so long or multi-line labels render differently than before.",
      "The diff adds no new error paths."
     ],
     "preconditions": [
      "Settings shows the RequestManageCredentials UI for a requesting app and resolves that app's ApplicationInfo label. The exact entry point is not shown.",
      "The app developer controls the app's label.",
      "The user is shown the consent screen. Per the commit message, the issue is in what the user sees, not in a permission check."
     ],
     "per_major": {
      "14": "same fix: commit 47f5449218e4 in android-security-14.0.0_r28 (cherry-pick, Change-Id Iefd92a41d84f5cfc3244dbd4a8f7f8d2a9105ad5); 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: commit 7f9aa5805598 in android-security-15.0.0_r16; identical code hunks, with only the import context in RequestManageCredentials.java differing (blob 2c1b45219d0, where android.app.Activity follows the static imports); 7/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 88ab2340c255 in android-security-16.0.0_r7; same hunks and RequestManageCredentials.java blob (2c1b45219d0) as Android 15; 7/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit 9cc2911dc24d in the android-17.0.0_r1 base; hunks identical to Android 14 (blobs 6a1d40aef7a -> 63d86b2e55c); 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit relying on PackageItemInfo.loadSafeLabel(PackageManager, float, int), PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and the TextUtils.SAFE_STRING_FLAG_* constants, which are framework APIs available to the platform-signed Settings app (general platform knowledge). On forks with modified imports in RequestManageCredentials.java, the import hunk may need manual placement. The three code hunks are the same in all four branch diffs."
     ],
     "confidence": "high",
     "unknowns": [
      "The intent or entry point used to start RequestManageCredentials, and any restrictions on its callers, are not shown.",
      "What the title catch blocks pass to expandTemplate when the package is not found is not shown.",
      "Whether other Settings or framework UIs that show a requesting app's label in a credential or KeyChain context use loadLabel() is not shown.",
      "Why the per-major facts report 7/8 patched lines present is not explained by the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0095": {
     "cve": "CVE-2026-0095",
     "summary": "Widens the local buf_size in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the allocation size for the cloned L2CAP buffer is no longer truncated before osi_malloc, preventing a heap buffer overflow in the following memcpy.",
     "component": "platform/packages/modules/Bluetooth, L2CAP flow control / retransmission (system/stack/l2cap/l2c_fcr.cc)",
     "weakness": "Integer truncation leading to undersized heap allocation and heap buffer overflow (CWE-680 / CWE-787 style)",
     "root_cause": "l2c_fcr_clone_buf(BT_HDR* p_buf, uint16_t new_offset, uint16_t no_of_bytes) computes the size of the new buffer as no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN and stored it in a uint16_t buf_size. Both no_of_bytes and new_offset are 16-bit, so their sum plus the header and FCS bytes can exceed 65535. The assignment truncated the value, osi_malloc(buf_size) then returned a buffer smaller than required, and the subsequent memcpy of no_of_bytes bytes to ((uint8_t*)(p_buf2 + 1)) + new_offset wrote past the end of the allocation.",
     "fix_analysis": [
      "system/stack/l2cap/l2c_fcr.cc:l2c_fcr_clone_buf: the single changed line declares buf_size as uint32_t instead of uint16_t. The expression itself is unchanged. It is evaluated in size_t (because of sizeof(BT_HDR)) and is now stored without truncation, so osi_malloc receives the full required size (at most roughly 2*65535 + sizeof(BT_HDR) + L2CAP_FCS_LEN, which fits in 32 bits).",
      "No other logic is changed: p_buf2->offset, p_buf2->len and the memcpy are identical before and after the fix. The allocation size now matches what the memcpy and the later FCS write can use."
     ],
     "completeness": [
      "Covers: the destination-side truncation in l2c_fcr_clone_buf for every caller, since the fix is inside the function.",
      "Leaves out (from the code): no_of_bytes is still not validated against the source buffer (p_buf->len). The memcpy reads no_of_bytes from ((uint8_t*)(p_buf + 1)) + p_buf->offset without checking that the source holds that many bytes. The commit only addresses the undersized allocation, not source bounds.",
      "The return value of osi_malloc is not checked, and the fix does not change this (osi_malloc aborts on allocation failure rather than returning NULL (general platform knowledge))."
     ],
     "behavior_changes": [
      "None functionally visible in the normal case. When no_of_bytes + new_offset + header + FCS exceeds 65535, the function now allocates the correct (larger) buffer instead of a truncated one."
     ],
     "preconditions": [
      "Reaching l2c_fcr_clone_buf in the L2CAP FCR (flow control / enhanced retransmission) code path. The material does not show which callers supply the arguments, nor whether a remote peer can control the values.",
      "A combination of no_of_bytes and new_offset whose sum with sizeof(BT_HDR) and L2CAP_FCS_LEN exceeds 65535."
     ],
     "per_major": {
      "14": "same fix: commit 006620f1caaa in android-security-14.0.0_r28, identical one-line uint16_t -> uint32_t change in l2c_fcr_clone_buf. Present at android-security-14.0.0_r29 (uint32_t buf_size at line 244).",
      "15": "same fix: commit f82bbb37a852 in android-security-15.0.0_r16, identical one-line change (line offset 242).",
      "16": "same fix: commit abc8b696b615 in android-security-16.0.0_r7, identical one-line change (function signature on one line, hunk at 229).",
      "17": "same fix: commit 554513a00597 in android-17.0.0_r1 (base), identical one-line change."
     },
     "backport_notes": [
      "Single self-contained one-line change with no dependencies. It applies cleanly wherever l2c_fcr_clone_buf declares a uint16_t buf_size. Verify that the local type is widened (uint32_t or size_t) and that no other uint16_t intermediate is introduced between the sum and osi_malloc."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers of l2c_fcr_clone_buf can supply large no_of_bytes/new_offset values and whether they are remotely reachable. Per index search, no callers appeared among the top results.",
      "Why the bulletin classifies it as EoP (the material does not describe the trigger or attack surface).",
      "Whether callers enforce the source-side bound (no_of_bytes vs p_buf->len)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0098": {
     "cve": "CVE-2026-0098",
     "summary": "DocumentsUI's Shared.getCallingPackageName now honors the EXTRA_PACKAGE_NAME calling-package override only when the caller's signature matches the platform (SYSTEM_UID) signature. Before, it honored it for any caller flagged as a system or updated system app.",
     "component": "platform/packages/apps/DocumentsUI, src/com/android/documentsui/base/Shared.java (calling package identification)",
     "weakness": "Improper authorization / insufficient trust check: the identity override (calling package name) was gated on installation status (FLAG_SYSTEM / FLAG_UPDATED_SYSTEM_APP) instead of on a signature check.",
     "root_cause": "Shared.getCallingPackageName(Activity) lets the caller replace the value from Activity.getCallingPackage() with the Intent.EXTRA_PACKAGE_NAME string extra. Before the fix, the gate was isSystemApp(info) || isUpdatedSystemApp(info): the caller's ApplicationInfo had to have FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP. So any system-flagged package, including one not signed with the platform key, could make DocumentsUI treat an arbitrary package name as the calling package. The commit message describes the fix as shifting trust from installation status to signatures. The bulletin rates the issue High EoP.",
     "fix_analysis": [
      "Shared.java:getCallingPackageName: removes the private helpers isSystemApp(ApplicationInfo) and isUpdatedSystemApp(ApplicationInfo), and the condition based on FLAG_SYSTEM/FLAG_UPDATED_SYSTEM_APP.",
      "Shared.java:getCallingPackageName: the override now applies only if activity.getIntent() != null && isTrustedSystemSignature(activity.getPackageManager(), info.uid). Here info is the ApplicationInfo of activity.getCallingPackage(). The extra must still be non-null and non-empty.",
      "Shared.java:isTrustedSystemSignature(PackageManager pm, int callingUid) (new): returns pm.checkSignatures(callingUid, android.os.Process.SYSTEM_UID) == PackageManager.SIGNATURE_MATCH. Only callers whose signature matches UID 1000 may supply EXTRA_PACKAGE_NAME.",
      "Single commit touching one file; no other DocumentsUI code is changed."
     ],
     "completeness": [
      "Covers the single override point in Shared.getCallingPackageName. DocumentsUI still accepts the EXTRA_PACKAGE_NAME extra, but only from platform-signed callers.",
      "The check compares signatures against SYSTEM_UID and requires SIGNATURE_MATCH. System or updated system apps signed with a non-platform key are no longer trusted. The diff adds no allowlist or permission-based alternative.",
      "The NameNotFoundException path is unchanged: if the calling package cannot be resolved, the unmodified getCallingPackage() value is returned.",
      "The fix does not change how the returned package name is used elsewhere in DocumentsUI; those callers are not in the material.",
      "The commit says Test: manual; no automated test is added."
     ],
     "behavior_changes": [
      "System or updated system apps (FLAG_SYSTEM / FLAG_UPDATED_SYSTEM_APP) that are not signed with the platform key can no longer override the calling package name via Intent.EXTRA_PACKAGE_NAME. For them, DocumentsUI uses Activity.getCallingPackage().",
      "New null guard: if activity.getIntent() is null, the override is skipped instead of dereferencing the intent.",
      "The gate no longer checks system-app flags. Platform-signed callers that lack FLAG_SYSTEM/FLAG_UPDATED_SYSTEM_APP can now use the override; platform-signed system apps keep it."
     ],
     "preconditions": [
      "A DocumentsUI activity path calls Shared.getCallingPackageName(Activity), and Activity.getCallingPackage() resolves to the caller's package.",
      "Pre-fix: the caller's ApplicationInfo has FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP, and the caller is not necessarily platform-signed.",
      "The launching intent carries a non-empty Intent.EXTRA_PACKAGE_NAME string extra naming another package."
     ],
     "per_major": {
      "14": "same fix: a712c9468e47 in android-security-14.0.0_r28, Shared.java 9f328be69..f691229ab. The code at android-security-14.0.0_r29 shows the patched logic (resolver: 4/8 patched-region lines present).",
      "15": "same fix: b05353f638a7 in android-security-15.0.0_r16, identical diff and blob hashes as 14. Resolver: 4/8 patched-region lines present at android-security-15.0.0_r17.",
      "16": "same fix: 0c8b7870ece9 in android-security-16.0.0_r7. Same hunk content at shifted line numbers (Shared.java bb8a39393..0a5648f01, hunks at lines 227/246/262). Resolver: 4/8 patched-region lines present at android-security-16.0.0_r8.",
      "17": "same fix: a08d1c7ea084 in android-17.0.0_r1 (base), identical diff to 14/15. The resolver reports 3/8 patched lines at android-security-17.0.0_r1; the reason is not shown in the material."
     },
     "backport_notes": [
      "Single self-contained commit in Shared.java. 14/15/17 share the same pre/post blob hashes. 16 has a different base blob, so check how it applies there. PackageManager.checkSignatures(int,int), Process.SYSTEM_UID and SIGNATURE_MATCH are long-standing public APIs (general platform knowledge).",
      "Downstream builds that rely on non-platform-signed system apps passing EXTRA_PACKAGE_NAME to DocumentsUI will lose that override after the backport; verify such integrations."
     ],
     "confidence": "high",
     "unknowns": [
      "Which DocumentsUI activities call Shared.getCallingPackageName and what the spoofed package name is used for. The material does not show these callers, so the concrete privilege gained is not shown beyond the bulletin's High EoP rating.",
      "How DocumentsUI activities are launched so that getCallingPackage() is non-null.",
      "Why the Android 17 security tag shows only 3/8 patched-region lines.",
      "Whether the 14 patch applies to the 16 base without conflicts.",
      "No regression test is included (Test: manual)."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0099": {
     "cve": "CVE-2026-0099",
     "summary": "Adds onBindingDied and onNullBinding handlers to the ServiceConnection objects in HostEmulationManager so the NFC service unbinds from a HostApduService whose binding died or whose onBind returned null, instead of leaving the binding in place.",
     "component": "platform/packages/apps/Nfc (platform/packages/modules/Nfc on 16+), card emulation / HostEmulationManager",
     "weakness": "Missing handling of ServiceConnection lifecycle callbacks (onBindingDied, onNullBinding). On the payment path this leaves a binding made with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS in place for a service that does not work (commit message: \"BAL bypasses in HostApduService\").",
     "root_cause": "HostEmulationManager binds to app-provided HostApduService implementations through mPaymentConnection and mConnection. bindPaymentServiceLocked passes Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS. Before the fix, neither connection overrode onBindingDied or onNullBinding. So if the target service returned a null binder or its binding died, nothing tore the binding down: mPaymentServiceBound / mServiceBound stayed true and mContext.unbindService was not called. Per the commit message, the NFC service was left \"hanging around waiting for a service that has crashed\". The commit title calls this several BAL (background activity launch) bypasses in HostApduService.",
     "fix_analysis": [
      "src/com/android/nfc/cardemulation/HostEmulationManager.java, mPaymentConnection (anonymous ServiceConnection): adds onBindingDied(ComponentName) and onNullBinding(ComponentName). Each takes mLock and calls unbindPaymentServiceLocked(). If mPaymentServiceBound is set, that helper calls mContext.unbindService(mPaymentConnection) and clears mPaymentServiceBound, mPaymentService, mPaymentServiceName and mPaymentServiceUserId.",
      "src/com/android/nfc/cardemulation/HostEmulationManager.java, mConnection (anonymous ServiceConnection): adds onBindingDied and onNullBinding. Each takes mLock and calls unbindServiceIfNeededLocked(). If mServiceBound is set, that helper calls mContext.unbindService(mConnection) and clears mServiceBound. Any further state reset is outside the shown context.",
      "Net effect: a binding to a HostApduService that never delivered a binder, or whose binding died, is released instead of staying in place. For the payment binding, that binding was made with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS."
     ],
     "completeness": [
      "On Android 14 the fix covers the two ServiceConnection objects it patches (mPaymentConnection and mConnection) and both lifecycle callbacks (onBindingDied, onNullBinding).",
      "The Android 14 variant goes through the existing Locked helpers, so the bound-state flags are reset. In the 15/16/17 variants, onNullBinding calls mContext.unbindService(this) directly and does not clear mPaymentServiceBound / mServiceBound in the shown diff. The onBindingDied added to mConnection (15) or HostEmulationServiceConnection (16/17) calls unbindServiceIfNeededLocked() without taking mLock. On 15/16/17 the payment connection gets no new onBindingDied.",
      "The fix does not change the bind flags: bindPaymentServiceLocked still passes BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS. It only shortens the life of a binding that does not work. The material does not show whether a service that binds successfully but never answers APDUs is limited in any other way.",
      "On 16/17 the diff shows another 'private ServiceConnection mConnection =' declaration right after the patched HostEmulationServiceConnection region. The material does not show whether that connection has these callbacks."
     ],
     "behavior_changes": [
      "When onBindingDied or onNullBinding fires on the payment or active HostApduService connection, the NFC service now calls unbindService. On Android 14 the bound-state fields are also cleared.",
      "A HostApduService that returns null from onBind is unbound instead of staying bound.",
      "On 16/17 the payment connection's onNullBinding logs a new NfcEventProto BindState value, SERVICE_NULL_BINDING. The 15+ variants add Log.i lines in the new callbacks."
     ],
     "preconditions": [
      "An installed app exposes a HostApduService (HostApduService.SERVICE_INTERFACE) that the NFC service binds to, either as the payment service (bindPaymentServiceLocked) or through mConnection.",
      "The bound service's onBind returns null, or its binding dies, so that onNullBinding or onBindingDied is delivered.",
      "NFC host card emulation is in use. The payment binding uses BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS."
     ],
     "per_major": {
      "14": "Fixed in platform/packages/apps/Nfc 34a7821a1bfe (android-security-14.0.0_r28): onBindingDied and onNullBinding added to both mPaymentConnection and mConnection, each calling the existing Locked unbind helper under mLock. 4/4 patched lines present at android-security-14.0.0_r29.",
      "15": "Adapted fix in platform/packages/apps/Nfc 7fb6795b51c3 (android-security-15.0.0_r16). mPaymentConnection gets only onNullBinding (mContext.unbindService(this) under mLock). mConnection gets onBindingDied (unbindServiceIfNeededLocked, no lock) and onNullBinding (unbindService(this) under mLock). 4/4 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix in platform/packages/modules/Nfc 127162a9e764 (android-security-16.0.0_r7), under NfcNci/ paths. Same callback additions as 15: onNullBinding on the payment connection, onBindingDied and onNullBinding on HostEmulationServiceConnection. Also adds NfcEventProto logging and a new BindState.SERVICE_NULL_BINDING in NfcNci/proto/event.proto. 6/6 lines present at android-security-16.0.0_r8.",
      "17": "Same as 16: platform/packages/modules/Nfc cfbd02ac1630 in the android-17.0.0_r1 base (NfcNci/ paths, proto change included). 6/6 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-commit fix per major. For 14-style code (packages/apps/Nfc, anonymous mPaymentConnection/mConnection), the 14 variant calls unbindPaymentServiceLocked()/unbindServiceIfNeededLocked() under mLock, so mPaymentServiceBound/mServiceBound are reset. The 15+ onNullBinding calls unbindService(this) directly and does not reset those flags in the shown diff. On 16+ the file is under NfcNci/ in packages/modules/Nfc, and the proto enum addition is only needed if the event-log call is ported."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which exact BAL bypass scenarios the commit title ('Several BAL bypasses') refers to beyond the lifecycle handling shown, and whether other commits under bug 385917501 cover more paths.",
      "The bind flags used for mConnection, which are not shown.",
      "The full body of unbindServiceIfNeededLocked() beyond clearing mServiceBound (context truncated).",
      "Which existing callback in the 15+ payment connection calls bindPaymentServiceLocked again (hunk context only).",
      "Whether the extra mConnection declared after the patched region on 16/17 also handles these callbacks."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0100": {
     "cve": "CVE-2026-0100",
     "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp) so that a RES_TABLE_PACKAGE_TYPE chunk whose 32-bit package id exceeds 255 is rejected and the ARSC fails to load instead of being accepted with an out-of-range package id.",
     "component": "platform/frameworks/base, libs/androidfw (libandroidfw resource table parser, LoadedArsc)",
     "weakness": "Missing bounds/range check on untrusted input (package id field of ResTable_package); per commit message can lead to overflow / unexpected behavior downstream",
     "root_cause": "In LoadedPackage::Load, the package id was taken directly from the ResTable_package header (loaded_package->package_id_ = dtohl(header->id)) without validation. The field in the chunk is 32-bit, but per the commit message resource IDs allocate only 8 bits for the package id. A malformed ARSC with id > 255 was accepted, and the commit message states this could lead to unexpected behavior or overflow during parsing. The neighbouring typeIdOffset field already had an equivalent uint8_t range check in the same function; the id field did not.",
     "fix_analysis": [
      "libs/androidfw/LoadedArsc.cpp : LoadedPackage::Load: reads the id into a local const uint32_t package_id = dtohl(header->id); if package_id > std::numeric_limits<uint8_t>::max() it logs 'RES_TABLE_PACKAGE_TYPE package id is out of valid range' and returns {} (null). Only then is package_id_ assigned via static_cast<int>(package_id). The check is placed before the existing package_id_ == 0 / kAppPackageId dynamic-flag logic and before the typeIdOffset check, mirroring the latter's style.",
      "libs/androidfw/tests/LoadedArsc_test.cpp: new TEST(LoadedArscTest, LoadArscWithInvalidPackageId) builds a minimal in-memory ResTable_header + ResTable_package with id = 256 and asserts LoadedArsc::Load returns null."
     ],
     "completeness": [
      "Covers the single assignment of package_id_ from the chunk header in LoadedPackage::Load; the new test shows that a package chunk with id 256 makes LoadedArsc::Load return null.",
      "The check is id > 255 only; id 0 and id 255 remain accepted (0 continues to be treated as dynamic, as before).",
      "The fix is confined to the LoadedArsc parser. The material does not show whether other consumers of ResTable_package::id (for example the legacy ResTable parser in ResourceTypes.cpp) perform an equivalent check.",
      "Error path returns {} with a LOG(ERROR), consistent with the other validation failures in the function; no new error type is introduced."
     ],
     "behavior_changes": [
      "ARSC files containing a RES_TABLE_PACKAGE_TYPE chunk with id > 255 now fail to load (LoadedPackage::Load returns null; the test shows LoadedArsc::Load returns null) with the log line 'RES_TABLE_PACKAGE_TYPE package id is out of valid range'; previously they were accepted.",
      "Well-formed resource tables (id in 0..255) are unaffected.",
      "New unit test LoadArscWithInvalidPackageId in libandroidfw_tests."
     ],
     "preconditions": [
      "A malformed resource table (ARSC) must be parsed by LoadedPackage::Load (reached via LoadedArsc::Load in the test), with a RES_TABLE_PACKAGE_TYPE chunk whose id field is greater than 255 and which otherwise passes the preceding chunk-size check.",
      "The specific process or entry point exercised for this CVE is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 6a9eb9dd74d0 in android-security-14.0.0_r28 (cherry-pick, Change-Id I438cd0e7f64e2b0ef4cc2bcb4f1f81778216e5e8); identical hunk in LoadedArsc.cpp (@@ -476) and identical test; code context at android-security-14.0.0_r29 shows the check present.",
      "15": "same fix: 86dd9d421620 in android-security-15.0.0_r16; identical hunk (@@ -481) and test; at android-security-15.0.0_r17, 7/8 lines of the patched region present.",
      "16": "same fix: ca855338abcf in android-security-16.0.0_r7; identical hunk (@@ -485) and test; at android-security-16.0.0_r8, 7/8 lines of the patched region present.",
      "17": "same fix: 50d18b36c708 in android-17.0.0_r1 (base release); identical hunk (@@ -549) and test (@@ -421); at android-security-17.0.0_r1, 7/8 lines of the patched region present."
     },
     "backport_notes": [
      "Single self-contained commit; the LoadedArsc.cpp hunk is identical across 14 through 17 with only line offsets differing, so it should apply to trees where LoadedPackage::Load still contains 'loaded_package->package_id_ = dtohl(header->id);'."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream consequence of an out-of-range package id (which computation overflows or which structure is mis-indexed) is not shown; the commit message only says 'unexpected behavior or overflow'.",
      "Which process/entry point (package installation, overlay loading, app resource loading) reaches LoadedPackage::Load for this CVE is not in the material.",
      "Whether the legacy ResTable parser (ResourceTypes.cpp) or other readers of ResTable_package::id have equivalent validation is not shown.",
      "What the resolver's '7/8 lines present' figure means for branches other than 14 is not explained in the material."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28577": {
     "cve": "CVE-2026-28577",
     "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is true and the target WindowToken already contains a window, enforcing one toast window per toast token.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm)",
     "weakness": "Missing validation of a state invariant (toast WindowToken assumed to hold a single toast window; only a per-UID limit was enforced)",
     "root_cause": "In WindowManagerService.addWindow, the only toast-specific guard in the `type == TYPE_TOAST` block was displayContent.canAddToastWindowForUid(callingUid), which limits toasts per calling UID. Nothing in that block checked whether the supplied WindowToken already held a window, even though a TYPE_TOAST WindowToken is meant to hold only one toast window (the comment added by the fix says so). A caller from a different UID that presented the same toast token passed the per-UID check and could attach a second toast window to that token. The added test testAddToastWindow_singleWindowPerToken shows this (\"Add second window with same token but different UID to bypass the canAddToastWindowForUid check\").",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowManagerService.java:addWindow: inside the `type == TYPE_TOAST` block, right after the canAddToastWindowForUid check, the fix adds `if (addToastWindowRequiresToken && !token.isEmpty())`. When it matches, the code logs a WM_ERROR ProtoLog warning and returns WindowManagerGlobal.ADD_BAD_APP_TOKEN.",
      "data/etc/services.core.protolog.json: registers the new ProtoLog message hash -610155150 (\"Adding toast window with non-empty token.\", WARN, WM_ERROR) on the Android 14 branch.",
      "services/core/java/com/android/server/wm/Session.java: refactor for testing. The public Session(service, callback) constructor now delegates to a new @VisibleForTesting Session(service, callback, callingPid, callingUid) constructor, so tests can create sessions for any pid/uid. Production callers still get Binder.getCallingPid() and Binder.getCallingUid(), as before.",
      "services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java adds createTestSession(atms, pid, uid), described as a partial backport that omits process creation. services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java adds testAddToastWindow_singleWindowPerToken. The test adds a toast window from uid 1234 to a TYPE_TOAST token, then asserts that a second toast from uid 1235 on the same token returns ADD_BAD_APP_TOKEN."
     ],
     "completeness": [
      "The new check only applies when addToastWindowRequiresToken is true. How that value is computed is not shown in the material. When it is false, nothing new prevents a second toast window on one token.",
      "The check is inside the `type == TYPE_TOAST` branch, so it only blocks additional TYPE_TOAST windows on a non-empty token. The material does not show whether non-toast window types can be attached to a TYPE_TOAST token, or whether that is checked elsewhere.",
      "The new early return comes after `win.openInputChannel(outInputChannel)` (line 1668 in the context), like the existing ADD_DUPLICATE_ADD return path. The fix does not change that ordering."
     ],
     "behavior_changes": [
      "addWindow returns WindowManagerGlobal.ADD_BAD_APP_TOKEN for a TYPE_TOAST window when addToastWindowRequiresToken is true and the WindowToken already has a child. Before the fix, the test scenario (second UID, same token) was not rejected.",
      "New WM_ERROR ProtoLog warning \"Adding toast window with non-empty token.\" on Android 14 and 17. On Android 15 and 16 the ProtoLog line is commented out.",
      "Session gains a package-private @VisibleForTesting constructor in the 14 commit. The production constructor behaves as before."
     ],
     "preconditions": [
      "The caller reaches WindowManagerService.addWindow through a Session (an IWindowSession binder) with attrs.type == TYPE_TOAST and a params.token that refers to an existing TYPE_TOAST WindowToken already holding a window.",
      "addToastWindowRequiresToken must be true for the caller. The test sets targetSdkVersion O for the calling package. The exact computation is outside the diff.",
      "The second window must pass the existing displayContent.canAddToastWindowForUid check. The test does this by using a different UID from the first window."
     ],
     "per_major": {
      "14": "Same fix plus extra test scaffolding: commit 2f08e7d895e3 in android-security-14.0.0_r28 adds the WMS check, the protolog.json entry, the @VisibleForTesting Session constructor and WindowTestsBase.createTestSession. 8/8 patched lines are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: commit 9af61140db79 in android-security-15.0.0_r16 adds the same WMS check with the ProtoLog line commented out, plus the test. No protolog.json or Session.java change. 8/8 lines are present at android-security-15.0.0_r17.",
      "16": "Adapted fix: commit d9ad94e69f4e in android-security-16.0.0_r7, same shape as 15 (ProtoLog commented out, WMS check plus test only). 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Same fix: commit a00072f03623 in android-security-17.0.0_r1 adds the WMS check with an active ProtoLog call, plus the test (the test uses the WindowRelayoutResult signature of addWindow). No protolog.json change in the diff. 8/8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The functional change is the check in WindowManagerService.addWindow. The Session.java constructor and WindowTestsBase.createTestSession only support the new test.",
      "The 14 commit adds a protolog.json hash entry (-610155150) for the new ProtoLog.w call. The 15 and 16 commits comment the call out instead. Check how your branch handles ProtoLog messages."
     ],
     "confidence": "high",
     "unknowns": [
      "How addToastWindowRequiresToken is computed (target SDK and/or permission conditions) is not in the provided diff or context.",
      "The material does not describe the concrete security impact of a second toast window sharing a token, or why it is rated High EoP.",
      "Why the 17 branch keeps an active ProtoLog call without a services.core.protolog.json change, while 15 and 16 commented it out.",
      "The material does not show whether a same-UID second toast on the same token can ever get past canAddToastWindowForUid and reach the new check."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28578": {
     "cve": "CVE-2026-28578",
     "summary": "Adds a per-package-name length check (enforcePackagePolicyPackageNamesLength) to the three DevicePolicyManagerService setters that accept a PackagePolicy (managed profile caller-id/contacts access policies and credential manager policy), rejecting overlong package names before the policy is processed.",
     "component": "platform/frameworks/base, services/devicepolicy (DevicePolicyManagerService, PackagePolicy setters)",
     "weakness": "Missing input length validation (package names inside a PackagePolicy)",
     "root_cause": "The DevicePolicyManagerService setters that take a PackagePolicy did not check the length of the strings returned by policy.getPackageNames(). Two of them are guarded by isProfileOwner(caller) && isManagedProfile(caller.getUserId()); the credential manager policy setter is guarded by canWriteCredentialManagerPolicy(caller). As a result, a supplied policy could carry package names longer than MAX_PACKAGE_NAME_LENGTH into the service. The bulletin rates this as a High-severity DoS; the material does not show the failure mode downstream.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java: new private helper enforcePackagePolicyPackageNamesLength(@NonNull PackagePolicy policy) loops over policy.getPackageNames() and skips null entries. On Android 14 it calls Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, \"Package name too long\"); on 15/16/17 it calls PolicySizeVerifier.enforceMaxPackageNameLength(pkg).",
      "The helper is called in three setters, each time right after the `if (!mHasFeature) return;` early exit and before getCallerIdentity() and the authorization check. Two are the managed-profile setters (context: Preconditions.checkCallAuthorization(isProfileOwner(caller) && isManagedProfile(caller.getUserId()))). The third is the credential manager policy setter (context: Preconditions.checkCallAuthorization(canWriteCredentialManagerPolicy(caller))), located just before getCredentialManagerPolicy. Each call is guarded by `if (policy != null)`, so a null policy skips the check.",
      "Per the commit message, the targeted APIs are the managed profile contacts access policy, the caller-id access policy (named in the tests) and the credential manager policy. The diff hunks do not show the setter method names."
     ],
     "completeness": [
      "Covers exactly three PackagePolicy entry points in DevicePolicyManagerService. This diff does not touch any other path that ingests a PackagePolicy, and the material does not show whether such paths exist.",
      "Only the length of each string is checked. This change does not bound the number of entries in getPackageNames(), and null entries are skipped rather than rejected.",
      "A null PackagePolicy bypasses the helper.",
      "The length check runs before the caller authorization check; the existing authorization check is unchanged."
     ],
     "behavior_changes": [
      "The three setters now reject a PackagePolicy that contains any package name over the maximum length. On Android 14 this is an IllegalArgumentException (\"Package name too long\") from Preconditions.checkArgument; on 15+ the rejection comes from PolicySizeVerifier.enforceMaxPackageNameLength.",
      "When mHasFeature is false, the setters still return silently before any validation."
     ],
     "preconditions": [
      "Device has the device admin feature (mHasFeature true).",
      "Caller reaches one of the three setters with a non-null PackagePolicy. For the policy to actually be applied, the caller must pass the existing authorization: profile owner of a managed profile for the two contacts/caller-id setters, or canWriteCredentialManagerPolicy for the credential manager setter. The visible part of canWriteCredentialManagerPolicy shows that holding MANAGE_PROFILE_AND_DEVICE_OWNERS is enough.",
      "The PackagePolicy contains at least one package name longer than MAX_PACKAGE_NAME_LENGTH."
     ],
     "per_major": {
      "14": "adapted fix: commit 1d7703a6b66d in android-security-14.0.0_r28. Same three call sites, but the helper uses Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, ...) instead of PolicySizeVerifier. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix as 17: commit e8ff8af05575 in android-security-15.0.0_r16; the helper calls PolicySizeVerifier.enforceMaxPackageNameLength(pkg). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix as 17: commit 48c17df8bdc4 in android-security-16.0.0_r7; the helper calls PolicySizeVerifier.enforceMaxPackageNameLength(pkg). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "base fix: commit 4914d6e98bd2 in android-17.0.0_r1 (the authorization context uses mDeviceAdmins.isProfileOwner(caller)). 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Android 14 commit lists two Cherrypick-From references (4914d6e98bd2... and 62a1a105f243...). It uses an inline Preconditions.checkArgument against MAX_PACKAGE_NAME_LENGTH instead of PolicySizeVerifier.enforceMaxPackageNameLength. A tree without PolicySizeVerifier can use that form, provided MAX_PACKAGE_NAME_LENGTH is visible in DevicePolicyManagerService.",
      "Only DevicePolicyManagerService.java is changed. In each of the three setters, place the helper call after the mHasFeature early return."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact names of the three patched setter methods (hunk headers only show the class; the names are inferred from the commit message and tests).",
      "The concrete downstream failure behind the DoS, including whether the policy is persisted.",
      "Whether other PackagePolicy ingestion paths in DevicePolicyManagerService remain unchecked.",
      "The value of MAX_PACKAGE_NAME_LENGTH, what PolicySizeVerifier.enforceMaxPackageNameLength throws, and whether PolicySizeVerifier exists on the Android 14 branch."
     ],
     "meta": {
      "generated": "2026-10-03",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "15": {
   "counts": {
    "announced": 54,
    "certain": 54,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-64505",
     "osv_id": "ASB-A-463980379",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "libpng's png_set_quantize now allocates and identity-initialises the quantize_index array at PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so out-of-range palette indices used by png_do_quantize no longer access memory past the heap buffer; a companion commit frees the previous quantize_index before reallocation and makes quantize_sort a local variable.",
      "confidence": "high",
      "backport_notes": [
       "Apply in order: 3547b8454a7e (png_free of quantize_index, quantize_sort made local), then 299ab85302d7 (PNG_MAX_PALETTE_LENGTH allocation). The second diff's context lines depend on the first. If the target already has the png_free guard (as Android 14/17 do), only the allocation-size change is needed, and the png_malloc argument line must match the local variant.",
       "Keep the png_struct quantize_sort member, because the backport deliberately avoids the pngstruct.h ABI change."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "299ab85302d76dc0d93499fa44ad8a791e744d5b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "3547b8454a7eba950b3aa767642153ebe991bece",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-64720",
     "osv_id": "ASB-A-463995203",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In libpng pngrtran.c png_init_read_transformations, the palette compositing loop now checks PNG_FLAG_OPTIMIZE_ALPHA. When the flag is set, it only premultiplies each palette RGB component by its tRNS alpha (using the gamma_to_1/gamma_from_1 tables) instead of compositing it against the background color, so the values stay within the premultiplied-alpha invariant that png_image_read_composite relies on later.",
      "confidence": "high",
      "backport_notes": [
       "One self-contained hunk in pngrtran.c with no dependencies. It applied to two slightly different libpng bases (hunk at line 1701 on 14/17, line 1697 on 15/16) without adaptation, so it should apply cleanly to any tree where the png_composite block for partial-alpha palette entries matches. The code uses + 128 rounding even though the commit message says + 127; follow the code."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "2a900c24d056c3de176d84d9c2c905427dd83fd6",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-65018",
     "osv_id": "ASB-A-463998243",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Two-commit fix in external/libpng pngread.c. The first commit rejects IHDR/output bit-depth mismatches in png_image_finish_read. The second reverts that check and instead sends interlaced 16-to-8 bit reads in png_image_read_direct through an intermediate row buffer (new png_image_read_direct_scaled), so that png_combine_row no longer writes IHDR-depth rows into the caller's 8-bit buffer.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the two commits in order (6a5cceb169b3, then 9f93c45faadd) or squash them. The second commit's png_image_finish_read hunk removes lines added by the first and will not apply to a tree that lacks them; in that case drop that hunk and take only the png_image_read_direct / png_image_read_direct_scaled additions. Shipping only the first commit stops the overflow but rejects all bit-depth conversions through the simplified API.",
       "The new branch uses display->local_row, display->first_row, display->row_bytes and png_safe_execute. Confirm these exist in the target libpng, and check that png_get_rowbytes returns the transformed row size at that point, before porting. No fork comparison was provided."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "6a5cceb169b3aaf91276ab09e2f709fd24af04c3",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "9f93c45faadd10bef0e37ed3510dfc7d873c0048",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2026-0039",
     "osv_id": "ASB-A-470966846",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In platform/external/dng_sdk, the libdng_sdk build is changed so that UBSan integer-overflow detections no longer go to the aborting ubsan-minimal runtime. They go to a library-private runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that throws std::runtime_error, which turns a hard abort into a C++ exception.",
      "confidence": "medium",
      "backport_notes": [
       "The Android.bp hunk has the same blob ids (1978b39..3d95a7b) in 14 through 17. The OWNERS hunk varies by branch and has no functional effect.",
       "The approach depends on libdng_sdk being built with -fexceptions and rtti (present in libdng_sdk-defaults), on the build honoring the combination of sanitize.never, manual -fsanitize cflags and -fno-sanitize-link-runtime, and on a caller catching the exception. Forks with a different libdng_sdk module layout should verify all of these."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0040",
     "osv_id": "ASB-A-467994860",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ exception (new ubsan-replacement/ubsan_throwing_runtime.cpp) instead of being handled by the ubsan minimal runtime with a hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "This is a single build-config commit. It applies where the libdng_sdk cc_library has cflags [\"-DqDNGValidate=0\"] after defaults [\"libdng_sdk-defaults\"], and it relies on the defaults already setting rtti: true and cppflags -fexceptions (lines 141-142 in the Android 15 context). The OWNERS hunk is branch-specific and can be dropped.",
       "The approach depends on Soong honoring sanitize.never together with hand-written -fsanitize cflags and -fno-sanitize-link-runtime (per the in-diff comment). After a backport, check that libdng_sdk.so has no undefined __ubsan_handle_* symbols."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0041",
     "osv_id": "ASB-A-467994310",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error, through a custom in-library runtime in ubsan-replacement/ubsan_throwing_runtime.cpp, instead of hard-aborting the process through the ubsan-minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching three files, with no dng_sdk source changes. The Android.bp hunk has the same index (1978b39..3d95a7b) on all four branches. The resolver's partial line counts (3/5, or 3/4 on 15) are a heuristic; code context confirming the change is only available for 15 (r17).",
       "The approach relies on -fexceptions, which is already set in libdng_sdk-defaults, and on sanitize never combined with explicit -fsanitize cflags, as described in the Android.bp comment. Downstream build customizations to this module should keep both."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0042",
     "osv_id": "ASB-A-456471487",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a library-local runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that turns detected integer-overflow checks into C++ exceptions, so a detected overflow no longer hard-aborts the decoding process.",
      "confidence": "high",
      "backport_notes": [
       "The change is build configuration plus one new source file. It applies cleanly where the libdng_sdk cc_library has the single cflags: [\"-DqDNGValidate=0\"] line and the defaults enable C++ exceptions (-fexceptions). The approach depends on what the Android.bp comment describes: setting sanitize 'never' combined with raw -fsanitize= cflags (plus -fno-sanitize-link-runtime) works in Soong. Verify this on the target build system.",
       "The per-major resolver reports 3/5 (3/4 on 15) lines of the patched region present at the latest tags, but the Android 15 code context shows the complete fix in place. Verify by checking for the srcs entry ubsan-replacement/ubsan_throwing_runtime.cpp."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0043",
     "osv_id": "ASB-A-453649377",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits (signed and unsigned add/sub/mul/negate/divrem) go to a runtime private to the library that throws std::runtime_error, instead of linking the Soong ubsan-minimal runtime. Detected arithmetic overflows in DNG processing become C++ exceptions rather than a hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. It depends only on the existing defaults (rtti: true, cppflags -fexceptions), which must stay in place so the thrown std::runtime_error can unwind. The Android.bp hunk is the same on all four branches (index 1978b39 -> 3d95a7b), so it applies wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"].",
       "sanitize.never: true turns off every Soong-managed sanitizer for the module, so a downstream tree that relies on other Soong sanitizers for libdng_sdk would need to re-add them through explicit cflags (general platform knowledge). The resolver's 3/4 or 3/5 line counts are partial matches; the r17 code context shows the complete patched region on Android 15."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0044",
     "osv_id": "ASB-A-449728942",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error from a handler file shipped in the project (ubsan-replacement/ubsan_throwing_runtime.cpp) instead of hard-aborting via the ubsan_minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with no dependency. Apply the Android.bp hunk to the libdng_sdk cc_library (not to libdng_sdk-defaults), keep the cflags exactly as in the diff, and add the new ubsan-replacement/ubsan_throwing_runtime.cpp. The fix relies on cppflags -fexceptions and rtti: true, which are present in libdng_sdk-defaults in the shown context. The OWNERS hunk can be dropped."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0051",
     "osv_id": "ASB-A-461790658",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that signed/unsigned integer-overflow UBSan hits throw a C++ std::runtime_error (via a bundled replacement runtime) instead of aborting the hosting process through the ubsan minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Build-config-only change plus one new source file. It applies to a branch whose libdng_sdk module still has cflags: [\"-DqDNGValidate=0\"] and whose libdng_sdk-defaults set cppflags -fexceptions, which the throw depends on. The target toolchain must accept -fno-sanitize-link-runtime (general platform knowledge).",
       "The resolver reports only part of the patched region at the latest tags: 3/4 lines on Android 15 and 3/5 lines on 14, 16 and 17. Even so, the Android 15 code context at android-security-15.0.0_r17 shows all added lines present."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0052",
     "osv_id": "ASB-A-467352655",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of invoking the Soong-linked ubsan-minimal runtime that aborts the hosting process.",
      "confidence": "high",
      "backport_notes": [
       "The Android.bp hunks for 14/15/16 are byte-identical (same index 1978b39..3d95a7b), so the patch applies cleanly wherever the libdng_sdk module has `cflags: [\"-DqDNGValidate=0\"]` directly after `defaults`. For trees with the 17-style layout, add the runtime source to the module's existing srcs list instead. Keep `sanitize: { never: true }` together with `-fno-sanitize-link-runtime`: with Soong's misc_undefined the minimal runtime is used, and the compiler emits the *_minimal handler names rather than the __ubsan_handle_*_overflow symbols this file defines (general platform knowledge).",
       "The library must keep `cppflags: [\"-fexceptions\"]` and `rtti: true` from libdng_sdk-defaults so the thrown std::runtime_error can unwind. Check the consumer catch sites in the target tree, since they are not shown in the material."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0080",
     "osv_id": "ASB-A-462431872",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes the libdng_sdk build so that integer-overflow sanitizer hits throw a std::runtime_error (from a bundled replacement ubsan runtime) instead of hard-aborting through the ubsan_minimal runtime.",
      "confidence": "medium",
      "backport_notes": [
       "The Android.bp hunk is identical on 14/15/16 (base blob 1978b39). On trees with an explicit per-file srcs list (as in 17), add ubsan_throwing_runtime.cpp to that list instead. The OWNERS hunk is non-functional.",
       "Google applied sanitize.never: true, the explicit -fsanitize cflags, -fno-sanitize-link-runtime and the new runtime source together in every branch. Backport them as one unit."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0097",
     "osv_id": "ASB-A-446114623",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In the LE Passkey Display / Key Notification association models, the Bluetooth SMP layer now waits for both user approval (SMP_SecurityGrant with SMP_SUCCESS) and remote passkey confirmation (STK generation) before sending SMP_KEY_READY_EVT. A user rejection now fails the pairing with SMP_PAIR_AUTH_FAIL, and SMP pairing is cancelled when the BTM pairing timer expires in BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with an LE pairing active.",
      "confidence": "medium",
      "backport_notes": [
       "Apply all five file changes together. The new struct in smp_int.h is needed by smp_keys.cc and smp_api.cc. Put the btif_dm.cc flag init in whichever function handles the LE key-notification event (btif_dm_ble_key_notif_evt on 14/15, btif_dm_ble_passkey_notif_evt on 16). Use btm_cb or btm_sec_cb and the logging macros that match the branch.",
       "The commit message names a feature flag, but the diff does not check it."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "c18ac65121e16e7ab352019283259c2e86a61cd1",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-21352",
     "osv_id": "ASB-A-483693973",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Bulk update of external/dng_sdk to 1.7.1 build 2471. It adds integer-overflow and size checks to the JXL decode/encode buffer-size computations in dng_jxl.cpp, fixes an off-by-one table read in dng_opcode_MapTable::ReplicateLastEntry, adds an overflow check to dng_ref_counted_block::Allocate, and guards several divide-by-zero cases that occur when blackLevel is 65535.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, but it is a vendor version bump (2410 to 2471). The dng_jxl.cpp hunks depend on the new 3-argument SafeInt64Mult added in dng_safe_arithmetic.h/.cpp, so apply those together. They also call SafeInt32Add and SafeInt32Mult, which this diff does not add, so confirm they exist in your tree. Branches at a different SDK build (for example 2502 at the latest security tags) will not match the dng_flags.h hunk, so verify the guards in dng_jxl.cpp and dng_misc_opcodes.cpp rather than the version string."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7a737bb68ca67e80e9eefff234d2dbbe55f19fba",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-21353",
     "osv_id": "ASB-A-483697751",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Updates external/dng_sdk from build 2410 to 1.7.1 build 2471, adding overflow-checked arithmetic and range checks to the JXL decode/encode buffer setup in dng_jxl.cpp, an overflow check in dng_ref_counted_block::Allocate, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and divide-by-zero guards for blackLevel == 65535 in the GainMap, MapTable, Vignette and BaselineMapPoly paths.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit. Decode in dng_jxl.cpp uses the new 3-argument SafeInt64Mult declared in dng_safe_arithmetic.h and defined in dng_safe_arithmetic.cpp, so those files must be taken together. The security-relevant hunks do not use the dng_tag_codes.h tcWebP addition or the __USE_BUILTIN_SMULL_OVERFLOW change."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7a737bb68ca67e80e9eefff234d2dbbe55f19fba",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-26418",
     "osv_id": "ASB-A-351830787",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a MANAGE_USERS-or-INTERACT_ACROSS_USERS permission check to the binder-exposed CarDevicePolicyService.setUserDisclaimerAcknowledged(int) in packages/services/Car, and documents the requirement with @RequiresPermission on the car-lib CarDevicePolicyManager wrapper.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained commit. Between branches only the @AddedInOrBefore context annotation differs. Check that CarDevicePolicyService.java imports android.content.pm.PackageManager, because the diff uses PackageManager.PERMISSION_GRANTED and the material does not show the imports."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Car",
       "sha": "20d4c410ce12c3ae070c0d9f738d803ce9ecbd48",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Car"
     ]
    },
    {
     "cve": "CVE-2025-32348",
     "osv_id": "ASB-A-406880479",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix stops pinned windows (for example PIP) from counting as a visible window when the system decides whether to allow a background activity launch (BAL), for both the caller and the real caller, and in the per-process \"activity in visible task\" exemption.",
      "confidence": "medium",
      "backport_notes": [
       "On 15, do not pick r10 alone because it was reverted. Use the r13 reapplication (b10fde7507d7). Also check the later changes visible at r17: WindowProcessController.hasVisibleNotPinnedActivity, and the uidHasActiveVisibleNotPinnedWindowPredicate constructor argument in BackgroundLaunchProcessController plus its use in isBoundByForegroundUid.",
       "The test mocks need mService.mVisibleActivityProcessTracker injected via setViaReflection. The BackgroundLaunchProcessController test call sites need the new inPinnedWindow argument."
      ],
      "meta": {
       "generated": "2026-10-04",
       "author_model": "claude-opus-5-5",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r10",
      "date": "2025-09-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "483fba0b4777ca2bdf8596425fe4d9ec4704fd30",
       "tag": "android-security-15.0.0_r10",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "294a98e56fd5a1128b4479d2f7119ae0225c1427",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "b10fde7507d70e2bb400e38df52669bab0ed0c07",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "df2a35dc2f8b2a1bbe3663ad5351d3afce6f7722",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 4,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 272,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48595",
     "osv_id": "ASB-A-430889718",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix is a third-party SQLite version bump: Android 15 installed the SQLite 3.44.5 sources (later reverted), Android 16 selects 3.44.5 (replacing 3440300) through the RELEASE_PACKAGE_LIBSQLITE3 build flag, and Android 14 updates its single dist/ tree from SQLite 3.39.2 to 3.39.6. The material identifies no specific code-level flaw.",
      "confidence": "medium",
      "backport_notes": [
       "On branches with the versioned layout (dist/sqlite-autoconf-<ver>/ selected by RELEASE_PACKAGE_LIBSQLITE3), a backport needs both the external/sqlite source install (e0f74ada1551) and a build/release flag_values change that selects 3440500 for the relevant release config, as 16 did for ap3a in b11607ea3ec7. The 15 submission was reverted for a performance regression, so plan to re-check performance.",
       "On branches with a single dist/ tree (the Android 14 layout), apply the upstream update in place and regenerate dist/Android.patch so the Android customizations in sqlite3.c and shell.c are kept."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/build/release",
       "sha": "df01bf9a7498c7933413b6ce2a351ab1478c00a1",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "e0f74ada1551bc07d86fdaa251b913831d4a8d82",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/build/release",
       "sha": "90a5586f0d3afb87347844e33a73e1f280d594c7",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "1c73770252076943b1b087ad38340f3655c4222b",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "c78d3d42798de8e8b8fc1d4c3afdf2be7891e448",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 9,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/build/release",
      "platform/external/sqlite",
      "platform/vendor/google_shared/build/release"
     ]
    },
    {
     "cve": "CVE-2025-48612",
     "osv_id": "ASB-A-429417453",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The NFC default payment app picker in Settings (DefaultPaymentSettings) no longer parses the selected preference key string into a ComponentName and user id. It now looks the key up in a map of PaymentAppInfo objects enumerated from PaymentBackend, so only a known candidate payment app can be set as default.",
      "confidence": "high",
      "backport_notes": [
       "Take both files together: DefaultPaymentSettings depends on the new PaymentAppInfo.getKey()/hashCode()/equals() in PaymentBackend.java. The same two-file diff was used on 14, 15, 16 and 17. For a Settings fork, apply the final relanded state; you do not need to replay the Android 15 revert/reland pair.",
       "The Android 15 revert cites a regression, but the reland hunks are identical to the original. If a separate regression fix exists, it is not in the material, so test the picker on a device with multiple payment apps and work/managed profiles after backporting."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "adac3d79c0f30b13aadfb75a78eb02fe60945ad6",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "1db9b27682b8b08dc6479b694e3f969b334d126e",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "7791c4097d931a467fca876247ff65017c7a7617",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48615",
     "osv_id": "ASB-A-433250316",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 1024-character bound (package name + class name + 1) on media button receiver component names in MediaButtonReceiverHolder: commit 1 covers components resolved from a PendingIntent, and commit 2 covers ComponentNames passed directly as broadcast receivers.",
      "confidence": "medium",
      "backport_notes": [
       "Apply commit 1 before commit 2: commit 2's diff is based on commit 1's file state (commit 1 introduces MAX_COMPONENT_NAME_LENGTH, and commit 2 replaces its inline length computation with componentNameTooLong). Both are confined to MediaButtonReceiverHolder.java and add no imports (Log and TextUtils are already imported).",
       "On Android 14 and 16 branches, check whether commit 2 is present: look for componentNameTooLong and the IllegalArgumentException in create(int, ComponentName). The material only shows commit 1 on those branches."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8f8c16cee29b1e035a07014648de67f181401048",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "595ed980538218b6dbdce4c4dab7bb1021e053e9",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48616",
     "osv_id": "ASB-A-438973280",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "KeyguardViewMediator now asks KeyguardUpdateMonitor.isUserInLockdown (the strong auth tracker that fires onStrongAuthStateChanged) instead of LockPatternUtils.isUserInLockdown at all three lockdown checks, so the keyguard shows on lockdown even when it is externally disabled (e.g. app pinning).",
      "confidence": "high",
      "backport_notes": [
       "One self-contained commit. Across branches the only adaptation is how the selected user id is obtained: KeyguardUpdateMonitor.getCurrentUser() on 14 and 17, mSelectedUserInteractor.getSelectedUserId() on 15 and 16. The fix relies on KeyguardUpdateMonitor.isUserInLockdown(int) and on mUpdateMonitor being available in KeyguardViewMediator, which the diffs in all shown branches use.",
       "Forks that added more mLockPatternUtils.isUserInLockdown calls in KeyguardViewMediator or in other keyguard callbacks should convert them to mUpdateMonitor.isUserInLockdown as well, as the updated javadoc advises."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r12",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9adff3df028526555f868eda1f06ec34ec7f41a5",
       "tag": "android-security-15.0.0_r12",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 182,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48648",
     "osv_id": "ASB-A-396667508",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "NotificationManagerService stops using PackageManagerInternal.isSameApp (which, per the commit message, has an SDK sandbox exception) for its package-ownership check and compares app ids directly via UserHandle.isSameApp against getPackageUid, rejects SDK sandbox uids in areNotificationsEnabledForPackage, and adjusts dependent call sites.",
      "confidence": "medium",
      "backport_notes": [
       "When porting the ownership-check change on a 15/16-shaped tree, also port the resolveNotificationUid USER_ALL -> UserHandle.getUserId(callingUid) change and the redirection of the direct mPackageManagerInternal.isSameApp sites (Wellbeing check, getEffectsSuppressor) to isCallerSameApp, as the 15/16 commits do.",
       "The test rework depends on UiServiceTestCase owning the PackageManagerInternal mock (mPmi) with getPackageUid stubs, and on SystemUtil from compatibility-device-util (added to Android.bp in the 17 diff); forks with a different test base may need to adapt."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3d502bcba4954eeb3bfcc6f764757d7cb63802e9",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48649",
     "osv_id": "ASB-A-339109116",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Threads a restorePregrantedPermissions flag through the clearApplicationUserData path (ActivityManager -> AMS -> PackageManagerService -> PermissionManager). An app that clears its own data through the public ActivityManager.clearApplicationUserData() no longer gets default-granted or role-granted runtime permissions re-granted. The restoring path stays available but now requires CLEAR_APP_USER_DATA.",
      "confidence": "high",
      "backport_notes": [
       "This is a cross-layer signature change. IPackageManager.aidl, IActivityManager.aidl, PermissionManagerServiceInterface and PermissionManagerServiceInternal all change, so every implementor and every direct IPackageManager caller must be updated in the same commit. On 14-style branches where PermissionService.kt resetRuntimePermissions is a TODO stub, AppIdPermissionPolicy.kt does not need changing (follow the 14 diff).",
       "The security logic sits in three places: the AMS permitted check (&& !restorePregrantedPermissions), the if (restorePregrantedPermissions) guard around grantRuntimePermissionInternal in PermissionManagerServiceImpl, and the restorePregrants conditional in AppIdPermissionPolicy.kt. Verify all three after merging."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "452f0beb15a97d475dbae5e77cf0967e9b3b6dcf",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48652",
     "osv_id": "ASB-A-452042097",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstaller's InstallStart no longer lets a caller that holds INSTALL_PACKAGES skip the unknown-sources user-restriction checks when it starts the installer with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent. The bypass now applies only to non-intent installs by INSTALL_PACKAGES holders and to privileged callers that set EXTRA_NOT_UNKNOWN_SOURCE.",
      "confidence": "medium",
      "backport_notes": [
       "The Java hunk depends on the locals isInstallPkgPermissionGranted, isPrivilegedAndKnown and intentAction in InstallStart.onCreate. A target tree must already compute them, or the computation must be added.",
       "If the target tree has the PIA v2 path (usePiaV2() / InstallRepository.kt), consider taking the Kotlin hunk from the Android 16 commit as well. The Android 15 commit leaves that path unchanged."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0985478406c2cdbcff01074ca1d3012c670ed6f9",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 4dc009cd724c",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0009",
     "osv_id": "ASB-A-329631990",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the Photopicker's automatic finish-on-first-selection behaviour in single-select mode and instead always enables the selection bar so the user must explicitly confirm the selected media before it is returned to the calling app.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the Revert^2 commit first, then the Event.kt duplicate-removal commit. Pick the MediaGrid.kt and SelectionBar.kt variant that matches the target branch. Branches with shouldIndicateSelected or PhotopickerRuntimeEnv gating, such as android-15.0.0_r36 per index search, need the 16/17 hunks.",
       "In the Android 15 r17 context, the new isEnabled(config, deferredPrefetchResultsMap) signature and the MediaStore and LocalPhotopickerConfiguration uses in SelectionBar.kt have no visible imports. SelectionBarFeatureTest still calls isEnabled with one argument. Check that the 15 tree compiles with the surrounding imports and overloads before reusing it."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "e7bcd640810f8a5b4dd2aba89769dbfdc7a84841",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "6d81d42dbc7a35307bee1501c88eee146c6c1b5c",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "first_commit_before_announcement_days": 91,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/providers/MediaProvider"
     ]
    },
    {
     "cve": "CVE-2026-0018",
     "osv_id": "ASB-A-449392803",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AccessibilityManagerService now validates each app-declared accessibility QS tile service through PackageManagerInternal, outside the service lock: the service must resolve, be exported, be protected by BIND_QUICK_SETTINGS_TILE and be enabled. AccessibilityUserState records a feature-to-tile mapping only when the tile component is in that validated set.",
      "confidence": "medium",
      "backport_notes": [
       "Apply f3ef4dd5056e first. Then apply 40729ba7fc18 if the target AccessibilityManagerServiceTest lacks a PackageManagerInternal mock registered through LocalServices (Android 15 needed it).",
       "AccessibilityTileUtils.java is identical across branches. The version-specific hunks are the installed-services container (mInstalledServices on 15 and 16, getInstalledServices()/buildInstalledServicesMapLocked on 17) and the context around readInstalledAccessibilityShortcutLocked (a flag-guarded shortcut-clearing block on 16, the same block unguarded on 17)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r13",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f3ef4dd5056e7f610890e83423935283a57672ae",
       "tag": "android-security-15.0.0_r13",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "40729ba7fc182485912bb79bc6ba04cc74e50987",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "first_commit_before_announcement_days": 91,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0036",
     "osv_id": "ASB-A-405392600",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "StageCoordinator.startAnimation now detects a \"malformed\" split-enter transition (opening tasks that belong to a split stage but cover at most one stage while split is not active) and starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea instead of letting them be shown outside of split.",
      "confidence": "high",
      "backport_notes": [
       "Dependencies to check in the target tree: a stored Transitions reference in StageCoordinator (add mTransitions if only a constructor local exists), the TRANSIT_CLOSE static import, StageTaskListener visibility (it must be public for the common.split package), and whether an isOpeningMode helper already exists in shared TransitionUtil (A16 reuses it, A14/A15/A17 define a local one). Use isSplitActive() where the branch has already replaced mMainStage.isActive()."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "fa1c418fd8aa86bc7e0b25a5b47a40c2694f1359",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0045",
     "osv_id": "ASB-A-380091558",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In the Bluetooth stack, btm_sec_is_upgrade_possible() now returns true when the peer is temporarily bonded and the service requires authentication. Separately, bta_jv_rfcomm_connect() no longer registers an RFC_MUX security service record with the client sec_mask.",
      "confidence": "medium",
      "backport_notes": [
       "On 15 and 16 Google changed btm_sec.cc and removed the RFC_MUX BTM_SetSecurityLevel block in bta_jv_rfcomm_connect in the same commit. On 14 only btm_sec.cc was changed. Keep the hunks together where the block exists.",
       "Field access differs by branch: p_dev_rec->security_required / is_bond_type_temporary() on 14 versus p_dev_rec->sec_rec.* on 15 and 16. If the target already has the flagged upgrade_temp_bonding_on_auth_req variant (16-style), replace it with the unflagged direction-aware check and drop the aconfig flag."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "c1a809d2333ead968166d376beb7bad205e53b9c",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0046",
     "osv_id": "ASB-A-443272513",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes the owner of the Letterbox input interceptor's InputWindowHandle from system_server's PID/UID to the letterboxed app's session PID/UID, so input obscured-touch checks no longer treat the letterbox as a trusted system window.",
      "confidence": "high",
      "backport_notes": [
       "Single-hunk change: in the Letterbox.InputInterceptor constructor, switch the two ownerPid/ownerUid assignments from WindowManagerService.MY_PID/MY_UID to win.mSession.mPid/win.mSession.mUid. Surrounding lines (layoutParamsType, inputConfig) vary by release and should stay as they are on the target branch.",
       "Requires the WindowState passed to InputInterceptor to have a non-null mSession. The same constructor already dereferences win.mWmService."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8eb831ece18d33bf5d931acf8640f529c69bf97f",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0048",
     "osv_id": "ASB-A-463364410",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "When WindowState.hide() is called with doAnimation=true for a force-hidden window (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility, or mPermanentlyHidden) that is animating, it now cancels the running animation and, if the window is playing an exit animation, hides it immediately without a hide animation.",
      "confidence": "high",
      "backport_notes": [
       "The hunk is identical on all four majors and only uses existing WindowState fields and methods (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, mAppOpVisibility, mPermanentlyHidden, mAnimatingExit, isAnimating(), cancelAnimation()). It should apply cleanly to any fork whose hide() still contains the `if (!mToken.okToAnimate()) { doAnimation = false; }` anchor."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c9bc382318ddf126ffb707de714d3b1cde794e9d",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0050",
     "osv_id": "ASB-A-290364858",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AdapterService.handleBondStateChanged now also revokes the stored per-device access permissions (message, phonebook and a third stored permission) when a device's bond state leaves BOND_BONDED (fromState == BOND_BONDED), not only when it reaches BOND_NONE. A device that reuses a bonded address and tries to rebond therefore loses the previously granted permissions, and the user is asked for consent again.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with one functional change: add the static import of BluetoothDevice.BOND_BONDED and widen the `if (toState == BOND_NONE)` condition in handleBondStateChanged. The 15 and 16 cherry-picks apply the clause with no flag check, even though the 15 commit message still lists the reboke_permission_on_unbond flag. The 17 form calls Flags.rebokePermissionOnUnbond().",
       "The new unit test mocks mMockContext.getSharedPreferences in AdapterServiceTest. On heavily customized trees the test may need changes, but the production change does not depend on it."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "da365e87dce9261c98dd093c1cedf98c7238e7f6",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0055",
     "osv_id": "ASB-A-460779368",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstallerService.createSessionInternal now validates SessionParams.volumeUuid with StorageManager.convert() when INSTALL_FORCE_VOLUME_UUID is requested, rejecting the session with IllegalArgumentException instead of accepting an arbitrary string that later crashed the system.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk: the diff adds no import or other file change and relies on the existing INSTALL_FORCE_VOLUME_UUID branch in createSessionInternal; the hunk applied unchanged across 14 through 17, differing only in line offset."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "613fa6465e4d444ad1ef57203249f13b5eef1909",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0056",
     "osv_id": "ASB-A-462431486",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds two validation checks on ResStringPool_header.styleCount in ResStringPool::setTo (libs/androidfw/ResourceTypes.cpp). A string pool whose style offset array would not fit before stringsStart, or whose styleCount would overflow when multiplied by sizeof(uint32_t), is now rejected with BAD_TYPE instead of being accepted.",
      "confidence": "high",
      "backport_notes": [
       "The ResourceTypes.cpp hunk is self-contained and identical from 14 through 17. It relies only on mEntryStyles, mHeader and std::numeric_limits. The new test file uses androidfw/StringPool.h (StringPool, StyleString, Span, FlattenUtf8, NoOpDiagnostics), BigBuffer and android::util::Copy. On trees where those helpers differ, the test can be adapted without affecting the fix."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e80f76ded67a090441448defdac937329e102e61",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0059",
     "osv_id": "ASB-A-447536200",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "RCE"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds remaining-buffer-length tracking (a bytes_left in/out parameter) to sdpu_build_attrib_seq and introduces base-size accounting in process_service_attr_rsp, so the SDP client no longer writes an attribute ID sequence past the end of the SDP_DATA_BUF_SIZE request buffer.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. The diff context in every branch shows the existing base_bytes / bytes_left handling in sdp_snd_service_search_req that this fix builds on. Because the sdpint.h prototype changed, any downstream caller of sdpu_build_attrib_seq must be updated. The loop-variable cleanups in sdp_utils.cc are not functional and can be dropped to reduce conflicts.",
       "The latest security tags show only partial line matches for the patched region (5/8 or 6/8). Check that sdpu_build_attrib_seq takes uint16_t& bytes_left and that process_service_attr_rsp has the base_bytes check."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "59f3f429e37eebcc1c457adb816d222e6361bb51",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0060",
     "osv_id": "ASB-A-459639258",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Hardens the \"Experimental: Enable ANGLE\" developer option in Settings. On Android 15 (and 16/17) the switch summary and the reboot dialog are reworded to warn users to back up data and that a factory reset may be needed (plus a translation import). On Android 14 the switch is also gated behind the debug.graphics.angle.developeroption.enable system property.",
      "confidence": "medium",
      "backport_notes": [
       "Apply in order: 46f81fda8cd3, then 73a18cca95cc, then 2b5834d47f28. The last one is marked DO NOT MERGE ANYWHERE and is tied to the 15 locale layout, so expect conflicts if locale files differ. The translations match only the first wording.",
       "A 14-style backport (2097b24534af) also needs getBoolean added to GraphicsDriverSystemPropertiesWrapper and its Injector, and the tests updated to set debug.graphics.angle.developeroption.enable=true. Otherwise the controller tests will see the preference disabled."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "2b5834d47f286a3fd8296af0b1ebf2d2bdeacce3",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "46f81fda8cd3a02062e82d8fc8156e1510d3111f",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "73a18cca95ccca6631d0298789e42009e6d918e4",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0061",
     "osv_id": "ASB-A-452010556",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowState now explicitly hides/shows its own server-owned surface (mSurfaceControl) in the pending transaction whenever policy visibility changes, instead of relying only on the WindowStateAnimator hiding the client-accessible surface.",
      "confidence": "high",
      "backport_notes": [
       "Single commit with three small insertions in WindowState.java, keyed on mSurfaceControl and getPendingTransaction(). Google kept the new calls on getPendingTransaction() whether the adjacent mWinAnimator.hide uses getPendingTransaction() (15/16) or getGlobalTransaction() (14/17).",
       "Test context differs per branch only in the SurfaceAnimator animation type constant used by the existing test (TOKEN_TRANSFORM on 15/16, WINDOW_ANIMATION on 14/17)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0922d84b1c64bc21033ba3a418258d13b6e09c7d",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0067",
     "osv_id": "ASB-A-470967228",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In external/dng_sdk, the libdng_sdk build is changed so that integer overflows detected by UBSan no longer abort the process through the ubsan-minimal runtime. Instead they call a library-local runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that throws std::runtime_error, so a crafted DNG that used to cause an abort now raises a catchable C++ exception.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit. On 14/15/16 the hunk adds a new srcs block to libdng_sdk next to the cflags change. Where libdng_sdk already lists its own srcs (the 17 layout), append ubsan-replacement/ubsan_throwing_runtime.cpp to that list instead. The fix depends on libdng_sdk-defaults already setting rtti: true and cppflags [\"-fexceptions\"]."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0070",
     "osv_id": "ASB-A-438186009",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.setApplicationHidden now refuses to hide packages listed in a new framework resource array application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings), in addition to the existing policy-exempt list returned by listPolicyExemptAppsUnchecked.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit across three files (policy_exempt_apps.xml, symbols.xml, DevicePolicyManagerService.java). The resource array, the java-symbol and the service changes must land together, or the R.array reference will not compile. Forks that have edited core/res/res/values/policy_exempt_apps.xml must merge the new array."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "060fe5399350d3ebc118e8fa1b6a08013c37e98c",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0074",
     "osv_id": "ASB-A-468061774",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds hidden process-level and thread-level default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and has the Launcher (Quickstep) process register a default listener that rejects non-allowlisted image mime types and downscales images whose estimated decoded size exceeds the configured 300 MB limit, so that arbitrarily large images are no longer decoded at full size through ImageDecoder in the Launcher process.",
      "confidence": "medium",
      "backport_notes": [
       "Two-repo dependency: the Launcher3 change calls the @hide ImageDecoder.setDefaultProcessListener, so the frameworks/base ImageDecoder commit (Change-Id I694d45deb8755aab5e994c12d2627fe08b5ce78c) must land first or in the same build; the Launcher3 change (Change-Id I79d705160a12efd0f4235a4c6bfd104b5b873dfa) adds a Kotlin source file and two new resources in quickstep/res/values/config.xml.",
       "Despite the Flag: lines in the commit messages, neither diff gates the code on the aconfig flag, so no flag plumbing is needed to make the backport active."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e3cb029648f46a3350db50139b75830416ae8cdc",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "8fd9af41d56b7c53319f331f4f7774ee291e304f",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2026-0075",
     "osv_id": "ASB-A-465133716",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ContactsProvider2 now replaces SQLiteException messages containing \"json\" with a generic \"Stripped exception message\" for callers that lack READ_PERMISSION (query) or WRITE_PERMISSION (insert/update/delete). query() also now reads the cursor count inside the try block, so SQLite errors thrown lazily are caught and stripped as well.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit in ContactsProvider2.java. It relies only on the existing LogFields.Builder/LogUtils logging and the READ_PERMISSION/WRITE_PERMISSION constants. If a target branch lacks the LogFields logging wrapper around query/insert/update/delete, the helper's logBuilder parameter must be adapted."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "9308174236474c81766457891a5334b82453d6c1",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0076",
     "osv_id": "ASB-A-470115162",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a check in ResXMLTree::validateNode (libs/androidfw/ResourceTypes.cpp) that rejects a START_ELEMENT node whose ResXMLTree_attrExt::attributeSize is smaller than sizeof(ResXMLTree_attribute), returning BAD_TYPE instead of accepting the node based only on the attributeSize*attributeCount product check.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit with no dependencies; the 5-line check applies cleanly wherever ResXMLTree::validateNode still has the 'check for sensical values' branch with the attributeSize*attributeCount product check (identical hunks on 14 through 17, only line offsets differ). The test uses util::HostToDevice16/32 from androidfw/Util.h, already included by the existing tests in ResourceTypes_test.cpp."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b4f448b356b136502fc7ed36049578d57f834aaa",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0078",
     "osv_id": "ASB-A-445418705",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService.setGlobalProxy now rejects proxySpec and exclusionList strings longer than 65535 modified-UTF-8 bytes by calling PolicySizeVerifier.enforceMaxStringLength before it stores them on the ActiveAdmin.",
      "confidence": "high",
      "backport_notes": [
       "If the target branch lacks android.app.admin.PolicySizeVerifier, take the Android 14 variant. It adds the class (its unused imports of ModifiedUtf8, Bundle, Parcelable, PersistableBundle, ComponentName, ArrayDeque and Queue can be dropped) and the import in DevicePolicyManagerService.java. Otherwise only the call-site change is needed.",
       "The new test comes with the <set-global-proxy /> entry in services/tests/servicestests/res/xml/device_admin_sample.xml, which the test admin needs for the USES_POLICY_SETS_GLOBAL_PROXY check; backport them together."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "71bc705a2542eeb820116fc8ae484d8d68df60c6",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0079",
     "osv_id": "ASB-A-470966318",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In platform/external/dng_sdk the libdng_sdk build is changed so that UBSan integer-overflow checks no longer link the aborting ubsan-minimal runtime. They now call library-local handlers that throw std::runtime_error, so an overflow detected while decoding a malformed DNG becomes a catchable C++ exception instead of a process abort.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit with no dependency. The hunk must land in the libdng_sdk cc_library, not in libdng_sdk-defaults, and the module must already build with -fexceptions/rtti (set in libdng_sdk-defaults). If a downstream Android.bp lists srcs explicitly, append ubsan-replacement/ubsan_throwing_runtime.cpp to that list as was done on 17.",
       "No fork comparison section was provided, so no per-file verdict for a fork can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "1ec41401b3d40b9165367953b662ab9189885b4b",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0085",
     "osv_id": "ASB-A-414389102",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider resolve the ContentValues key case-insensitively, so that oversized values supplied under a case-variant column name (e.g. \"DaTa1\") are truncated to the simple-field maximum size instead of bypassing the check.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The DataRowHandler.java hunk is identical across 14, 15 and 17 and differs only by a one-line offset on 16. Only the test file needs adapting (presence or absence of @Test annotations, removal of the mMockContactsDatabaseHelper field if present)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "7d266fdeb2ece57424e699d3bd53aa86d41ddf30",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0087",
     "osv_id": "ASB-A-483142784",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" exactly or hosts ending in \".xyz.com\", instead of any host whose string merely ends in \"xyz.com\".",
      "confidence": "high",
      "backport_notes": [
       "Single, self-contained commit that changes one condition in DomainVerificationService.java and adds no new dependencies. Across branches only the test differs: addPackage(pkg) on 14 and 17, addPackage(pkg, null) on 15 and 16. Adapt the test call to the local DomainVerificationService.addPackage signature."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b81e2355caf161b6d7d177b94867d5630acbb5e2",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0088",
     "osv_id": "ASB-A-471127462",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller.getCallingAppLabel() now returns appInfo.loadSafeLabel(pm) instead of appInfo.loadLabel(pm), so the calling app's label shown in the CA certificate redirect dialog is sanitized by the system before display.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit, single-line change with identical file hashes (883c897 -> 1eff53f) on all four majors, so it applies cleanly wherever getCallingAppLabel() returns appInfo.loadLabel(pm). ApplicationInfo.loadSafeLabel(PackageManager) is a hidden/system API available to CertInstaller as a platform app built against the framework (general platform knowledge); forks that moved getCallingAppLabel() or format the label elsewhere should apply the same substitution at the point where the label is loaded."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 0,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "d29fcd1eff251a5fc97656ad73d3a26aa7d7e303",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2026-0091",
     "osv_id": "ASB-A-438742644",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix stops WindowManager from sending a launching app's IApplicationThread token out of system_server. TransitionController now strips it from the RemoteTransition placed in the TransitionRequestInfo and stores the delegate process on the server-side Transition. IActivityTaskManager.setRunningRemoteTransitionDelegate now takes a transition token instead of a caller-supplied IApplicationThread.",
      "confidence": "medium",
      "backport_notes": [
       "The AIDL signature change in IActivityTaskManager and the shell call sites must ship together with the server changes, in one atomic change. The HierarchyOp field used to carry the binder and the op number differ per branch (14/17: mReparent with value 18; 15: mInsetsFrameOwner with 19; 16: mInsetsFrameOwner with 27), so match the target branch's HierarchyOp constants instead of copying the numbers.",
       "On 16, the new case in both toString and WindowOrganizerController directly follows HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS, and the diff shows no break between them, so that case falls through into the new code. Check fall-through when placing the case in other trees."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-opus-5-5",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f7488ad342c2821820e0664722d2e34ae4041aa9",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0093",
     "osv_id": "ASB-A-473812391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces ApplicationInfo.loadLabel with loadSafeLabel (DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where Settings renders the requesting app's name in the credential-management request UI (RequestManageCredentials and CredentialManagementAppAdapter).",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit: apply all three substitutions (one in RequestManageCredentials, two in CredentialManagementAppAdapter). The adapter hunks apply unchanged on every major. Only the import context of RequestManageCredentials differs between 14/17 and 15/16.",
       "loadSafeLabel(PackageManager, float, int) and PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX are system APIs, not part of the public SDK. Settings can use them because it builds against the platform (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "7f9aa5805598404fd9fbcae89f02bd46f461745e",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0095",
     "osv_id": "ASB-A-484861632",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Widens the local allocation-size variable in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the computed buffer size is no longer truncated before osi_malloc, preventing a heap buffer overflow in the following memcpy.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained one-line change with no dependencies; applies to any branch where l2c_fcr_clone_buf declares 'uint16_t buf_size' (only surrounding formatting of the function signature differs between 14/15 and 16)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "f82bbb37a852bc6a440fbb6b6c8b8906809dfba8",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0098",
     "osv_id": "ASB-A-397217317",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DocumentsUI Shared.getCallingPackageName now only honours the Intent.EXTRA_PACKAGE_NAME calling-package override when the real caller's UID shares the platform signature (checkSignatures against Process.SYSTEM_UID), instead of accepting any app with FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching only Shared.java with no new dependencies. Google applied it with the same pre-image blob (9f328be69) on 14/15/17 and with an 11-line offset on 16."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "b05353f638a76bf2b9c44572a4f663666142f948",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    },
    {
     "cve": "CVE-2026-0099",
     "osv_id": "ASB-A-385917501",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds onNullBinding overrides (and, for the per-transaction connection, an onBindingDied override) to the two ServiceConnection objects in HostEmulationManager. The NFC service now unbinds from a HostApduService whose onBind returned null or whose binding died, instead of keeping the binding.",
      "confidence": "medium",
      "backport_notes": [
       "The material shows one commit per branch, but the 14 and 15 variants differ. 14 routes both callbacks through the existing unbind*Locked helpers under mLock. 15 calls mContext.unbindService(this) directly for null binding and calls unbindServiceIfNeededLocked() without the lock for binding death. On 16+ the code lives in packages/modules/Nfc under NfcNci/, and the fix also touches event.proto (Nfc is a mainline module there (general platform knowledge)).",
       "No fork comparison section was provided, so no per-file fork verdict can be reported."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "7fb6795b51c3f6676f834aacde44ada257c32199",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Nfc"
     ]
    },
    {
     "cve": "CVE-2026-0100",
     "osv_id": "ASB-A-484973621",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp) so that a RES_TABLE_PACKAGE_TYPE chunk whose 32-bit package id is above 255 is rejected instead of being stored as the package id.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained commit. The code hunk is identical on all four majors and targets the line 'loaded_package->package_id_ = dtohl(header->id);' in LoadedPackage::Load. The adjacent typeIdOffset check already uses std::numeric_limits, and the fix adds no include."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "86dd9d421620ff9c78771ea6737fa02b106b9e61",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28577",
     "osv_id": "ASB-A-389950114",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the supplied WindowToken already contains a window, enforcing one toast window per toast token.",
      "confidence": "high",
      "backport_notes": [
       "The functional change is a self-contained 6-line insertion in addWindow after the canAddToastWindowForUid check. It needs only WindowToken.isEmpty() and the existing addToastWindowRequiresToken local. If you keep the ProtoLog line active on a branch that uses the pre-generated services.core.protolog.json, add the matching entry as the Android 14 commit does, or comment the line out as the 15/16 commits do.",
       "The test depends on a WindowTestsBase.createTestSession(atms, pid, uid) helper. Android 14 lacked it, so its commit adds a minimal version plus a pid/uid Session constructor. Branches without that helper need the same test-only additions."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9af61140db7920b4268a987fd50c2f9507151707",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28578",
     "osv_id": "ASB-A-475228205",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a per-package-name maximum length check on PackagePolicy objects passed to three DevicePolicyManagerService setters (two managed-profile contacts/caller-id access policy setters and the credential manager policy setter), so overlong package names are rejected before caller authorization and before the policy is applied.",
      "confidence": "high",
      "backport_notes": [
       "On branches without PolicySizeVerifier.enforceMaxPackageNameLength, use the Android 14 form (Preconditions.checkArgument against MAX_PACKAGE_NAME_LENGTH); confirm MAX_PACKAGE_NAME_LENGTH is already available in DevicePolicyManagerService on the target branch, as the 14 diff adds no import or definition.",
       "Insert the check after the mHasFeature early return and before getCallerIdentity in each of the three setters so behavior matches upstream ordering."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-15.0.0_r16",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e8ff8af05575238ef3a960b0ad0320d2ebdfbbc6",
       "tag": "android-security-15.0.0_r16",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    }
   ],
   "tags": [
    "android-security-15.0.0_r16"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "477935679"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c87384f4be8208d8e785bec875b421ec737a33f4",
       "subject": "DO NOT MERGE: Enforce suspend restrictions in startOp and noteOp paths",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": [
        "cve_fix",
        "restrict_automerge"
       ]
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "396653764"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "51da2317c66a051a4cb93886f0341acae92a006f",
       "subject": "Do not count closing activity as visible process",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "463674813"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "424df716e8bc9d6775885fee25763b30486271a8",
       "subject": "On face auth success, verify the face auth'd user is the current user",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "401188957"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1cbbf73a3c716e96090336b58294e43ec2634280",
       "subject": "Checking userId in com.android.server.pm.ComputerEngine#resolveContentProvider",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "471173239"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/KeyChain",
       "sha": "cbb6b7d9d6f819cfd3e8587999bf818207406266",
       "subject": "Sanitize app label in KeyChainActivity to prevent UI injection.",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "bulletin_history": [
      {
       "bulletin": "2026-06",
       "cve": "CVE-2026-0094",
       "severity": "High",
       "versions": [
        "14",
        "15",
        "16",
        "16-qpr2"
       ],
       "status": "removed",
       "removed_seen": "2026-10-13",
       "removed_in_version": "1.4"
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "368376056",
      "489303684"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "da5a77b7fc72a8f2408ef99c842f68b34ed1dcd6",
       "subject": "Load the Secure NFC setting when the current user is changed upon query",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "368376056",
      "475315815",
      "475447365"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "a83ac8aaf676babbe0e1f54a0560e3aa6035d959",
       "subject": "NFC Security Vulnerability in Multi-User Mode",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/apps/Nfc",
       "sha": "190b04f8e98b4bf4fbda7f561270df20ca522a13",
       "subject": "nfc: Modify Secure NFC restoration to use the corresponding userId",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "368376056"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "9fb2a90f5db29c9523c2f5683c5c651776145182",
       "subject": "Allow multi-user to control secure nfc",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "315320090",
      "418085016"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/Permission",
       "sha": "d8ef171266fefe15830893137490f0cac5dc828b",
       "subject": "Revert^2 \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/modules/Permission",
       "sha": "f164b690f6cf01df9b92a67dae990a325a5f67b0",
       "subject": "Revert \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    },
    {
     "bug_ids": [
      "462214379"
     ],
     "commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "74cfd539ab6b567e6827fe3a918dd9853906923d",
       "subject": "Disallow shell to change CarrierRestrictionRules",
       "tag": "android-security-15.0.0_r16",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-15.0.0_r16"
     ],
     "tag": "android-security-15.0.0_r16"
    }
   ],
   "coverage": {
    "n_ingested_tags": 18,
    "first_ingested_tag": "android-security-15.0.0_r1",
    "latest_ingested_tag": "android-security-15.0.0_r18",
    "window_base_tag": "android-15.0.0_r1",
    "window_start": "2024-09-03",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2025-64505": {
     "cve": "CVE-2025-64505",
     "summary": "libpng's png_set_quantize now allocates and identity-initialises the quantize_index array at PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so out-of-range palette indices used by png_do_quantize no longer access memory past the heap buffer; a companion commit frees the previous quantize_index before reallocation and makes quantize_sort a local variable.",
     "component": "platform/external/libpng, read transformations (pngrtran.c, palette quantization)",
     "weakness": "Heap buffer overflow (out-of-bounds read) from an undersized allocation (no bounds check on the palette index in the pixel loop), plus a memory leak on repeated png_set_quantize calls",
     "root_cause": "In pngrtran.c:png_set_quantize, when full_quantize == 0, png_ptr->quantize_index was allocated with exactly num_palette bytes and initialised only for i < num_palette. Per the commit message, png_do_quantize's pixel processing loop indexes this array with palette indices taken from the image data. In a malformed PNG these indices can be >= num_palette, so the lookup went beyond the allocation. The commit message says palette[], trans_alpha[] and riffled_palette[] were already oversized to PNG_MAX_PALETTE_LENGTH for this reason; quantize_index was not. Separately, each call to png_set_quantize overwrote png_ptr->quantize_index without freeing the earlier allocation, which leaked memory when the function was called more than once per png_struct.",
     "fix_analysis": [
      "Commit 3547b8454a7e (first, pngrtran.c:png_set_quantize): adds png_free(png_ptr, png_ptr->quantize_index) before the png_malloc so repeated calls do not leak. It also turns quantize_sort from a png_struct member into a function-local png_bytep that is malloc'd, used for the histogram bubble sort and palette reordering, and freed at the end of the histogram branch, and it moves int j to block scope. The pngstruct.h member removal was reverted in this backport to avoid an ABI change, so png_ptr->quantize_sort still exists but this function no longer uses it.",
      "Commit 299ab85302d7 (second, pngrtran.c:png_set_quantize): changes the quantize_index allocation size from (png_alloc_size_t)num_palette to PNG_MAX_PALETTE_LENGTH and extends the identity-initialisation loop to i < PNG_MAX_PALETTE_LENGTH. Every 8-bit index therefore has an in-bounds entry. No bounds check is added to png_do_quantize; the fix relies on the oversized array."
     ],
     "completeness": [
      "Covers the quantize_index allocation in the full_quantize == 0 path of png_set_quantize, the only allocation of that array shown in the material. All 256 possible byte indices now fall inside the buffer.",
      "png_do_quantize itself is unchanged. Its safety depends on quantize_index always being 256 bytes.",
      "The local quantize_sort array is still allocated at num_palette bytes. In the code shown it is only indexed by i, j < num_palette inside png_set_quantize, not by image data."
     ],
     "behavior_changes": [
      "quantize_index is always a 256-byte allocation regardless of num_palette.",
      "Pixels with a palette index >= num_palette are now read as identity-mapped values instead of reading out of bounds. No error is raised.",
      "Calling png_set_quantize repeatedly on one png_struct no longer leaks the previous quantize_index.",
      "The png_struct layout is unchanged in the backport (the quantize_sort member is kept for ABI)."
     ],
     "preconditions": [
      "The application must call png_set_quantize on the read png_struct with full_quantize == 0. This sets PNG_QUANTIZE and allocates quantize_index.",
      "The input PNG must be a paletted image whose pixel data contains indices >= num_palette (malformed relative to its palette).",
      "Per index search (semantic, possibly incomplete), the results returned (stb_image, tinyexr, androidfw PngCrunch.cpp write path) do not show an Android platform caller of png_set_quantize."
     ],
     "per_major": {
      "14": "adapted fix: commit 411d9be12a3e in android-security-14.0.0_r28 makes the same 256-byte allocation and loop change. The replaced allocation line is a different expression ((png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte)))). The png_free leak guard is already present in its context. 7/8 patched-region lines present at android-security-14.0.0_r29.",
      "15": "same fix: two commits in android-security-15.0.0_r16, first the leak/refactor commit 3547b8454a7e, then the overflow fix 299ab85302d7 (cherry-picks of 9f83a580a609 and 31ed93cef639). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 93147074acc1 in android-security-16.0.0_r7, identical diff to Android 15 (same blob ids 3749ebfa6..5fd5b7330). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "adapted fix: commit dc721fee982c in android-17.0.0_r1 (base), identical to the Android 14 variant (blob ids 831d98d08..03ffef132) with the different allocation expression. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply in order: 3547b8454a7e (png_free of quantize_index, quantize_sort made local), then 299ab85302d7 (PNG_MAX_PALETTE_LENGTH allocation). The second diff's context lines depend on the first. If the target already has the png_free guard (as Android 14/17 do), only the allocation-size change is needed, and the png_malloc argument line must match the local variant.",
      "Keep the png_struct quantize_sort member, because the backport deliberately avoids the pngstruct.h ABI change."
     ],
     "confidence": "high",
     "unknowns": [
      "The png_do_quantize pixel loop that performs the out-of-bounds access is not in the material; the access is inferred from the commit message and the allocation size.",
      "Whether the later num_palette > maximum_colors remap code (histogram == NULL path, full_quantize cube path) writes only quantize_index entries below num_palette, and whether it has other unfreed allocations, is not shown.",
      "Why Android 14 and 17 report 7/8 patched-region lines (possibly the different pre-existing allocation line) is not shown.",
      "Which Android platform components call png_set_quantize with full_quantize == 0 on untrusted input is not shown.",
      "Why the bulletin rates this as Critical/DoS is not explained by the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-64720": {
     "cve": "CVE-2025-64720",
     "summary": "In libpng pngrtran.c png_init_read_transformations, the palette compositing loop now checks PNG_FLAG_OPTIMIZE_ALPHA. When the flag is set, it only premultiplies each palette RGB component by its tRNS alpha (using the gamma_to_1/gamma_from_1 tables) instead of compositing it against the background color, so the values stay within the premultiplied-alpha invariant that png_image_read_composite relies on later.",
     "component": "platform/external/libpng, read transformations (pngrtran.c)",
     "weakness": "Incorrect value computation leading to an out-of-bounds array index (palette entries break the component <= alpha premultiplied invariant and are then used as lookup-table indices in png_image_read_composite)",
     "root_cause": "In png_init_read_transformations (pngrtran.c), the palette loop composited every palette entry with 0 < trans_alpha[i] < 0xff against the background color (png_composite with back_1.red/green/blue), with no condition on the alpha mode. It did this even when PNG_FLAG_OPTIMIZE_ALPHA was set. In that mode the palette is expected to hold pure premultiplied RGB (component <= alpha), and the background is applied later in png_image_read_composite. Compositing with the background broke this invariant. According to the commit message, png_image_read_composite then used the out-of-range premultiplied values as indices into the png_sRGB_base[] and png_sRGB_delta[] tables, causing out-of-bounds access. The material does not show the outer conditions that guard this block.",
     "fix_analysis": [
      "pngrtran.c:png_init_read_transformations, inner else branch for trans_alpha[i] not 0 and not 0xff: adds a test on (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA).",
      "When the flag is set, for each of red, green and blue: component = gamma_to_1[palette[i].c]; component = (component * trans_alpha[i] + 128) / 255 (computed as png_uint_32); palette[i].c = gamma_from_1[component]. No background term is used, so the linear-space result is at most alpha. The commit message says this preserves the invariant expected downstream.",
      "When the flag is not set, the previous png_composite-based background compositing (v = gamma_to_1[...]; png_composite(w, v, trans_alpha[i], back_1.c); palette[i].c = gamma_from_1[w]) is kept unchanged.",
      "The diff does not modify the trans_alpha[i] == 0 case (palette[i] = back) or the opaque case."
     ],
     "completeness": [
      "The change only touches palette entries with partial alpha (0 < alpha < 0xff) in this loop of png_init_read_transformations. The context at line 1696 shows that the alpha == 0 branch still assigns palette[i] = back, whether or not PNG_FLAG_OPTIMIZE_ALPHA is set.",
      "The diff context ends at an outer 'else' (a sibling path that is not shown). The material does not show that path, so it cannot tell whether that path needed or received an equivalent change.",
      "No bounds check was added where the values are consumed (the png_image_read_composite lookups into png_sRGB_base and png_sRGB_delta). The fix relies on producing in-range values at the source rather than clamping at the table access.",
      "Rounding constant: the commit message says '+ 127' but the code uses '+ 128'. The code is what was applied on all branches."
     ],
     "behavior_changes": [
      "For paletted images decoded with PNG_FLAG_OPTIMIZE_ALPHA set, partially transparent palette entries are now stored as premultiplied RGB with no background color mixed in. The commit message says the background is applied later in png_image_read_composite, so output colors for such images may differ from before.",
      "No new error paths or API changes. Behavior when the optimized alpha mode is off is unchanged."
     ],
     "preconditions": [
      "Input is a PNG with a palette and a tRNS chunk that has at least one alpha value strictly between 0 and 255.",
      "The reader has PNG_FLAG_OPTIMIZE_ALPHA set (the optimized/premultiplied alpha mode that the commit message calls PNG_ALPHA_OPTIMIZED), and the palette compositing block of png_init_read_transformations is reached. The full set of conditions guarding that block is not shown.",
      "Per the commit message, the overflow happens in png_image_read_composite (simplified read API). Per index search, libpng is used by frameworks/base/libs/androidfw/Png.cpp (readPng, which uses png_set_palette_to_rgb and png_set_tRNS_to_alpha) and bootable/recovery/minui/resources.cpp (PngHandler). The material does not show whether those callers enable the optimized alpha mode."
     ],
     "per_major": {
      "14": "same fix: platform/external/libpng ba5981bebfba in android-security-14.0.0_r28, identical hunk at pngrtran.c line 1701 (blob 03ffef132..38d1306f1). At android-security-14.0.0_r29, 6/8 lines of the patched region are present.",
      "15": "same fix: platform/external/libpng 2a900c24d056 in android-security-15.0.0_r16, hunk at pngrtran.c line 1697 (blob 5fd5b7330..6a5a401a0). The code context at android-security-15.0.0_r17 shows the PNG_FLAG_OPTIMIZE_ALPHA branch in place (6/8 lines of the patched region present).",
      "16": "same fix: platform/external/libpng 2113a245b832 in android-security-16.0.0_r7, identical to the Android 15 hunk (same blob hashes 5fd5b7330..6a5a401a0). At android-security-16.0.0_r8, 6/8 lines of the patched region are present.",
      "17": "same fix: platform/external/libpng 20c02c533b25, included in the android-17.0.0_r1 base and identical to the Android 14 hunk (blob 03ffef132..38d1306f1). At android-security-17.0.0_r1, 6/8 lines of the patched region are present."
     },
     "backport_notes": [
      "One self-contained hunk in pngrtran.c with no dependencies. It applied to two slightly different libpng bases (hunk at line 1701 on 14/17, line 1697 on 15/16) without adaptation, so it should apply cleanly to any tree where the png_composite block for partial-alpha palette entries matches. The code uses + 128 rounding even though the commit message says + 127; follow the code."
     ],
     "confidence": "high",
     "unknowns": [
      "The consumer side (png_image_read_composite and its png_sRGB_base/png_sRGB_delta lookups) is not shown. The out-of-bounds mechanism comes from the commit message only.",
      "The outer conditions guarding the palette compositing block (for example gamma or background transform flags) are not shown.",
      "The sibling path after the outer 'else' at the end of the diff context is not visible, so whether it behaves the same way is unknown.",
      "Why only 6/8 lines of the patched region match at the latest security tags is not explained in the material.",
      "The material does not show which Android system components, if any, decode untrusted PNGs with the optimized alpha mode. The bulletin rates this as a Critical DoS but does not identify the affected process."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-65018": {
     "cve": "CVE-2025-65018",
     "summary": "Two-commit fix in external/libpng pngread.c. The first commit rejects IHDR/output bit-depth mismatches in png_image_finish_read. The second reverts that check and instead sends interlaced 16-to-8 bit reads in png_image_read_direct through an intermediate row buffer (new png_image_read_direct_scaled), so that png_combine_row no longer writes IHDR-depth rows into the caller's 8-bit buffer.",
     "component": "platform/external/libpng, simplified read API (pngread.c: png_image_finish_read / png_image_read_direct)",
     "weakness": "Heap buffer overflow (out-of-bounds write) caused by a size mismatch between the caller's output buffer and the row size used during interlaced row combination",
     "root_cause": "In the simplified API the caller allocates the output buffer with PNG_IMAGE_SIZE(image). That size is computed from the requested output format, which is 8-bit when PNG_FORMAT_FLAG_LINEAR is not set. When the IHDR bit depth is 16 and 8-bit output is requested, png_image_read_direct applies png_set_scale_16. According to the commit messages, png_combine_row then writes into the user buffer using the IHDR bit depth before the 16-to-8 transformation has reduced the row. The writes therefore go beyond the buffer allocated via PNG_IMAGE_SIZE. The first commit's example is 32x32 16-bit RGB to 8-bit RGBA: 6144 bytes written against a 4096-byte buffer, a 2048-byte overflow. The second commit's message says the problem affects only interlaced PNGs and that the non-interlaced fast path is safe.",
     "fix_analysis": [
      "Commit 6a5cceb169b3 (first fix), pngread.c:png_image_finish_read: before the png_image_read_control display is memset, it reads image->opaque->png_ptr->bit_depth and tests (image->format & PNG_FORMAT_FLAG_LINEAR). It returns png_image_error(...) in two cases: bit_depth == 16 with non-linear output ('16-bit PNG must use 16-bit output format'), and bit_depth < 16 with linear output ('8-bit PNG must not use 16-bit output format'). This blocks every bit-depth conversion through the simplified API, interlaced or not, in both directions.",
      "Commit 9f93c45faadd (rearchitecture), pngread.c:png_image_finish_read: removes the 14-line rejection block added by the first commit, so bit-depth conversions are accepted again.",
      "Commit 9f93c45faadd, pngread.c:png_image_read_direct: adds int do_local_scale = 0. In the 8-bit output branch, after png_set_scale_16(png_ptr), it sets do_local_scale = 1 when png_ptr->interlaced != 0. It adds a new 'else if (do_local_scale != 0)' branch, after a preceding branch that returns result and before the fast-path else. The new branch allocates row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)), which the code comment describes as sized for the transformed 8-bit output. It stores the row in display->local_row, runs png_image_read_direct_scaled under png_safe_execute, clears local_row, frees the row and returns the result. The commit message says this follows the do_local_compose pattern.",
      "Commit 9f93c45faadd, pngread.c: new static png_image_read_direct_scaled(png_voidp argument). It sets passes from png_ptr->interlaced: 1 for PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7_PASSES for ADAM7, png_error otherwise. For each pass and each of image->height rows it calls png_read_row(png_ptr, local_row, NULL), then memcpy(output_row, local_row, row_bytes) with row_bytes = display->row_bytes, and advances output_row by row_bytes. Row reads therefore target the libpng-owned local_row, and only row_bytes bytes per row are copied into the user buffer."
     ],
     "completeness": [
      "Final state (after the second commit): only the 8-bit output branch of png_image_read_direct (png_set_scale_16) gets the intermediate buffer, and only for interlaced images. The non-interlaced 16-to-8 fast path is intentionally unchanged; the commit message states it is already safe.",
      "The png_set_expand_16 branch (8-to-16 output) gets no local buffer. The first commit rejected that direction too; the second commit does not handle it, which matches its statement that only interlaced 16-to-8 reads are affected.",
      "The second commit does not touch the colormapped path (png_image_read_colormap / png_image_read_colormapped). The first commit's check sat in png_image_finish_read ahead of both paths, so the final fix is narrower in scope than the interim one. The material does not show whether the colormapped path has an analogous issue.",
      "The do_local_scale branch is an else-if after a preceding branch in png_image_read_direct, so that earlier branch wins when its condition holds. The material does not show how that branch sizes its buffers for interlaced 16-bit input."
     ],
     "behavior_changes": [
      "Interim state (first commit only): png_image_finish_read fails with png_image_error for any 16-bit PNG read with a non-linear format and for any PNG below 16 bits read with a linear format. This is a functional regression for simplified-API callers that rely on bit-depth conversion. The second commit calls it 'unnecessarily limiting' and reverts it.",
      "Final state: interlaced 16-bit PNGs read to 8-bit output now take an extra heap row allocation (png_get_rowbytes size) plus a per-row memcpy for every pass. The commit message calls this the slow but necessary path. Non-interlaced reads are unchanged.",
      "png_image_read_direct_scaled adds an 'unknown interlace type' png_error path, matching the one in png_image_read_composite."
     ],
     "preconditions": [
      "A caller uses libpng's simplified read API (png_image_begin_read_* followed by png_image_finish_read), requests an output format without PNG_FORMAT_FLAG_LINEAR (8-bit components) and sizes the buffer with PNG_IMAGE_SIZE(image).",
      "The input PNG has IHDR bit depth 16 and is interlaced (ADAM7), according to the second commit's analysis.",
      "The read goes through png_image_read_direct (non-colormapped output format).",
      "Per index search, frameworks/base/libs/androidfw/Png.cpp::readPng uses the low-level API (png_read_info, png_set_strip_16) rather than png_image_finish_read, so it is not on the patched path. The gfxstream stb_image hits are a separate PNG decoder. The material does not show which Android components call the simplified API."
     ],
     "per_major": {
      "14": "Same final fix: the rearchitecture commit 5e46c0292d5c is in android-security-14.0.0_r28. Its removal hunk shows the earlier rejection check was already in the tree. 8/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix: both commits (6a5cceb169b3, then 9f93c45faadd) are in android-security-15.0.0_r16. Code at android-security-15.0.0_r17 shows the png_image_read_direct_scaled row loop, and png_image_finish_read no longer has the rejection block.",
      "16": "Partially shown: the material shows only the first commit (960064fdb5f4, the rejection check in png_image_finish_read) in android-security-16.0.0_r7, with 4/4 lines present at android-security-16.0.0_r8. Whether the rearchitecture commit also landed on 16 is not shown.",
      "17": "Same final fix: the rearchitecture commit 0e9750be87cb is in base android-17.0.0_r1, and its removal hunk shows the first commit was present too. 8/8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply the two commits in order (6a5cceb169b3, then 9f93c45faadd) or squash them. The second commit's png_image_finish_read hunk removes lines added by the first and will not apply to a tree that lacks them; in that case drop that hunk and take only the png_image_read_direct / png_image_read_direct_scaled additions. Shipping only the first commit stops the overflow but rejects all bit-depth conversions through the simplified API.",
      "The new branch uses display->local_row, display->first_row, display->row_bytes and png_safe_execute. Confirm these exist in the target libpng, and check that png_get_rowbytes returns the transformed row size at that point, before porting. No fork comparison was provided."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether the rearchitecture commit (which removes the restrictive check) landed on Android 16; the material shows only the first commit there.",
      "Which Android components call the libpng simplified API (png_image_finish_read), and therefore what privilege boundary lies behind the Critical EoP rating.",
      "The exact code in png_combine_row and the interlace path that performs the IHDR-depth write; only the commit messages describe it.",
      "The commit says png_combine_row writes using the IHDR bit depth, but local_row is sized with png_get_rowbytes, which the code comment says is the transformed 8-bit size. The material does not show why writes into local_row stay in bounds.",
      "Whether the branch that comes before do_local_scale in png_image_read_direct (the do_local_compose path, per the commit message) handles interlaced 16-to-8 input safely.",
      "png_image_read_direct_scaled reuses one local_row for every row and copies the full row_bytes into the user buffer after each png_read_row. The material cannot show whether pixels from earlier interlace passes survive correctly in each output row; the commit says regression tests pass."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0039": {
     "cve": "CVE-2026-0039",
     "summary": "In platform/external/dng_sdk, the libdng_sdk build is changed so that UBSan integer-overflow detections no longer go to the aborting ubsan-minimal runtime. They go to a library-private runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that throws std::runtime_error, which turns a hard abort into a C++ exception.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image library; the commit's test is ImageDecoderTest#testInterestingDngs)",
     "weakness": "Integer overflow (signed/unsigned) in libdng_sdk arithmetic. UBSan detects it and the minimal runtime aborts, which is an unrecoverable crash (denial of service). The fix changes how the failure is handled, not the arithmetic.",
     "root_cause": "libdng_sdk-defaults (Android.bp lines 144-149) builds the library with sanitize.misc_undefined = [unsigned-integer-overflow, signed-integer-overflow]. Per the commit title and message, a detected overflow went to the ubsan-minimal runtime, which does a hard abort. So any integer overflow detected inside libdng_sdk ended the hosting process, even though the library is built with -fexceptions and rtti (lines 141-142) and could recover through exception handling. The commit references seven bug ids. The individual overflow sites and inputs behind them are not shown in the material.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize: { never: true }. Per the in-file comment, this disables the ubsan_minimal runtime for this module so the library can inject its own handlers.",
      "Android.bp :: cc_library libdng_sdk: re-enables the two checks by hand through cflags \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" and adds \"-fno-sanitize-link-runtime\" so no sanitizer runtime is linked. The existing \"-DqDNGValidate=0\" is kept.",
      "Android.bp :: cc_library libdng_sdk: adds srcs ubsan-replacement/ubsan_throwing_runtime.cpp so the module supplies its own handlers.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines five extern \"C\" hidden-visibility handlers: __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one does throw std::runtime_error(\"ubsan: <kind>\"). A detected overflow therefore unwinds as a C++ exception instead of aborting.",
      "OWNERS: metadata-only change (bug component, include of the frameworks/base graphics OWNERS file); no functional effect."
     ],
     "completeness": [
      "Applies to the instrumented signed/unsigned overflow checks across the libdng_sdk module. No per-site arithmetic fix is included, so the overflows still happen; they are now reported through an exception instead of an abort.",
      "Only the five overflow handler entry points are provided. The manual -fsanitize list matches the two checks previously enabled in the defaults, so no other UBSan check classes are enabled or handled.",
      "sanitize.never is set only on the libdng_sdk module. libdng_sdk-defaults still carries misc_undefined, so any other module built from those defaults (none shown in the material) would keep the previous behavior.",
      "The fix relies on some caller frame catching std::runtime_error. The material does not show where that catch happens. If nothing catches it, the process would still terminate.",
      "The host_linux target still links static_libs libcompiler_rt-extras (line 196). The material does not show whether this interacts with -fno-sanitize-link-runtime on host builds."
     ],
     "behavior_changes": [
      "When an integer overflow is detected inside libdng_sdk, a std::runtime_error is thrown instead of the process aborting. The message is one of \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\".",
      "Inputs that previously triggered the abort can now end as an error reported to the caller, if a frame catches the exception. Which error is reported depends on that catching frame, which is not shown.",
      "Build change: libdng_sdk no longer links a sanitizer runtime. Sanitizer instrumentation remains for the two overflow checks."
     ],
     "preconditions": [
      "Input processed by libdng_sdk that drives its arithmetic into signed or unsigned integer overflow. The test name ImageDecoderTest#testInterestingDngs points to DNG decoding through the platform image decoder.",
      "Before the fix, libdng_sdk had to be built with the misc_undefined overflow sanitizers (as in libdng_sdk-defaults) for the abort to occur.",
      "Index search results (android_hardware_camera2_DngCreator.cpp, frameworks/av img_utils DngUtils.cpp) are DNG writing paths and do not visibly call libdng_sdk, so their relevance is not shown."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. Android.bp and ubsan_throwing_runtime.cpp are identical to the Android 15 commit; only the OWNERS hunk differs, because the pre-existing contents differ. At android-security-14.0.0_r29, 3/5 lines of the patched region are present.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. The commit is a cherry-pick of two internal commits, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902. At android-security-15.0.0_r17, 3/4 lines of the patched region are present; the code excerpt shows the new sanitize/cflags/srcs block and the runtime file.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the runtime file are identical; the OWNERS base differs. At android-security-16.0.0_r8, 3/5 lines of the patched region are present.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab (title tagged RESTRICT AUTOMERGE) is in the android-17.0.0_r1 base. Android.bp and the runtime file are identical. At android-security-17.0.0_r1, 3/5 lines of the patched region are present."
     },
     "backport_notes": [
      "The Android.bp hunk has the same blob ids (1978b39..3d95a7b) in 14 through 17. The OWNERS hunk varies by branch and has no functional effect.",
      "The approach depends on libdng_sdk being built with -fexceptions and rtti (present in libdng_sdk-defaults), on the build honoring the combination of sanitize.never, manual -fsanitize cflags and -fno-sanitize-link-runtime, and on a caller catching the exception. Forks with a different libdng_sdk module layout should verify all of these."
     ],
     "confidence": "medium",
     "unknowns": [
      "The specific integer-overflow sites in dng_sdk behind the seven referenced bugs, and the inputs that reach them.",
      "Where the thrown std::runtime_error is caught (inside dng_sdk or in a consumer), and what error the consumer reports.",
      "Which processes host libdng_sdk on device (the test suggests the ImageDecoder path, but the consumer code is not in the material).",
      "Whether any other module uses libdng_sdk-defaults and therefore keeps the previous aborting behavior.",
      "Whether the parameterless handler declarations match the calling convention the compiler emits for these checks."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0040": {
     "cve": "CVE-2026-0040",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ exception (new ubsan-replacement/ubsan_throwing_runtime.cpp) instead of being handled by the ubsan minimal runtime with a hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and UBSan runtime handlers",
     "weakness": "Integer overflow in libdng_sdk arithmetic surfaced as a sanitizer abort (process termination, DoS); the diff does not identify the specific arithmetic site(s).",
     "root_cause": "libdng_sdk inherits sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] from libdng_sdk-defaults (Android.bp lines 144-149 in context). With that configuration Soong uses the ubsan minimal runtime, whose handlers abort the process on a detected overflow (general platform knowledge). The commit message says C++ exceptions are enabled in this library (rtti: true, cppflags -fexceptions in the defaults) and that they are a much better answer to ubsan errors than a hard abort. So DNG input that drove a signed or unsigned integer overflow in libdng_sdk arithmetic terminated the hosting process instead of producing a recoverable error. The commit references seven bugs, which suggests multiple overflow sites; none of them appear in the material.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true }. Per the in-diff comment, this disables the ubsan_minimal runtime for this module.",
      "Android.bp, cc_library libdng_sdk: replaces cflags [\"-DqDNGValidate=0\"] with [\"-DqDNGValidate=0\", \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\", \"-fno-sanitize-link-runtime\"]. The compiler still instruments signed and unsigned overflow, but no sanitizer runtime library is linked.",
      "Android.bp, cc_library libdng_sdk: adds srcs [\"ubsan-replacement/ubsan_throwing_runtime.cpp\"] so the module itself supplies the handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\"), so a detected overflow unwinds as a C++ exception that can be caught instead of aborting.",
      "OWNERS: metadata only (bug component, include of frameworks/base graphics OWNERS, LAST_RESORT_SUGGESTION owners). No functional effect."
     ],
     "completeness": [
      "The fix does not correct any specific overflowing computation in dng_sdk. It only changes what happens when the compiler-inserted overflow check fires: an exception instead of an abort.",
      "The replacement runtime defines five handlers (add, sub, mul, negate, divrem). Only the signed and unsigned integer overflow checks are enabled in the module cflags.",
      "Only the libdng_sdk cc_library is changed. libdng_sdk-defaults still carries sanitize.misc_undefined, so any other module built from those defaults (none is shown) would keep the previous behavior.",
      "The material does not show whether every libdng_sdk entry point reachable from untrusted input catches std::runtime_error. An uncaught exception would still terminate the process."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the ubsan minimal runtime (sanitize.never plus -fno-sanitize-link-runtime). The same two overflow checks stay enabled.",
      "Callers that catch these exceptions will see a decode failure for inputs that previously crashed the process. The commit cites ImageDecoderTest#testInterestingDngs as its test."
     ],
     "preconditions": [
      "A process decodes attacker-supplied DNG data through libdng_sdk. The cited test, ImageDecoderTest#testInterestingDngs, points at the platform image decoding path.",
      "The input drives a signed or unsigned integer overflow in libdng_sdk arithmetic that the UBSan instrumentation detects. The specific sites are not shown.",
      "The DngCreator and img_utils code found by the index search writes DNGs via TiffWriter. The snippets do not show it calling libdng_sdk (per index search)."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp changes are identical; the OWNERS hunk differs in the pre-existing owner lines. At android-security-14.0.0_r29, 3 of 5 lines of the patched region are present.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (cherry-pick, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). At android-security-15.0.0_r17, 3 of 4 lines of the patched region are present.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Functional changes are identical; the OWNERS hunk is adapted to the branch's prior content. At android-security-16.0.0_r8, 3 of 5 lines of the patched region are present.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1, marked RESTRICT AUTOMERGE. Functional changes are identical. At android-security-17.0.0_r1, 3 of 5 lines of the patched region are present."
     },
     "backport_notes": [
      "This is a single build-config commit. It applies where the libdng_sdk cc_library has cflags [\"-DqDNGValidate=0\"] after defaults [\"libdng_sdk-defaults\"], and it relies on the defaults already setting rtti: true and cppflags -fexceptions (lines 141-142 in the Android 15 context). The OWNERS hunk is branch-specific and can be dropped.",
      "The approach depends on Soong honoring sanitize.never together with hand-written -fsanitize cflags and -fno-sanitize-link-runtime (per the in-diff comment). After a backport, check that libdng_sdk.so has no undefined __ubsan_handle_* symbols."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic site(s) in dng_sdk overflow for the seven referenced bugs; the material contains no source change in the parser.",
      "Whether all callers of libdng_sdk reachable from untrusted input catch std::runtime_error (not only dng_exception), so that the thrown exception does not itself terminate the process.",
      "Whether the compiler emits exactly the five defined handler symbols under these flags (for example, recoverable versus _abort variants).",
      "Whether other modules in Android.bp use libdng_sdk-defaults and so keep the minimal runtime.",
      "Why the per-major facts report only part of the patched region present in each latest security tag, even though the Android 15 code context shows the full change."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0041": {
     "cve": "CVE-2026-0041",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error, through a custom in-library runtime in ubsan-replacement/ubsan_throwing_runtime.cpp, instead of hard-aborting the process through the ubsan-minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image library, build configuration in Android.bp)",
     "weakness": "Sanitizer-detected integer overflow (signed/unsigned) in libdng_sdk led to a process abort (DoS); the specific overflow sites are not shown",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined for \"unsigned-integer-overflow\" and \"signed-integer-overflow\". Per the commit message, these errors were handled by the ubsan-minimal runtime with a hard abort. So any detected integer overflow in the library, for example while decoding attacker-supplied DNG data, killed the hosting process. The commit lists seven bug IDs. The library is already built with rtti and -fexceptions (lines 141-142 of Android.bp), so these faults can be turned into recoverable exceptions instead of fatal aborts.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true }. Per the in-file comment, this disables the ubsan_minimal runtime so a custom runtime can be injected; Soong has no dedicated flag for this, so 'never' is combined with explicit -fsanitize= cflags.",
      "Android.bp, cc_library libdng_sdk: adds the checks back explicitly through cflags \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\", plus \"-fno-sanitize-link-runtime\" so that the sanitizer runtime is not linked. \"-DqDNGValidate=0\" is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message. Because the library is compiled with -fexceptions, a detected overflow now unwinds as a C++ exception instead of aborting.",
      "OWNERS: metadata only (bug component and owner list), no functional effect."
     ],
     "completeness": [
      "The fix is a build-level mitigation: it changes what happens when an overflow is detected (throw instead of abort). It does not fix any of the underlying overflowing arithmetic; the diff touches no dng_sdk source file.",
      "Five integer-overflow handlers are provided (add, sub, mul, negate, divrem), matching the two checks enabled in the cflags (signed- and unsigned-integer-overflow). The material does not show whether these are every runtime entry point the compiler may emit for those checks.",
      "The replacement applies only to the cc_library \"libdng_sdk\" module. The sanitize.misc_undefined block remains in libdng_sdk-defaults. Any other module built from those defaults (none shown in the material) would keep the default behavior.",
      "The material does not show whether the thrown std::runtime_error is caught at every decode entry point. An uncaught exception would still terminate the process. The commit's Test: line is ImageDecoderTest#testInterestingDngs."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now raises std::runtime_error (\"ubsan: add-overflow\", etc.) instead of aborting the process. Malformed DNG inputs that previously crashed the host process are expected to fail the decode instead.",
      "libdng_sdk no longer links the sanitizer runtime (sanitize never plus -fno-sanitize-link-runtime). The overflow instrumentation itself is kept through explicit cflags.",
      "Unsigned wraparound, which is well-defined C++ but is instrumented by unsigned-integer-overflow, also throws now."
     ],
     "preconditions": [
      "A DNG image is decoded through libdng_sdk from attacker-controlled data. The commit's test (ImageDecoderTest#testInterestingDngs) points to the ImageDecoder path as the validated entry point.",
      "The input must make one of the instrumented integer operations overflow. The specific overflow sites are not shown in the material (seven bug IDs are referenced)."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp changes are identical; the OWNERS diff differs only in the pre-existing owner list. Resolver reports 3/5 patched-region lines at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16, cherry-picked from two internal commits. The code context at android-security-15.0.0_r17 shows the Android.bp and runtime changes in place (resolver: 3/4 lines).",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, same functional content. Resolver reports 3/5 lines at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (commit title marked RESTRICT AUTOMERGE), same functional content. Resolver reports 3/5 lines at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching three files, with no dng_sdk source changes. The Android.bp hunk has the same index (1978b39..3d95a7b) on all four branches. The resolver's partial line counts (3/5, or 3/4 on 15) are a heuristic; code context confirming the change is only available for 15 (r17).",
      "The approach relies on -fexceptions, which is already set in libdng_sdk-defaults, and on sanitize never combined with explicit -fsanitize cflags, as described in the Android.bp comment. Downstream build customizations to this module should keep both."
     ],
     "confidence": "high",
     "unknowns": [
      "The specific overflow sites in dng_sdk behind the seven referenced bugs are not shown.",
      "The material does not show which catch handlers in dng_sdk or its callers (e.g. the ImageDecoder path) absorb the new std::runtime_error, or whether every entry point is covered.",
      "The content of the second internal cherry-pick source (475c8cce973f) is not described.",
      "Whether the five handlers cover every sanitizer runtime entry point the compiler emits for the enabled checks is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0042": {
     "cve": "CVE-2026-0042",
     "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a library-local runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that turns detected integer-overflow checks into C++ exceptions, so a detected overflow no longer hard-aborts the decoding process.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and sanitizer runtime",
     "weakness": "Integer overflow during DNG processing surfaced as a process abort (DoS) via the ubsan-minimal runtime; the fix changes the failure mode rather than any individual arithmetic site.",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow (lines 144-149 at android-security-15.0.0_r17). The commit replaces the ubsan-minimal runtime because its handling is a hard abort, so an instrumented overflow reached while processing DNG data terminates the host process. The commit message states that C++ exceptions are already enabled in this library (the defaults show rtti: true and cppflags -fexceptions) and that a thrown exception is a much better outcome than a hard abort for ubsan errors. The commit lists seven bug IDs.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true }. Per the inline comment, this disables the ubsan_minimal runtime so the module can inject its own runtime, overriding the misc_undefined entries inherited from libdng_sdk-defaults.",
      "Android.bp, cc_library libdng_sdk: keeps the instrumentation by passing -fsanitize=signed-integer-overflow,unsigned-integer-overflow directly in cflags, and adds -fno-sanitize-link-runtime so no sanitizer runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs so the module itself supplies the handler symbols that the instrumentation calls.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message instead of aborting.",
      "OWNERS: metadata-only change (bug component, include of the frameworks/base graphics OWNERS, LAST_RESORT_SUGGESTION markers); no security effect."
     ],
     "completeness": [
      "Provides handlers for add, sub, mul, negate and divrem overflow, which matches the two checks enabled in the module's cflags.",
      "The fix does not correct any specific overflowing arithmetic in the DNG SDK sources. Every overflow site remains and now results in a thrown std::runtime_error. The material does not show whether every frame between the overflow and the nearest catch is exception-safe.",
      "The host_linux static_libs libcompiler_rt-extras entry is untouched. The material does not show the per-target effect of -fno-sanitize-link-runtime on host builds."
     ],
     "behavior_changes": [
      "On a detected signed or unsigned integer overflow inside libdng_sdk, a std::runtime_error (\"ubsan: add-overflow\" etc.) is thrown instead of the hard abort from the ubsan-minimal runtime. Callers that catch the exception see a decode failure.",
      "libdng_sdk no longer links the ubsan-minimal runtime. Sanitizer instrumentation is now controlled by explicit cflags, with sanitize.never set, instead of by Soong's sanitize property.",
      "The sanitize.misc_undefined configuration in libdng_sdk-defaults is effectively overridden for libdng_sdk only."
     ],
     "preconditions": [
      "An application or system component decodes an attacker-supplied DNG image through libdng_sdk. The commit's Test line (ImageDecoderTest#testInterestingDngs) points to the ImageDecoder decoding path as the exercised consumer.",
      "The parsed data must drive an instrumented integer operation to overflow. The material does not show the specific overflow sites.",
      "Index search returned frameworks/base android_hardware_camera2_DngCreator.cpp and frameworks/av img_utils DngUtils.cpp. These use img_utils TiffWriter and do not evidently link libdng_sdk, so they are not confirmed callers."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. Android.bp and ubsan_throwing_runtime.cpp are identical to the Android 15 commit; the OWNERS diff differs only in the removed entries (adaubert, zalman).",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (cherry-pick, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). The code context at android-security-15.0.0_r17 shows the full sanitize/cflags/srcs block and the new runtime file in place.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the runtime file are identical; the OWNERS base differs trivially.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in the android-17.0.0_r1 base (RESTRICT AUTOMERGE); identical Android.bp and runtime file."
     },
     "backport_notes": [
      "The change is build configuration plus one new source file. It applies cleanly where the libdng_sdk cc_library has the single cflags: [\"-DqDNGValidate=0\"] line and the defaults enable C++ exceptions (-fexceptions). The approach depends on what the Android.bp comment describes: setting sanitize 'never' combined with raw -fsanitize= cflags (plus -fno-sanitize-link-runtime) works in Soong. Verify this on the target build system.",
      "The per-major resolver reports 3/5 (3/4 on 15) lines of the patched region present at the latest tags, but the Android 15 code context shows the complete fix in place. Verify by checking for the srcs entry ubsan-replacement/ubsan_throwing_runtime.cpp."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific DNG SDK arithmetic sites overflow for the seven referenced bugs; the diff touches no SDK source files.",
      "Which consumers catch std::runtime_error (as opposed to the SDK's own exception type) at the library boundary; the material only names ImageDecoderTest#testInterestingDngs.",
      "Whether any overflow site sits beneath a noexcept frame or destructor, where the new throw would still terminate the process.",
      "Whether any build configuration makes the compiler reference non-recoverable (_abort) handler variants or traps, which this runtime does not supply.",
      "Whether the parameterless handler signatures match the sanitizer runtime ABI in a way that matters; the material does not describe that ABI.",
      "Why the bulletin rates a DoS as Critical; the material gives no detail on the affected process."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0043": {
     "cve": "CVE-2026-0043",
     "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits (signed and unsigned add/sub/mul/negate/divrem) go to a runtime private to the library that throws std::runtime_error, instead of linking the Soong ubsan-minimal runtime. Detected arithmetic overflows in DNG processing become C++ exceptions rather than a hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG SDK used for DNG image decoding): build configuration and sanitizer runtime",
     "weakness": "Integer overflow handling (the diff changes how sanitizer-detected signed and unsigned integer overflows are handled across the whole library; no specific overflow site is shown)",
     "root_cause": "libdng_sdk took sanitize.misc_undefined [\"unsigned-integer-overflow\",\"signed-integer-overflow\"] from its defaults. Integer overflows in the SDK's arithmetic were therefore handled by the Soong-linked ubsan-minimal runtime, which the commit message describes as a hard abort. The commit message says C++ exceptions, already enabled for the library (rtti: true and cppflags -fexceptions in the defaults block), are a much better way to handle ubsan errors. Seven bug IDs are attached to this single change, which is library-wide rather than a set of individual arithmetic fixes. The material does not show the specific overflowing expressions, or how the pre-fix behaviour led to the Critical EoP rating.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so Soong no longer applies its sanitizer configuration to this module and no longer links the ubsan-minimal runtime (as the inline comment explains).",
      "Android.bp, cc_library libdng_sdk: adds cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which turns the same two checks back on directly through the compiler. Also adds -fno-sanitize-link-runtime so no ubsan runtime is linked, and adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow as extern \"C\" with hidden visibility. Each one throws std::runtime_error with a descriptive message. The overflow checks the compiler inserts in libdng_sdk now resolve to these handlers, so a detected overflow unwinds as a C++ exception instead of aborting.",
      "OWNERS: ownership metadata update only, no functional effect."
     ],
     "completeness": [
      "Covers only the two sanitizer classes named in cflags (signed-integer-overflow, unsigned-integer-overflow) and only the five matching handler entry points. No other UBSan checks are enabled or handled for libdng_sdk.",
      "Applies only to the cc_library named libdng_sdk. The defaults block still carries sanitize.misc_undefined, so any other module built from those defaults (none shown in the material) keeps the previous behaviour.",
      "The fix does not change any specific arithmetic in the SDK sources. It relies on compiler instrumentation catching the overflow at runtime, and on a caller catching the resulting std::runtime_error. The material shows no catch site, so it does not show that all consumers recover rather than terminate.",
      "sanitize.never: true turns off all Soong-managed sanitizers for libdng_sdk. The material does not show which other sanitizers, if any, were applied to this module before."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk arithmetic now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting through the ubsan-minimal runtime.",
      "libdng_sdk no longer links a ubsan runtime (-fno-sanitize-link-runtime, sanitize.never), and Soong no longer applies sanitizer configuration to the module. The two overflow checks are enabled explicitly through cflags instead.",
      "Callers of libdng_sdk may now see a std::runtime_error propagate out of SDK calls where the process used to abort."
     ],
     "preconditions": [
      "Code linked against libdng_sdk must process a DNG image whose contents push libdng_sdk arithmetic into an integer overflow. The Test line (atest ImageDecoderTest#testInterestingDngs) points to the image decoding path as the consumer that was exercised.",
      "The material does not show the triggering input structure or the overflow site."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp changes are identical; only the OWNERS content differs. Resolver reports 3/5 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (cherry-pick, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). The code context at android-security-15.0.0_r17 shows the patched Android.bp region and the new runtime file. The resolver reports a 3/4 line match there.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, identical functional diff. Resolver reports 3/5 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (RESTRICT AUTOMERGE), identical functional diff. Resolver reports 3/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit. It depends only on the existing defaults (rtti: true, cppflags -fexceptions), which must stay in place so the thrown std::runtime_error can unwind. The Android.bp hunk is the same on all four branches (index 1978b39 -> 3d95a7b), so it applies wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"].",
      "sanitize.never: true turns off every Soong-managed sanitizer for the module, so a downstream tree that relies on other Soong sanitizers for libdng_sdk would need to re-add them through explicit cflags (general platform knowledge). The resolver's 3/4 or 3/5 line counts are partial matches; the r17 code context shows the complete patched region on Android 15."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic expressions in dng_sdk overflow, and in which SDK files or functions. The material contains no source change to dng_sdk code.",
      "What the seven attached bug IDs describe.",
      "Why the pre-fix state is rated Critical EoP rather than a sanitizer abort (denial of service). The material does not show whether the sanitizer runtime was actually in effect on affected builds, or what the overflow made possible.",
      "Where consumers (for example the image decoding path named in the Test line) catch the std::runtime_error, and whether every consumer handles it without terminating.",
      "Whether any other module builds from libdng_sdk-defaults and so keeps the previous behaviour."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0044": {
     "cve": "CVE-2026-0044",
     "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error from a handler file shipped in the project (ubsan-replacement/ubsan_throwing_runtime.cpp) instead of hard-aborting via the ubsan_minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG/raw image decoding library, build configuration and UBSan runtime)",
     "weakness": "Integer overflow in DNG processing arithmetic, surfaced as a sanitizer-triggered process abort (denial of service). The fix changes how overflows are handled, not the arithmetic.",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow. libdng_sdk used the ubsan_minimal runtime, which the commit message calls a hard abort on ubsan errors. A DNG input that drives libdng_sdk into an instrumented integer overflow therefore terminated the host process. The cited test, ImageDecoderTest#testInterestingDngs, points at the image decoding path. The module is built with cppflags -fexceptions and rtti: true, and the commit message says exceptions are 'a much, much better solution for ubsan errors than a hard abort is'.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize { never: true }. Per the diff comment, this disables the ubsan_minimal runtime for this module so a custom runtime can be injected. The inherited libdng_sdk-defaults misc_undefined setting is not changed.",
      "Android.bp :: cc_library libdng_sdk: adds cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which re-enables the same overflow instrumentation by hand, and -fno-sanitize-link-runtime, so no sanitizer runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp :: cc_library libdng_sdk: adds srcs ubsan-replacement/ubsan_throwing_runtime.cpp, so the module itself provides the UBSan handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>-overflow\"), so a detected overflow becomes a C++ exception instead of an abort.",
      "OWNERS: metadata only (bug component, include of the frameworks/base graphics OWNERS, LAST_RESORT_SUGGESTION entries). No functional effect."
     ],
     "completeness": [
      "Provides handlers for add, sub, mul, negate and divrem overflow, matching the two overflow checks enabled through cflags.",
      "Does not fix any individual overflowing computation in the dng_sdk sources. The overflow still happens and is turned into an exception. Correct behaviour depends on a catch site in libdng_sdk or its caller handling std::runtime_error, and the material does not show that catch site.",
      "Only the libdng_sdk module is changed. libdng_sdk-defaults still carries sanitize.misc_undefined with the overflow checks, so any other module built from those defaults (none shown in the material) would keep the previous behaviour.",
      "The change is build-level and applies to the libdng_sdk module, which is host_supported."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk now raises std::runtime_error with the message 'ubsan: add-overflow' / 'sub-overflow' / 'mul-overflow' / 'negate-overflow' / 'divrem-overflow' instead of aborting the process.",
      "libdng_sdk no longer uses the ubsan_minimal runtime. The handler symbols are provided by the library itself with hidden visibility.",
      "The overflow instrumentation keeps the same scope: the same two checks, now passed through cflags."
     ],
     "preconditions": [
      "libdng_sdk parses an attacker-controlled DNG/raw image whose contents cause an integer overflow in instrumented code. The commit's test (ImageDecoderTest#testInterestingDngs) points at the ImageDecoder decoding path.",
      "Before the fix, the process doing the decode is terminated by the abort. After the fix, the exception must reach a catch handler."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp hunks are identical to the Android 15 commit. The OWNERS hunk differs in its pre-existing content and in the trailing newline (not functional). Resolver reports 3/5 lines of the patched region at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (cherry-pick, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). The code context at android-security-15.0.0_r17 shows the Android.bp and runtime-file changes. The resolver reports 3/4 lines of the patched region present.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Android.bp and the runtime file are identical, and OWNERS differs only in its pre-existing content. Resolver reports 3/5 lines at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (subject carries RESTRICT AUTOMERGE; resolved via base). Android.bp and the runtime file are identical. Resolver reports 3/5 lines at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit with no dependency. Apply the Android.bp hunk to the libdng_sdk cc_library (not to libdng_sdk-defaults), keep the cflags exactly as in the diff, and add the new ubsan-replacement/ubsan_throwing_runtime.cpp. The fix relies on cppflags -fexceptions and rtti: true, which are present in libdng_sdk-defaults in the shown context. The OWNERS hunk can be dropped."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific overflow sites in dng_sdk triggered the seven referenced bugs. The fix is generic and the material shows no source-level overflow location.",
      "Where the thrown std::runtime_error is caught (inside libdng_sdk or in the ImageDecoder caller). The material does not show the catch site.",
      "Whether sanitize { never: true } also removes other Soong-managed sanitizers (for example product-wide CFI/HWASan) from libdng_sdk. The material only states it is used to disable the ubsan_minimal runtime.",
      "Why the per-major resolver reports only partial line presence (3/4 or 3/5) of the patched region, while the code context shows the change applied.",
      "Why the bulletin rates a DoS as Critical. The material gives no detail beyond the rating."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0051": {
     "cve": "CVE-2026-0051",
     "summary": "Rebuilds libdng_sdk so that signed/unsigned integer-overflow UBSan hits throw a C++ std::runtime_error (via a bundled replacement runtime) instead of aborting the hosting process through the ubsan minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new ubsan-replacement/ubsan_throwing_runtime.cpp",
     "weakness": "Integer overflow in DNG parsing arithmetic turned into a hard abort by the sanitizer runtime (crash / DoS); the specific overflowing expressions are not part of the diff.",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined for \"unsigned-integer-overflow\" and \"signed-integer-overflow\". The commit comment says the ubsan_minimal runtime was in use; that runtime aborts on a detected overflow. As a result, input that made dng_sdk arithmetic overflow aborted the process that was decoding it, even though the library is built with -fexceptions and rtti. The commit message calls exceptions 'a much, much better solution for ubsan errors than a hard abort'. The commit cites seven bug IDs; the diff does not touch or identify any overflow site.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } to disable the ubsan_minimal runtime for this module (per the in-file comment), overriding the sanitize settings inherited from libdng_sdk-defaults.",
      "Android.bp, cc_library libdng_sdk: re-enables the same checks manually with cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow and adds -fno-sanitize-link-runtime so no compiler-provided ubsan runtime is linked.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. These take the place of the runtime handlers, and each throws std::runtime_error with a short message. Because libdng_sdk-defaults already sets cppflags -fexceptions and rtti: true, the throw unwinds instead of aborting.",
      "OWNERS: metadata only (bug component, include of frameworks/base graphics OWNERS, last-resort suggestions); no functional effect."
     ],
     "completeness": [
      "The fix changes how an overflow fails (exception instead of abort). It does not correct any overflowing arithmetic: overflows still occur and are still detected, and they now surface as a C++ exception.",
      "Only five overflow handlers (add, sub, mul, negate, divrem) are provided, matching the two -fsanitize checks enabled in cflags. No other UBSan checks are enabled for libdng_sdk.",
      "sanitize.never: true removes all Soong-managed sanitizers from libdng_sdk, not just the integer-overflow ones (general platform knowledge).",
      "libdng_sdk-defaults still carries sanitize.misc_undefined; any other module that uses those defaults (none shown in the material) is not changed by this fix.",
      "The thrown type is std::runtime_error, not a dng_exception. The diff does not show whether every consumer path catches it. The commit's test is ImageDecoderTest#testInterestingDngs."
     ],
     "behavior_changes": [
      "A detected signed or unsigned integer overflow inside libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\" etc.) instead of aborting the process; callers that catch it see a decode failure rather than a crash.",
      "libdng_sdk no longer uses Soong's sanitizer configuration (sanitize.never: true); the overflow instrumentation comes from explicit module cflags, and the module is host_supported.",
      "The replacement handlers are hidden-visibility, so they are not exported from libdng_sdk."
     ],
     "preconditions": [
      "A DNG must be processed by a component that links libdng_sdk, and processing must drive an arithmetic expression into signed or unsigned integer overflow. The commit's regression test points to the ImageDecoder DNG decode path as one such consumer.",
      "The specific overflow sites and input constraints are not in the material."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28, identical Android.bp and ubsan_throwing_runtime.cpp changes; only the OWNERS hunk differs (previous owners list).",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (cherry-pick, Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902); code context at android-security-15.0.0_r17 shows the full change in place.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, identical functional diff; OWNERS hunk differs.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (resolved in base) as 'RESTRICT AUTOMERGE', identical functional diff."
     },
     "backport_notes": [
      "Build-config-only change plus one new source file. It applies to a branch whose libdng_sdk module still has cflags: [\"-DqDNGValidate=0\"] and whose libdng_sdk-defaults set cppflags -fexceptions, which the throw depends on. The target toolchain must accept -fno-sanitize-link-runtime (general platform knowledge).",
      "The resolver reports only part of the patched region at the latest tags: 3/4 lines on Android 15 and 3/5 lines on 14, 16 and 17. Even so, the Android 15 code context at android-security-15.0.0_r17 shows all added lines present."
     ],
     "confidence": "high",
     "unknowns": [
      "Which dng_sdk expressions overflow for the reported inputs, and what each of the seven cited bug IDs tracks; no overflow site appears in the diff.",
      "Which consumers catch the std::runtime_error; the diff does not show catch sites, only that ImageDecoderTest#testInterestingDngs is the regression test.",
      "Whether any other module built from libdng_sdk-defaults still links the aborting ubsan minimal runtime.",
      "Why the resolver reports only partial patched-region matches at the latest tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0052": {
     "cve": "CVE-2026-0052",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of invoking the Soong-linked ubsan-minimal runtime that aborts the hosting process.",
     "component": "platform/external/dng_sdk, libdng_sdk (Android.bp build config and UBSan runtime)",
     "weakness": "Integer overflow in DNG parsing turned into a process abort by the UBSan minimal runtime (crash, DoS)",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow (Android.bp lines 144-149). With that Soong configuration the library used the ubsan_minimal runtime, which does a hard abort when an overflow is detected (per the commit message and Android.bp comment). Any arithmetic overflow triggered while libdng_sdk parses attacker-controlled DNG data therefore aborted the process using the library (the test reference ImageDecoderTest#testInterestingDngs points at the ImageDecoder path), even though the library is built with -fexceptions and rtti (lines 141-142) and could recover through exception handling. The commit references seven bugs. The diff does not fix any individual arithmetic site. It changes what happens when an overflow is detected.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds `sanitize: { never: true }` so Soong does not apply its sanitizer configuration or link the ubsan-minimal runtime for this module (this overrides the misc_undefined inherited from libdng_sdk-defaults).",
      "Android.bp :: cc_library libdng_sdk: turns the same checks back on manually via cflags `-fsanitize=signed-integer-overflow,unsigned-integer-overflow` and adds `-fno-sanitize-link-runtime`, so the compiler instruments the code but no sanitizer runtime is linked. `-DqDNGValidate=0` is kept.",
      "Android.bp :: cc_library libdng_sdk: adds `ubsan-replacement/ubsan_throwing_runtime.cpp` to srcs to supply the handler symbols the instrumentation calls.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\") instead of aborting. The handlers are declared without parameters, so they ignore any diagnostic data.",
      "OWNERS: ownership and bug-component metadata only, no functional effect."
     ],
     "completeness": [
      "Covers five integer-overflow handler entry points (add, sub, mul, negate, divrem) for the two sanitizers enabled in the libdng_sdk module. No other UBSan check kinds are enabled in the module's cflags.",
      "The fix is a mitigation at the detection layer. None of the arithmetic in the dng_sdk sources is changed, so overflows are still detected and now surface as exceptions that must be caught by the caller or by dng_sdk's own exception handling.",
      "The `sanitize.never` setting and the custom runtime apply only to the libdng_sdk module. libdng_sdk-defaults still carries misc_undefined (lines 144-149 at r17), so any other module built from those defaults (none is shown in the material) would still get the aborting minimal runtime.",
      "Recovery depends on the exception reaching a catch site. The material does not show the catch sites in dng_sdk or its consumers."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error (message \"ubsan: add-overflow\" etc.) instead of terminating the process.",
      "libdng_sdk no longer links the Soong-provided ubsan-minimal runtime. The sanitizer instrumentation stays in place through explicit cflags.",
      "The exception text does not say where the overflow happened, because the handlers take no arguments.",
      "Host builds (host_supported: true) get the same behavior because the cflags and srcs are not target-scoped."
     ],
     "preconditions": [
      "An attacker-supplied DNG/TIFF-based image is parsed by libdng_sdk in the target process. The test name ImageDecoderTest#testInterestingDngs indicates that the android.graphics.ImageDecoder path is a consumer.",
      "The parsed data must drive arithmetic in dng_sdk to a signed or unsigned integer overflow (the specific sites are not shown in the material)."
     ],
     "per_major": {
      "14": "same fix: commit 42e65547eb33 in android-security-14.0.0_r28, identical Android.bp and runtime file. The OWNERS hunk differs only in the names it replaces. Resolved by change_id.",
      "15": "same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16 (the commit analysed here). Resolved by change_id.",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7, identical Android.bp and runtime file. The OWNERS text differs slightly. Resolved by change_id.",
      "17": "adapted fix: commit 81ded662eee0 in android-17.0.0_r1 (the original that the 15 commit was cherry-picked from). The Android.bp layout differs: the hunk starts at line 105 and the module already has an srcs list, so ubsan_throwing_runtime.cpp is appended to that list instead of getting a new srcs block. No OWNERS change. Resolved via related_bug."
     },
     "backport_notes": [
      "The Android.bp hunks for 14/15/16 are byte-identical (same index 1978b39..3d95a7b), so the patch applies cleanly wherever the libdng_sdk module has `cflags: [\"-DqDNGValidate=0\"]` directly after `defaults`. For trees with the 17-style layout, add the runtime source to the module's existing srcs list instead. Keep `sanitize: { never: true }` together with `-fno-sanitize-link-runtime`: with Soong's misc_undefined the minimal runtime is used, and the compiler emits the *_minimal handler names rather than the __ubsan_handle_*_overflow symbols this file defines (general platform knowledge).",
      "The library must keep `cppflags: [\"-fexceptions\"]` and `rtti: true` from libdng_sdk-defaults so the thrown std::runtime_error can unwind. Check the consumer catch sites in the target tree, since they are not shown in the material."
     ],
     "confidence": "high",
     "unknowns": [
      "Which concrete arithmetic sites in dng_sdk overflow, and whether the seven referenced bugs are distinct sites (the bugs are not described in the material).",
      "Where the thrown std::runtime_error is caught (a try/catch inside dng_sdk or in consumers such as the ImageDecoder/Skia path), and whether every consumer of libdng_sdk handles it.",
      "Whether any other module in external/dng_sdk inherits libdng_sdk-defaults and therefore still aborts on overflow.",
      "Why the per-major checks report only partial presence of the patched region at the latest security tags (3/4 lines at 15.0.0_r17 and similar) although the resolver marks each branch as resolved and the r17 code context shows the full change."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0080": {
     "cve": "CVE-2026-0080",
     "summary": "Changes the libdng_sdk build so that integer-overflow sanitizer hits throw a std::runtime_error (from a bundled replacement ubsan runtime) instead of hard-aborting through the ubsan_minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG SDK used for DNG image decoding), build configuration",
     "weakness": "Integer overflow in dng_sdk turned into a process abort by the ubsan minimal runtime (reachable abort / DoS). This commit does not change the underlying arithmetic.",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined = [unsigned-integer-overflow, signed-integer-overflow] (lines 144-149 at android-security-15.0.0_r17). Per the commit message and the Android.bp comment, this used the ubsan_minimal runtime, which hard-aborts on a detected overflow. So input that triggered an integer overflow in instrumented dng_sdk code ended the process that was decoding it, even though the library is built with rtti: true and cppflags -fexceptions. The commit references seven bugs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942).",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize: { never: true }. Per the inline comment, this disables the ubsan_minimal runtime that Soong would otherwise apply, so a custom runtime can be injected.",
      "Android.bp :: cc_library libdng_sdk: re-enables the two checks by hand with the cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow, and adds -fno-sanitize-link-runtime so no sanitizer runtime is linked automatically.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new file, added to libdng_sdk srcs): defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message. The library already builds with -fexceptions and rtti, so a C++ exception is raised instead of an abort.",
      "OWNERS (14/15/16 only): metadata change that adds the bug component and an OWNERS include. It has no functional effect."
     ],
     "completeness": [
      "Defines handlers for five overflow kinds (add, sub, mul, negate, divrem), for the libdng_sdk module only. The defaults shown enable only the signed and unsigned integer-overflow checks.",
      "Does not fix any of the overflowing arithmetic in dng_sdk sources. It only changes the failure mode from an abort to a C++ exception. The material does not show whether callers catch std::runtime_error. An exception that is never caught would still terminate the process.",
      "libdng_sdk-defaults still carries sanitize.misc_undefined (Android.bp lines 144-149 at r17). Any other module built from those defaults would keep the old behavior. The material does not show whether such modules exist.",
      "The host_linux target still links libcompiler_rt-extras. The material does not show whether host builds resolve the handlers the same way."
     ],
     "behavior_changes": [
      "An integer overflow detected by the sanitizer inside libdng_sdk now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting the process.",
      "Soong-managed sanitization is disabled for libdng_sdk (sanitize.never: true). The two integer-overflow checks are kept through explicit cflags.",
      "Test cited: atest ImageDecoderTest#testInterestingDngs."
     ],
     "preconditions": [
      "libdng_sdk must process DNG input that triggers a signed or unsigned integer overflow in instrumented code.",
      "The referenced test (ImageDecoderTest#testInterestingDngs) suggests that DNG decoding through ImageDecoder reaches this code. The material does not show which process hosts the decode.",
      "Per index search, the matches in frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp and frameworks/av/media/img_utils/src/DngUtils.cpp use img_utils TiffWriter and do not appear to involve libdng_sdk."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. Identical Android.bp hunk (1978b39..3d95a7b), the new ubsan_throwing_runtime.cpp, and an OWNERS update. Resolved by change_id. The line check at android-security-14.0.0_r29 finds 3/5 lines of the patched region present.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (Cherrypick-From lists 81ded662eee0 and 475c8cce973f). Identical Android.bp hunk, the new runtime file, and OWNERS. The code context at android-security-15.0.0_r17 shows the Android.bp block and the runtime file. The line check reports 3/4 lines present.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Identical Android.bp hunk and runtime file, and OWNERS updated. Resolved by change_id. The line check at android-security-16.0.0_r8 finds 3/5 lines present.",
      "17": "adapted fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1. Android.bp there has a different layout (hunks at lines 105 and 220): the sanitize/cflags block replaces the single cflags line, and ubsan_throwing_runtime.cpp is appended to the existing explicit srcs list. There is no OWNERS change. Resolved by related_bug. The line check at android-security-17.0.0_r1 finds 5/6 lines present."
     },
     "backport_notes": [
      "The Android.bp hunk is identical on 14/15/16 (base blob 1978b39). On trees with an explicit per-file srcs list (as in 17), add ubsan_throwing_runtime.cpp to that list instead. The OWNERS hunk is non-functional.",
      "Google applied sanitize.never: true, the explicit -fsanitize cflags, -fno-sanitize-link-runtime and the new runtime source together in every branch. Backport them as one unit."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which dng_sdk arithmetic sites overflow for the seven referenced bugs. The commit does not touch dng_sdk sources.",
      "Which process hosts the DNG decode on the affected path, and why the bulletin rates the issue Critical.",
      "Whether all callers of libdng_sdk catch std::runtime_error, so that the exception does not escape and terminate the process.",
      "Whether other modules use libdng_sdk-defaults and therefore still abort on overflow.",
      "Whether the five defined handlers cover every handler the compiler emits for these checks.",
      "The content of the second cherry-picked commit 475c8cce973f (only the combined result is shown).",
      "Why the per-major line checks report only partial presence of the patched region in the latest tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0097": {
     "cve": "CVE-2026-0097",
     "summary": "In the LE Passkey Display / Key Notification association models, the Bluetooth SMP layer now waits for both user approval (SMP_SecurityGrant with SMP_SUCCESS) and remote passkey confirmation (STK generation) before sending SMP_KEY_READY_EVT. A user rejection now fails the pairing with SMP_PAIR_AUTH_FAIL, and SMP pairing is cancelled when the BTM pairing timer expires in BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with an LE pairing active.",
     "component": "platform/packages/modules/Bluetooth, system/stack/smp (SMP LE pairing state machine), system/stack/btm/btm_sec.cc, system/btif/src/btif_dm.cc",
     "weakness": "Missing user-authorization step in the LE pairing state machine. For the passkey display / key notification models, smp_process_stk moved on to key readiness once the STK was generated, without waiting for local user approval.",
     "root_cause": "In system/stack/smp/smp_keys.cc:smp_process_stk, once the STK was generated the code always masked the key and sent SMP_KEY_READY_EVT with SMP_KEY_TYPE_STK, moving on toward encrypting the link with the STK. There was no state for local user approval in the SMP_MODEL_SEC_CONN_PASSKEY_DISP and SMP_MODEL_KEY_NOTIF models. SMP_SecurityGrant in system/stack/smp/smp_api.cc had no branch for these models. Separately, btm_sec_pairing_timeout in system/stack/btm/btm_sec.cc did not cancel SMP pairing in BTM_PAIR_STATE_WAIT_AUTH_COMPLETE for LE. Also, btif_dm_ble_key_notif_evt in system/btif/src/btif_dm.cc did not initialize pairing_cb.is_le_only / is_le_nc for the key-notification event; the commit message calls these the default LE pairing flags.",
     "fix_analysis": [
      "system/stack/smp/smp_int.h: tSMP_CB gets a passkey_display_state struct with two bools: approved (the user approved the pairing) and confirmed (the remote device confirmed the passkey).",
      "system/stack/smp/smp_keys.cc:smp_process_stk: after smp_mask_enc_key, if selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF, the function sets passkey_display_state.confirmed = true and stores the masked key in p_cb->tk. If approved is not yet set, it logs 'Waiting for user to approve pairing' and returns without sending SMP_KEY_READY_EVT. Otherwise (other models, or already approved) it sends SMP_KEY_READY_EVT with SMP_KEY_TYPE_STK as before.",
      "system/stack/smp/smp_api.cc:SMP_SecurityGrant: a new early branch runs when bd_addr matches smp_cb.pairing_bda and the model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF. If res == SMP_SUCCESS, it sets approved = true. If confirmed is already set, it then sends SMP_KEY_READY_EVT with {SMP_KEY_TYPE_TK, smp_cb.tk.data()}; if not, it only logs and waits. Any other res sends SMP_AUTH_CMPL_EVT with status SMP_PAIR_AUTH_FAIL. The branch returns, so the existing consent/grant handling is skipped.",
      "system/stack/btm/btm_sec.cc:btm_sec_pairing_timeout: in case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, if btm_sec_cb.pairing_flags has BTM_PAIR_FLAGS_LE_ACTIVE, the function calls SMP_PairCancel(p_cb->pairing_bda), then falls through (FALLTHROUGH_INTENDED) to the existing BTM_PAIR_STATE_GET_REM_NAME timeout handling. SMP_PairCancel raises SMP_AUTH_CMPL_EVT with SMP_PAIR_FAIL_UNKNOWN when SMP is not idle for that address. This matches the commit message: LE pairing is cancelled if authentication times out. The state/flags log is raised from verbose to warn.",
      "system/btif/src/btif_dm.cc:btif_dm_ble_key_notif_evt: sets pairing_cb.is_le_only = true and pairing_cb.is_le_nc = false after bond_state_changed and is_ssp = false."
     ],
     "completeness": [
      "The approval gate applies only when selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF. For all other models, smp_process_stk and SMP_SecurityGrant behave as before.",
      "The gate is placed only in smp_process_stk (the STK key-ready path). No other existing SMP_KEY_READY_EVT emission site is changed in the diff; for example, smp_generate_compare is untouched.",
      "The diff has no code that clears passkey_display_state.approved / confirmed. The material does not show whether the existing control-block reset between pairing sessions covers the new struct.",
      "The commit message names Flag: com.android.bluetooth.flags.passkey_entry_pairing_approval, but the diff has no flag check, so the new behaviour is unconditional in this backport.",
      "The SMP_SecurityGrant branch applies only to the two listed models and only when bd_addr equals smp_cb.pairing_bda. In all other cases the existing logic runs."
     ],
     "behavior_changes": [
      "For LE passkey display / key notification pairings, the SMP_KEY_READY_EVT from smp_process_stk is held back until the user approves. An info log, 'Waiting for user to approve pairing', is written when the remote confirms first.",
      "A user rejection via SMP_SecurityGrant now ends the pairing with SMP_PAIR_AUTH_FAIL (SMP_AUTH_CMPL_EVT).",
      "When the BTM pairing timer expires in BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with BTM_PAIR_FLAGS_LE_ACTIVE set, the SMP pairing is now cancelled (SMP_PairCancel), in addition to the existing UI timeout notification.",
      "btm_sec_pairing_timeout logs its state/flags at warn level instead of verbose. SMP_SecurityGrant logs extra fields (res, br_state, cb_evt, pairing_bda, association model).",
      "For key-notification events, btif pairing_cb is now marked is_le_only = true and is_le_nc = false."
     ],
     "preconditions": [
      "An LE (SMP) pairing is in progress and selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF (the local device displays the passkey and the remote enters it).",
      "The remote device confirms the passkey, so smp_process_stk is reached. Before the fix, smp_process_stk then sent SMP_KEY_READY_EVT without waiting for user approval.",
      "The material does not show whether the pairing must be started locally or remotely, or what remote traffic is needed."
     ],
     "per_major": {
      "14": "Adapted fix (725df035dfe2, android-security-14.0.0_r28) with the same logic. It uses the BTM_TRACE_WARNING / SMP_TRACE_* macros and btm_cb.pairing_flags instead of btm_sec_cb.pairing_flags, tSMP_CB is a typedef struct, and SMP_SecurityGrant keeps its LOG_ASSERT on is_gd_shim_enabled. 7/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Same fix (c18ac65121e1, android-security-15.0.0_r16). It is a combined backport of ag/35861798, ag/35914708 and ag/35861802. 7/8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Adapted fix (ce76f82dac2a, android-security-16.0.0_r7) with the same logic. The btif function is named btif_dm_ble_passkey_notif_evt and uses std::format, and the is_le_only/is_le_nc init comes after the bond_type assignment. 7/8 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "The resolver says it is resolved in base (57541dad9e1f, android-17.0.0_r1). However, the diff supplied for 17 is identical to the Android 14 diff, including its index hashes, and only 3/8 lines of the patched region are present at android-security-17.0.0_r1. The material does not reliably show what the fix looks like on 17."
     },
     "backport_notes": [
      "Apply all five file changes together. The new struct in smp_int.h is needed by smp_keys.cc and smp_api.cc. Put the btif_dm.cc flag init in whichever function handles the LE key-notification event (btif_dm_ble_key_notif_evt on 14/15, btif_dm_ble_passkey_notif_evt on 16). Use btm_cb or btm_sec_cb and the logging macros that match the branch.",
      "The commit message names a feature flag, but the diff does not check it."
     ],
     "confidence": "medium",
     "unknowns": [
      "How the SMP state machine handles the deferred SMP_KEY_READY_EVT that SMP_SecurityGrant sends with SMP_KEY_TYPE_TK (rather than SMP_KEY_TYPE_STK), and which SMP flows call smp_process_stk for the SC passkey model. The material does not show smp_act.cc or the state tables.",
      "The full pre-fix behaviour of SMP_SecurityGrant beyond the consent-dialog branch.",
      "Whether existing control-block reset code resets passkey_display_state between pairing sessions.",
      "How btif routes the user's dialog response to SMP_SecurityGrant, and what role is_le_only / is_le_nc play in that routing.",
      "The exact trigger, and why the issue is rated Critical EoP beyond what the diff implies.",
      "What the Android 17 fix actually contains, since the supplied 17 diff is identical to the 14 diff."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21352": {
     "cve": "CVE-2026-21352",
     "summary": "Bulk update of external/dng_sdk to 1.7.1 build 2471. It adds integer-overflow and size checks to the JXL decode/encode buffer-size computations in dng_jxl.cpp, fixes an off-by-one table read in dng_opcode_MapTable::ReplicateLastEntry, adds an overflow check to dng_ref_counted_block::Allocate, and guards several divide-by-zero cases that occur when blackLevel is 65535.",
     "component": "platform/external/dng_sdk (Adobe DNG SDK: JXL codec glue, opcode processing, memory allocation helpers)",
     "weakness": "Integer overflow / missing bounds checks in buffer size computation, plus an off-by-one out-of-bounds read and unguarded division by zero",
     "root_cause": "Several size and index computations on DNG/JXL file data were done with unchecked arithmetic. In source/dng_jxl.cpp, get_color_channel_data_at in the chunk readers (jxl_image_chunk_reader and jxl_buffer_chunk_reader) truncated size_t xpos/ypos/xsize/ysize into int32 and added them without overflow checks. dng_jxl_decoder::Decode assigned basicInfo.xsize/ysize into dng_point size without a range check. It multiplied fColStep * size.h, fPlaneStep * totalPlanes and the 64-bit bytesNeeded product without overflow checks, and it cast the libjxl output buffer size to uint32 without a range check. EncodeJXL_Common computed previewPixelBuffer.fRowStep and previewBytesNeeded with plain multiplication. jxl_memory_block added 160u to bytesNeeded without an overflow check and did not check Malloc for null. The potential result is undersized allocations. Separately, dng_opcode_MapTable::ReplicateLastEntry read table[fCount], where fCount may equal 0x10000 and the table is allocated with exactly 0x10000 uint16 entries: an off-by-one out-of-bounds read. dng_ref_counted_block::Allocate computed size + sizeof(header) without an overflow check. dng_opcode_GainMap::ProcessArea, RefVignette32, RefBaselineMapPoly32 and dng_opcode_MapTable::Prepare divided by a value that becomes zero when blackLevel == 65535. The commit references two bugs, and the material does not show which individual change corresponds to CVE-2026-21352.",
     "fix_analysis": [
      "source/dng_jxl.cpp:jxl_memory_block::jxl_memory_block: computes mallocSize = (size_t)(160u + bytesNeeded) and calls ThrowOverflow() if mallocSize <= bytesNeeded. It also calls ThrowMemoryFull() if allocator.Malloc returns null, before DNG_ALIGN_SIMD is applied.",
      "source/dng_jxl.cpp:jxl_image_chunk_reader::get_color_channel_data_at: rejects xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\") and computes fArea.b/fArea.r with SafeInt32Add. It adds DNG_REQUIRE(fPlanes <= 0x7fffffff) and DNG_REQUIRE(fArea.W() <= 0x7fffffff), and computes fRowStep with SafeInt32Mult instead of SafeUint32Mult.",
      "source/dng_jxl.cpp:jxl_buffer_chunk_reader::get_color_channel_data_at: same coordinate range check. fArea.t/l/b/r are computed with SafeInt32Add, including the offset from fBuffer.fArea. Same DNG_REQUIREs on fPlanes and fArea.W(), and fRowStep via SafeInt32Mult. The existing SafeUint32Mult for bytesNeeded is kept.",
      "source/dng_jxl.cpp:EncodeJXL_Common: adds DNG_REQUIRE(previewPixelBuffer.fArea.W() <= 0x7fffffff). fRowStep is computed with SafeInt32Mult, and previewBytesNeeded with the 3-argument SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp:dng_jxl_decoder::Decode: adds DNG_REQUIRE(basicInfo.xsize <= 0x7fffffff && basicInfo.ysize <= 0x7fffffff) before assigning into dng_point size. Sets buffer.fRowStep = SafeInt32Mult(buffer.fColStep, size.h) and wholeBuffer.fRowStep = SafeInt32Mult(wholeBuffer.fPlaneStep, totalPlanes). bytesNeeded is computed with the new 3-argument SafeInt64Mult. Adds DNG_REQUIRE(size <= 0xffffffff) before the JxlDecoderImageOutBufferSize result is cast to uint32 for host.Allocate.",
      "source/dng_safe_arithmetic.h and .cpp: adds int64 SafeInt64Mult(int64, int64, int64), implemented as nested 2-argument SafeInt64Mult calls. Removes the __ANDROID__ special case, so __builtin_smull_overflow is used on Android when the compiler supports it.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::ReplicateLastEntry: reads table[fCount - 1] instead of table[fCount]. The constructor accepts fCount from 1 to 0x10000, so table[fCount] could index one past the 0x10000-entry table.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::Prepare: srcScale = (blackLevel < 65535) ? 65535.0 / (65535.0 - blackLevel) : 0.0.",
      "source/dng_ref_counted_block.cpp:dng_ref_counted_block::Allocate: computes mallocSize = size + sizeof(header) into a size_t and calls ThrowOverflow() if mallocSize <= size, before malloc.",
      "source/dng_gain_map.cpp:dng_opcode_GainMap::ProcessArea, plus RefVignette32 and RefBaselineMapPoly32 in source/dng_reference.cpp: blackScale1 = (blackScale2 != 0.0) ? 1.0f / blackScale2 : 0.0. This avoids division by zero when blackLevel == 65535.",
      "source/dng_flags.h: build version goes from 2410 to 2471. source/dng_tag_codes.h: appends tcWebP to the end of the enum, so existing values do not change."
     ],
     "completeness": [
      "All JXL paths changed in the diff (the two chunk readers, dng_jxl_decoder::Decode and EncodeJXL_Common) now use checked arithmetic for area, row step and byte count. The final allocation sizes are range-checked before narrowing casts.",
      "In jxl_buffer_chunk_reader, the kept bytesNeeded = SafeUint32Mult(fRowStep, fArea.H(), fPixelSize) relies on the new DNG_REQUIRE and SafeInt32Mult bounding fRowStep. No explicit check that fArea.H() is non-negative is added, but fArea.b is now computed with SafeInt32Add from values bounded by 0x7fffffff.",
      "The dng_ref_counted_block::Allocate check (mallocSize <= size) compares a size_t against a uint32 size. On 64-bit builds, a uint32 plus sizeof(header) cannot wrap in size_t, so the check only takes effect on 32-bit builds.",
      "The jxl_memory_block else-branch (allocator.Allocate(uint32(bytesNeeded)) when bytesNeeded <= 0x80000000) is unchanged. It is already bounded by the fUseMalloc threshold.",
      "The divide-by-zero guards set the scale to 0.0 instead of rejecting blackLevel == 65535. They do not change input validation.",
      "The commit is a vendor drop that references two bug IDs. The material does not isolate which change is the CVE, so completeness against the specific reported issue cannot be fully judged."
     ],
     "behavior_changes": [
      "JXL images whose xsize or ysize exceeds 0x7fffffff, or whose chunk coordinates or sizes exceed 0x7fffffff, now fail with a dng_exception (ThrowOverflow / DNG_REQUIRE) instead of being processed with truncated values.",
      "Row-step and byte-count overflows in JXL decode/encode now throw a dng_exception instead of wrapping.",
      "A libjxl output buffer size above 0xffffffff now throws instead of being truncated to uint32 for allocation.",
      "Allocation failure in jxl_memory_block's large-allocation path now throws ThrowMemoryFull instead of continuing with a null pointer.",
      "MapTable opcodes now replicate the actual last parsed entry (table[fCount - 1]) instead of the slot after it.",
      "blackLevel == 65535 no longer produces division-by-zero scale factors in GainMap, Vignette, BaselineMapPoly and MapTable processing. The scale becomes 0.0.",
      "SafeInt64Mult on Android now takes the __builtin_smull_overflow path when the compiler supports it.",
      "The SDK build string reported by kDNGSDK_GetInfoVersion changes to 1.7.1 (2471)."
     ],
     "preconditions": [
      "A process that links external/dng_sdk must parse an untrusted DNG file. The JXL changes are only reached if the file uses JXL-compressed image data that reaches dng_jxl_decoder::Decode or the jxl_*_chunk_reader callbacks.",
      "The MapTable, GainMap, Vignette and BaselineMapPoly changes are only reached if the DNG carries the corresponding opcodes and they are applied during processing.",
      "Per index search, the only out-of-project matches are in the DngCreator JNI (frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp), which is a DNG writer path using TiffWriter. The material does not show whether it reaches dng_sdk parsing code."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 72d44e39d499 in android-security-14.0.0_r28. The diff is identical to the Android 15 commit (same file indices and hunks).",
      "15": "same fix: platform/external/dng_sdk 7a737bb68ca6 in android-security-15.0.0_r16. At android-security-15.0.0_r17 the code context shows several patched lines present (jxl_memory_block, GainMap, ref_counted_block, RefVignette32, safe_arithmetic), and dng_flags.h has moved on to build 2502. The resolver reports 5/8 patched-region lines present.",
      "16": "same fix: platform/external/dng_sdk b0d9d2a8d100 in android-security-16.0.0_r7, identical diff.",
      "17": "same fix, slightly different context: platform/external/dng_sdk 9a9e97362f2d in android-17.0.0_r1. The only difference is the dng_reference.cpp base blob (95a6087), where the RefBaselineMapPoly32 hunk sits at line 3011 instead of 3009."
     },
     "backport_notes": [
      "Single commit, but it is a vendor version bump (2410 to 2471). The dng_jxl.cpp hunks depend on the new 3-argument SafeInt64Mult added in dng_safe_arithmetic.h/.cpp, so apply those together. They also call SafeInt32Add and SafeInt32Mult, which this diff does not add, so confirm they exist in your tree. Branches at a different SDK build (for example 2502 at the latest security tags) will not match the dng_flags.h hunk, so verify the guards in dng_jxl.cpp and dng_misc_opcodes.cpp rather than the version string."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which of the bundled changes (JXL size overflow, MapTable off-by-one, ref-counted block overflow, divide-by-zero guards) corresponds to CVE-2026-21352. The commit cites two bug IDs and the bulletin lists one CVE.",
      "Which Android process or app uses dng_sdk to parse untrusted files (the privilege boundary behind the Critical EoP rating) is not shown.",
      "The types of totalPlanes, of the dng_pixel_buffer step fields and of the libjxl size variable in dng_jxl_decoder::Decode are not shown, so the exact overflow window before the fix cannot be confirmed from the material.",
      "Whether enabling __builtin_smull_overflow on Android changes behavior beyond performance is not shown.",
      "Why only 5/8 patched-region lines are present at the latest security tags is not explained by the material. The dng_flags.h build moving to 2502 may account for part of it.",
      "Whether host.Allocate zero-fills the MapTable buffer, which determines the value the old code read when fCount < 0x10000, is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21353": {
     "cve": "CVE-2026-21353",
     "summary": "Updates external/dng_sdk from build 2410 to 1.7.1 build 2471, adding overflow-checked arithmetic and range checks to the JXL decode/encode buffer setup in dng_jxl.cpp, an overflow check in dng_ref_counted_block::Allocate, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and divide-by-zero guards for blackLevel == 65535 in the GainMap, MapTable, Vignette and BaselineMapPoly paths.",
     "component": "platform/external/dng_sdk (DNG/JXL image library: source/dng_jxl.cpp, dng_misc_opcodes.cpp, dng_ref_counted_block.cpp, dng_gain_map.cpp, dng_reference.cpp, dng_safe_arithmetic.*)",
     "weakness": "Integer overflow / truncation in buffer geometry and allocation size computation (dng_jxl.cpp, dng_ref_counted_block.cpp), off-by-one out-of-bounds read (dng_opcode_MapTable::ReplicateLastEntry), and floating-point divide by zero when blackLevel == 65535. The material does not say which of these is the CVE.",
     "root_cause": "Several size and coordinate computations on file-controlled values used unchecked arithmetic or narrowing casts. In dng_jxl.cpp, the get_color_channel_data_at callbacks of jxl_image_chunk_reader and jxl_buffer_chunk_reader cast size_t xpos/ypos/xsize/ysize directly to int32 and added them without overflow checks. They also computed the int32 fRowStep with SafeUint32Mult, so a uint32 result above 0x7fffffff could not be represented in the int32 field. dng_jxl_decoder::Decode stored basicInfo.xsize/ysize in dng_point size without a range check, computed fRowStep and the 64-bit bytesNeeded with plain multiplication, and cast the JxlDecoderImageOutBufferSize result to uint32 for host.Allocate without checking that it fits. On its large-allocation path, the jxl_memory_block constructor added 160 to a uint64 bytesNeeded without checking for wrap, and did not check for a null return from allocator.Malloc. EncodeJXL_Common computed the preview fRowStep and previewBytesNeeded with unchecked multiplication. dng_ref_counted_block::Allocate computed size + sizeof(header) without a wrap check. dng_opcode_MapTable::ReplicateLastEntry read table[fCount] instead of table[fCount - 1]. The table holds 0x10000 uint16 entries and the constructor allows fCount == 0x10000, so in that case the read is one past the end. For smaller fCount it reads an entry that was never filled from the stream. Finally, dng_opcode_GainMap::ProcessArea, dng_opcode_MapTable::Prepare, RefVignette32 and RefBaselineMapPoly32 divided by (1 - blackLevel/65535) or (65535 - blackLevel), which is zero when blackLevel == 65535.",
     "fix_analysis": [
      "source/dng_jxl.cpp, jxl_memory_block constructor (malloc path for > 2 GB): computes size_t mallocSize = 160u + bytesNeeded, calls ThrowOverflow() if mallocSize <= bytesNeeded, and calls ThrowMemoryFull() if allocator.Malloc returns null.",
      "source/dng_jxl.cpp, get_color_channel_data_at in jxl_image_chunk_reader and jxl_buffer_chunk_reader: rejects xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\"), computes fArea bounds with SafeInt32Add, adds DNG_REQUIRE checks for fPlanes <= 0x7fffffff and fArea.W() <= 0x7fffffff, and computes fRowStep with SafeInt32Mult instead of SafeUint32Mult. It also changes the printf format in the qLogStreamIntput debug path.",
      "source/dng_jxl.cpp, EncodeJXL_Common: adds DNG_REQUIRE previewPixelBuffer.fArea.W() <= 0x7fffffff, computes fRowStep via SafeInt32Mult and previewBytesNeeded via 3-argument SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp, dng_jxl_decoder::Decode: adds DNG_REQUIRE basicInfo.xsize/ysize <= 0x7fffffff before filling dng_point size, computes buffer.fRowStep and wholeBuffer.fRowStep via SafeInt32Mult and bytesNeeded via the new 3-argument SafeInt64Mult, and adds DNG_REQUIRE size <= 0xffffffff before host.Allocate((uint32) size).",
      "source/dng_safe_arithmetic.h/.cpp: adds int64 SafeInt64Mult(int64, int64, int64), a chained two-argument SafeInt64Mult used by Decode. It also removes the Android-specific '0 &&' that disabled __builtin_smull_overflow, so on Android clang builds SafeInt64Mult can use SafeInt64MultByClang instead of SafeInt64MultSlow. The removed comment had cited a runtime-library issue (LLVM bug 28629).",
      "source/dng_ref_counted_block.cpp, dng_ref_counted_block::Allocate: computes size_t mallocSize = size + sizeof(header) and calls ThrowOverflow() if mallocSize <= size, before malloc.",
      "source/dng_misc_opcodes.cpp, dng_opcode_MapTable::ReplicateLastEntry: reads table[fCount - 1] instead of table[fCount]. The constructor rejects fCount == 0 and fCount > 0x10000 with ThrowBadFormat before calling ReplicateLastEntry.",
      "source/dng_misc_opcodes.cpp, dng_opcode_MapTable::Prepare: srcScale = (blackLevel < 65535) ? 65535.0 / (65535.0 - blackLevel) : 0.0.",
      "source/dng_gain_map.cpp (dng_opcode_GainMap::ProcessArea) and source/dng_reference.cpp (RefVignette32, RefBaselineMapPoly32): blackScale1 = (blackScale2 != 0.0) ? 1.0f / blackScale2 : 0.0.",
      "source/dng_flags.h: build version 2410 -> 2471. source/dng_tag_codes.h: adds a tcWebP enum value. Neither is used by the hunks above."
     ],
     "completeness": [
      "The commit is an upstream version bump that bundles several independent hardening changes under two bug IDs (483697751, 483693973). The material does not say which change is the CVE fix, so completeness is judged per change.",
      "JXL paths: the buffer setups touched by the diff (the two chunk readers, buffer and wholeBuffer in Decode, and the encoder preview buffer) now use range checks plus Safe*Mult/Add. In jxl_buffer_chunk_reader, bytesNeeded was already computed with SafeUint32Mult. The fix bounds the coordinates but adds no check that the requested chunk area lies within fImage/fBuffer bounds. Whether such a check exists elsewhere, or whether other unchecked buffer setups exist in the file, is not shown.",
      "jxl_memory_block: only the > 2 GB malloc path gets the wrap and null checks. The <= 2 GB path uses allocator.Allocate(uint32(bytesNeeded)), which cannot truncate there because bytesNeeded <= 0x80000000 on that path.",
      "dng_ref_counted_block::Allocate: the wrap check only matters where size_t is 32-bit. On 64-bit the uint32 + size_t sum cannot wrap.",
      "MapTable: fixes the one-past-end read and the replication of an unfilled entry. For blackLevel == 65535, the Prepare guard sets srcScale to 0.0 rather than rejecting the file.",
      "The divide-by-zero guards cover dng_opcode_GainMap::ProcessArea, dng_opcode_MapTable::Prepare, RefVignette32 and RefBaselineMapPoly32. The material does not show whether other blackLevel-based divisions exist in the SDK."
     ],
     "behavior_changes": [
      "JXL images whose xsize, ysize, chunk coordinates or plane count exceed 0x7fffffff, or whose stride or byte-size products overflow, now fail with a dng_exception (ThrowOverflow or a DNG_REQUIRE failure) instead of continuing with wrapped values.",
      "JxlDecoderImageOutBufferSize results above 0xffffffff are now rejected before allocation.",
      "Allocation-size wrap in jxl_memory_block (large path) and in dng_ref_counted_block::Allocate now throws via ThrowOverflow. A null Malloc return in jxl_memory_block now throws via ThrowMemoryFull.",
      "MapTable opcodes with fCount < 0x10000 now copy the actual last stream entry (table[fCount-1]) into entries fCount..0xffff, which changes lookup results compared with before.",
      "With blackLevel == 65535, the GainMap, MapTable, Vignette and BaselineMapPoly paths now use a scale of 0.0 instead of dividing by zero.",
      "On Android clang builds where the builtin is available, SafeInt64Mult may now use __builtin_smull_overflow instead of SafeInt64MultSlow."
     ],
     "preconditions": [
      "dng_sdk parses a DNG/TIFF or JXL-compressed image with attacker-controlled dimensions, opcode lists (MapTable, GainMap, Vignette, BaselineMapPoly with blackLevel) or JXL basic info.",
      "Index search found frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp near dng_sdk on Android 15. It is a DNG writer path, and the material does not show that it reaches the patched decode functions.",
      "The material does not show which Android process reaches dng_jxl_decoder::Decode or the opcode paths with untrusted input."
     ],
     "per_major": {
      "14": "Same fix: commit 72d44e39d499 'Update dng_sdk to 1.7.1 2471' in android-security-14.0.0_r28, with a diff identical to Android 15. At android-security-14.0.0_r29, 5/8 lines of the patched region are present. The material does not explain why.",
      "15": "Same fix: commit 7a737bb68ca6 in android-security-15.0.0_r16. Code context at android-security-15.0.0_r17 shows the jxl_memory_block checks, the Allocate wrap check, the blackScale2 guards in GainMap and RefVignette32, and the 3-argument SafeInt64Mult. dng_flags.h is already at build 2502, which may account for some non-matching lines (5/8 present). The ReplicateLastEntry body is not included in the context.",
      "16": "Same fix: commit b0d9d2a8d100 in android-security-16.0.0_r7, identical diff. At android-security-16.0.0_r8, 5/8 lines of the patched region are present. The cause is not shown.",
      "17": "Same fix: commit 9a9e97362f2d in android-17.0.0_r1 (resolved via base). The diff is identical except for the dng_reference.cpp blob hashes and the RefBaselineMapPoly32 hunk at line 3011 instead of 3009. At android-security-17.0.0_r1, 5/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single self-contained commit. Decode in dng_jxl.cpp uses the new 3-argument SafeInt64Mult declared in dng_safe_arithmetic.h and defined in dng_safe_arithmetic.cpp, so those files must be taken together. The security-relevant hunks do not use the dng_tag_codes.h tcWebP addition or the __USE_BUILTIN_SMULL_OVERFLOW change."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which of the bundled changes corresponds to CVE-2026-21353 and which to the second bug ID.",
      "How DNG_REQUIRE fails in this tree (which exception or error code).",
      "Which Android process or API parses untrusted DNG/JXL via dng_sdk, and so the privilege boundary behind the Critical EoP rating.",
      "Whether chunk coordinates are checked against the destination image/buffer bounds anywhere else in dng_jxl.cpp.",
      "Why only 5/8 patched-region lines are present at the latest security tags (14 r29, 15 r17, 16 r8, 17 r1).",
      "Whether the __builtin_smull_overflow re-enablement has a functional effect on Android builds."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-26418": {
     "cve": "CVE-2025-26418",
     "summary": "Adds a MANAGE_USERS-or-INTERACT_ACROSS_USERS permission check to the binder-exposed CarDevicePolicyService.setUserDisclaimerAcknowledged(int) in packages/services/Car, and documents the requirement with @RequiresPermission on the car-lib CarDevicePolicyManager wrapper.",
     "component": "platform/packages/services/Car, CarDevicePolicyService (car admin / new-user disclaimer)",
     "weakness": "missing permission check (confused deputy on a binder entry point)",
     "root_cause": "CarDevicePolicyService extends ICarDevicePolicyService.Stub, so setUserDisclaimerAcknowledged(int userId) is a binder entry point. Before the fix it performed no caller check. It set the internal per-user disclaimer status to NEW_USER_DISCLAIMER_STATUS_ACKED, cancelled the user disclaimer notification via BuiltinPackageDependency.createNotificationHelper(...).cancelUserDisclaimerNotification(user), and then called DevicePolicyManager.acknowledgeNewUserDisclaimer() on a context from mContext.createContextAsUser(user, 0). Per index search, DevicePolicyManagerService.acknowledgeNewUserDisclaimer itself requires canManageUsers or INTERACT_ACROSS_USERS. Because that call is made by the car service, the downstream check presumably passed on the car service's privileges and not the original caller's (inferred, not shown). The commit message says the missing checks 'previously allowed other apps to call it indirectly'.",
     "fix_analysis": [
      "service/src/com/android/car/admin/CarDevicePolicyService.java:setUserDisclaimerAcknowledged(int): calls the new private enforcePermission() as the first statement, before setUserDisclaimerStatus, the notification cancellation and the DevicePolicyManager call.",
      "service/src/com/android/car/admin/CarDevicePolicyService.java:enforcePermission(): uses mContext.checkCallingOrSelfPermission for MANAGE_USERS and INTERACT_ACROSS_USERS. If neither is PERMISSION_GRANTED it throws SecurityException(\"Requires MANAGE_USERS or INTERACT_ACROSS_USERS permissions\"). This matches the permission set that DevicePolicyManagerService.acknowledgeNewUserDisclaimer enforces (per index search).",
      "car-lib/src/android/car/admin/CarDevicePolicyManager.java:setUserDisclaimerAcknowledged(UserHandle): adds @RequiresPermission(anyOf = {MANAGE_USERS, INTERACT_ACROSS_USERS}). This is an annotation only and adds no runtime check in the client library."
     ],
     "completeness": [
      "Covers the binder entry point setUserDisclaimerAcknowledged(int) in CarDevicePolicyService. The check runs before any state change or downstream call.",
      "The sibling binder method setUserDisclaimerShown(int userId) in the same class (context lines 224-227) still has no permission check and still sets the status to NEW_USER_DISCLAIMER_STATUS_SHOWN. It does not call DevicePolicyManager or cancel the notification.",
      "checkCallingOrSelfPermission also accepts the car service's own identity. No validation of userId was added."
     ],
     "behavior_changes": [
      "Callers of ICarDevicePolicyService.setUserDisclaimerAcknowledged that hold neither MANAGE_USERS nor INTERACT_ACROSS_USERS now get a SecurityException, and the disclaimer is not acknowledged.",
      "The car-lib wrapper CarDevicePolicyManager.setUserDisclaimerAcknowledged now carries @RequiresPermission. This affects only annotation and lint, not runtime behavior."
     ],
     "preconditions": [
      "The caller must be able to reach the ICarDevicePolicyService binder. Before the fix, the method itself checked no permission.",
      "Per index search, the legitimate caller is NewUserDisclaimerActivity in packages/apps/Car/Settings, through CarDevicePolicyManager.setUserDisclaimerAcknowledged."
     ],
     "per_major": {
      "14": "same fix: commit 6afaae7b4be4 in android-security-14.0.0_r28 with identical code. Only the context differs (the car-lib method has an @AddedInOrBefore(majorVersion = 33) annotation). 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 20d4c410ce12 in android-security-15.0.0_r16 (cherry-pick, Change-Id Ic4b8ed6037a0ca6fe5d7bc8faf74d3b33eb49a27). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "not affected: the bulletin does not list Android 16. The patched region is present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "same fix: commit 91c7ed0c3871 in base android-17.0.0_r1. 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A single self-contained commit. Between branches only the @AddedInOrBefore context annotation differs. Check that CarDevicePolicyService.java imports android.content.pm.PackageManager, because the diff uses PackageManager.PERMISSION_GRANTED and the material does not show the imports."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether Car Settings NewUserDisclaimerActivity holds MANAGE_USERS or INTERACT_ACROSS_USERS is not shown.",
      "How DevicePolicyManagerService resolves the caller identity for the call made by the car service is not shown.",
      "The exact PoC, and whether the High EoP rating covers effects beyond suppressing the new-user disclaimer, are not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32348": {
     "cve": "CVE-2025-32348",
     "summary": "The fix stops pinned windows (for example PIP) from counting as a visible window when the system decides whether to allow a background activity launch (BAL), for both the caller and the real caller, and in the per-process \"activity in visible task\" exemption.",
     "component": "platform/frameworks/base, WindowManager background activity launch (BackgroundActivityStartController, BackgroundLaunchProcessController, VisibleActivityProcessTracker, WindowProcessController)",
     "weakness": "Authorization check too permissive: a pinned (PIP) window counts as foreground visibility and grants a background activity launch exemption",
     "root_cause": "Before the fix, BalState computed m(Real)CallingUidHasAnyVisibleWindow from mService.hasActiveVisibleWindow(uid). Per index search, that method returns true if VisibleActivityProcessTracker.hasVisibleActivity(uid) is true or the uid has a non-app visible window. hasVisibleActivity did not filter by windowing mode, so an app with only a visible pinned activity could get BAL_ALLOW_VISIBLE_WINDOW. BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed also granted BAL_ALLOW_FOREGROUND when hasActivityInVisibleTask was true and appSwitchState != APP_SWITCH_DISALLOW, even if the process was in pinned windowing mode.",
     "fix_analysis": [
      "VisibleActivityProcessTracker: adds hasVisibleNotPinnedActivity(uid), which calls match() with the predicate wpc -> !wpc.inPinnedWindowingMode(). hasVisibleActivity now passes WindowContainer.alwaysTruePredicate(), and match() no longer accepts a null predicate.",
      "BackgroundActivityStartController.BalState: replaces m(Real)CallingUidHasAnyVisibleWindow with three fields: m(Real)CallingUidHasVisibleActivity, m(Real)CallingUidHasVisibleNotPinnedActivity and m(Real)CallingUidHasNonAppVisibleWindow. These are read from mService.mVisibleActivityProcessTracker and mService.mActiveUids. The fields are also added to toString().",
      "Caller check (checkBackgroundActivityStartAllowedByCaller): BAL_ALLOW_VISIBLE_WINDOW now requires mCallingUidHasVisibleNotPinnedActivity. Non-app visible windows are still allowed by the existing BAL_ALLOW_NON_APP_VISIBLE_WINDOW check.",
      "Real-caller check (exercised by tests via checkBackgroundActivityStartAllowedBySender): with balImproveRealCallerVisibilityCheck enabled, the visible-window allow path requires mRealCallingUidHasVisibleNotPinnedActivity. With the flag disabled, it requires mRealCallingUidHasVisibleNotPinnedActivity or mActiveUids.hasNonAppVisibleWindow(realCallingUid).",
      "BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed: adds an inPinnedWindow parameter. BAL_ALLOW_FOREGROUND for hasActivityInVisibleTask is now granted only if (isCheckingForFgsStart || !inPinnedWindow). WindowProcessController passes inPinnedWindowingMode().",
      "The stats logging call now passes m(Real)CallingUidHasVisibleActivity, which counts pinned activities as visible.",
      "Android 15 history: the fix landed in r10 (483fba0b4777), was reverted in r12 (294a98e56fd5, 'app compatibility regression') and was reapplied in r13 (b10fde7507d7)."
     ],
     "completeness": [
      "Covers the caller and real-caller visible-window exemptions and the per-process foreground-task exemption.",
      "Foreground service start checks (isCheckingForFgsStart) are deliberately left out, so pinned windows still count for background FGS starts.",
      "In the original commit, the 'process bound by foreground uid' exemption in BackgroundLaunchProcessController (isBoundByForegroundUid) was not changed. The r17 code shows a later change that uses mUidHasActiveVisibleNotPinnedWindowPredicate for non-FGS checks there.",
      "At r17, hasVisibleNotPinnedActivity calls wpc.hasVisibleNotPinnedActivity() rather than !wpc.inPinnedWindowingMode(), so the predicate was changed after this commit. The commit that changed it is not in the material.",
      "Per index search, ActivityTaskManagerService.hasActiveVisibleWindow still uses hasVisibleActivity, which counts pinned activities. Per the r17 code, it still backs the predicate used for FGS-start bound-by-foreground checks."
     ],
     "behavior_changes": [
      "An app whose only visible activity is pinned (PIP) no longer gets BAL_ALLOW_VISIBLE_WINDOW or BAL_ALLOW_FOREGROUND for non-FGS background activity starts. Without another exemption, the start is blocked (tests expect BAL_BLOCK).",
      "When balImproveRealCallerVisibilityCheck is enabled, non-app visible windows no longer satisfy the real-caller visible-window check. Tests now expect BAL_ALLOW_NON_APP_VISIBLE_WINDOW instead of BAL_ALLOW_VISIBLE_WINDOW.",
      "The caller visible-window verdict and the process foreground-task verdict now use the new two-argument BalVerdict constructor, which sets background=true (previously false). The caller message changes to 'callingUid has visible non-pinned window'.",
      "BalState.toString() output uses new field names. This affects dumps and logs.",
      "Google reverted the change once because of an app compatibility regression."
     ],
     "preconditions": [
      "The calling app or real calling app has a visible activity in pinned windowing mode (e.g. PIP) and attempts a background activity start.",
      "The app switch state is APP_SWITCH_ALLOW or APP_SWITCH_FG_ONLY (or the home app, for the real caller) on the caller and flag-enabled real-caller paths, and not APP_SWITCH_DISALLOW on the process path.",
      "The process path is reached through BackgroundActivityStartController.checkProcessAllowsBal -> WindowProcessController.areBackgroundActivityStartsAllowed (per index search)."
     ],
     "per_major": {
      "14": "Adapted fix 4ec3e4cea49d in android-security-14.0.0_r22. The inline callingUidHasAnyVisibleWindow and realCallingUidHasAnyVisibleWindow are now computed as hasVisibleNotPinnedActivity || hasNonAppVisibleWindow. It also adds a local ALWAYS_TRUE predicate and the inPinnedWindow gate (switched to the APP_SWITCH_DISALLOW comparison). 7/8 patched lines are present at r29.",
      "15": "Same fix 483fba0b4777 in r10. It was reverted in r12 (294a98e56fd5) and reapplied in r13 (b10fde7507d7). At r17 it is present with later changes: wpc.hasVisibleNotPinnedActivity() and a NotPinned predicate for the bound-by-foreground check. 7/8 patched lines are present at r17.",
      "16": "Adapted fix b96d7a515e9e in android-security-16.0.0_r1. It adds only the NotPinned fields, because the VisibleActivity and NonAppVisibleWindow fields already existed, and it changes the caller and real-caller checks. The gate uses checkConfiguration.isCheckingForFgsStart together with checkOtherExemptions. 8/8 patched lines are present at r8.",
      "17": "The resolver lists 58822cb0d7e4 in android-17.0.0_r1, which is a revert of an older-layout version of the fix (ActivityStarter-based BAL code). 0/8 patched lines are present at android-security-17.0.0_r1. The material does not show how or whether 17 carries the protection."
     },
     "backport_notes": [
      "On 15, do not pick r10 alone because it was reverted. Use the r13 reapplication (b10fde7507d7). Also check the later changes visible at r17: WindowProcessController.hasVisibleNotPinnedActivity, and the uidHasActiveVisibleNotPinnedWindowPredicate constructor argument in BackgroundLaunchProcessController plus its use in isBoundByForegroundUid.",
      "The test mocks need mService.mVisibleActivityProcessTracker injected via setViaReflection. The BackgroundLaunchProcessController test call sites need the new inPinnedWindow argument."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which commit(s) introduced WindowProcessController.hasVisibleNotPinnedActivity and the NotPinned bound-by-foreground predicate seen at r17.",
      "What the compatibility regression behind the revert was, and whether the reapplied version differs functionally from r10 (the diffs shown are identical).",
      "How Android 17 handles pinned windows, since the listed 17 commit is a revert and the patched region is absent.",
      "How the bulletin's DoS classification maps to this BAL change."
     ],
     "meta": {
      "generated": "2026-10-04",
      "author_model": "claude-opus-5-5",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48595": {
     "cve": "CVE-2025-48595",
     "summary": "The fix is a third-party SQLite version bump: Android 15 installed the SQLite 3.44.5 sources (later reverted), Android 16 selects 3.44.5 (replacing 3440300) through the RELEASE_PACKAGE_LIBSQLITE3 build flag, and Android 14 updates its single dist/ tree from SQLite 3.39.2 to 3.39.6. The material identifies no specific code-level flaw.",
     "component": "platform/external/sqlite (libsqlite3 amalgamation under dist/) and platform/build/release (RELEASE_PACKAGE_LIBSQLITE3 flag_values)",
     "weakness": "unknown",
     "root_cause": "The material shows only wholesale SQLite upgrades (3440300 to 3440500 via the flag on 16, 3.39.2 to 3.39.6 on 14), not a targeted change, so the vulnerable SQLite code is not identified. The Android 14 diff of dist/orig/sqlite3.c contains many upstream changes between 3.39.2 and 3.39.6. Examples: a new bounds check in modifyPagePointer that returns SQLITE_CORRUPT_PAGE when pCell+4 exceeds usableSize; duplicate-page handling in sqlite3PcacheMove and pcache1Rekey; a rewrite of the sibling page reordering in balance_nonroot that drops aPgOrder/aPgFlags; removal of NEVER() around a corruption check in defragmentPage; and a journalOff reset in pager_open_journal. The material does not say which of these corresponds to this CVE.",
     "fix_analysis": [
      "Android 15, platform/external/sqlite e0f74ada1551 (android-security-15.0.0_r12): adds a new source tree dist/sqlite-autoconf-3440500/ for SQLite 3.44.5 (orig/ and patched sqlite3.c, shell.c, sqlite3.h, sqlite3ext.h, plus Android.patch, METADATA, README.version). The commit message says it does not change which version is built; the RELEASE_PACKAGE_LIBSQLITE3 trunk-stable flag controls the selection.",
      "Android 15, platform/build/release df01bf9a7498 (android-security-15.0.0_r12): deletes flag_values/trunk_staging/RELEASE_PACKAGE_LIBSQLITE3.textproto (value \"3440300\"). The commit says these directories are not used in 24Q3 and the removal avoids confusion, and that a cuttlefish test image was verified to run SQLite 3.44.5. The material does not show the change that actually selects the version for 24Q3.",
      "Android 15, platform/build/release 90a5586f0d3a (android-security-15.0.0_r13): reverts submission 35204825-24Q3-sqlite-3.44.5 because of a performance regression and restores the trunk_staging flag file with value \"3440300\". At android-security-15.0.0_r17 the code context shows the dist/sqlite-autoconf-3440500/ files absent and the flag file holding \"3440300\".",
      "Android 16, platform/build/release b11607ea3ec7 (android-security-16.0.0_r3): changes flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto string_value from \"3440300\" to \"3440500\".",
      "Android 14, platform/external/sqlite 2abdb383b72c (android-security-14.0.0_r24): updates dist/orig/sqlite3.c, dist/orig/shell.c, dist/Android.patch, README.version and METADATA in place from 3.39.2 to 3.39.6 (SQLITE_VERSION_NUMBER 3039006, source id 2025-08-01), and adds new LICENSE and NOTICE files. Android.patch is regenerated so the Android customizations are carried forward: fdsan tagging, unixLogError on fstat failures, SQLITE_CORRUPT_BKPT for an unsupported file format, fts3_tokenizer disabled, optional fts1/fts2 aliases, and the Android functions registered in shell.c.",
      "Android 17, platform/external/sqlite e13117e12f88 (android-17.0.0_r1): removes the obsolete dist/sqlite-autoconf-3500100/ directory."
     ],
     "completeness": [
      "The fix replaces the whole SQLite amalgamation instead of patching a function, so it covers whatever upstream fixed between the old and new versions. The material does not isolate the relevant change, so the code cannot show whether the specific flaw is covered.",
      "Android 15: the submission was reverted in android-security-15.0.0_r13 because of a performance regression. The code context at r17 shows the 3.44.5 tree absent and the flag at 3440300. The per-major facts still list 15 as resolved, with 2/2 lines of the patched region present at r17, so the material conflicts on whether 15 is patched.",
      "Android 16: the ap3a flag selects 3440500, and the value is still present at android-security-16.0.0_r8. Other flag_values directories are not shown.",
      "Android 14 stays on the 3.39.x line (3.39.6), so it relies on upstream having included the relevant fix in 3.39.6. The material does not confirm this independently."
     ],
     "behavior_changes": [
      "The reported SQLite version changes: 3440500 selected on Android 16 (ap3a), and 3.39.6 on Android 14 (SQLITE_VERSION_NUMBER 3039006).",
      "Android 14 inherits all upstream changes from 3.39.2 to 3.39.6 that are visible in the diff. For example: modifyPagePointer now returns SQLITE_CORRUPT_PAGE for an out-of-range cell pointer; the defragmentPage overflow check is no longer marked NEVER(); sqlite3_strglob and sqlite3_strlike tolerate NULL arguments; unixGetTempname and the Windows temp-directory paths take SQLITE_MUTEX_STATIC_TEMPDIR; OP_SeekScan gains a P5 operand; and sqlite3PcacheMove drops a page already cached at the destination page number.",
      "Android 15 build/release: the trunk_staging flag value file was removed and then restored."
     ],
     "preconditions": [
      "unknown: the material does not identify the vulnerable SQLite code path or how it is reached. It only shows library version upgrades."
     ],
     "per_major": {
      "14": "Adapted fix: platform/external/sqlite 2abdb383b72c (android-security-14.0.0_r24) updates the single dist/ tree in place from SQLite 3.39.2 to 3.39.6. At android-security-14.0.0_r29 only 3/8 lines of the patched region are reported present, so the material does not fully confirm the current state.",
      "15": "Listed as resolved (bug_id, android-security-15.0.0_r12): the SQLite 3.44.5 sources were installed (platform/external/sqlite e0f74ada1551) and the trunk_staging flag value was removed (platform/build/release df01bf9a7498). platform/build/release 90a5586f0d3a in android-security-15.0.0_r13 reverted the submission because of a performance regression. At r17 the code context shows the 3440500 directory absent and RELEASE_PACKAGE_LIBSQLITE3 at 3440300, while the resolver reports 2/2 patched-region lines present. The effective state on 15 is unclear.",
      "16": "Same fix through flag selection: platform/build/release b11607ea3ec7 (android-security-16.0.0_r3) sets flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto to 3440500. The value is still present at android-security-16.0.0_r8.",
      "17": "Resolved in base (android-17.0.0_r1). The fix commit listed is platform/external/sqlite e13117e12f88, which removes the obsolete dist/sqlite-autoconf-3500100/ directory; those files are absent at android-security-17.0.0_r1. The material does not show which SQLite version 17 builds."
     },
     "backport_notes": [
      "On branches with the versioned layout (dist/sqlite-autoconf-<ver>/ selected by RELEASE_PACKAGE_LIBSQLITE3), a backport needs both the external/sqlite source install (e0f74ada1551) and a build/release flag_values change that selects 3440500 for the relevant release config, as 16 did for ap3a in b11607ea3ec7. The 15 submission was reverted for a performance regression, so plan to re-check performance.",
      "On branches with a single dist/ tree (the Android 14 layout), apply the upstream update in place and regenerate dist/Android.patch so the Android customizations in sqlite3.c and shell.c are kept."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which SQLite function or upstream change actually fixes this CVE. The material only shows version upgrades.",
      "How 24Q3 (Android 15) selected 3.44.5 before the revert. The only build/release change shown removes a trunk_staging value that the commit calls unused.",
      "Whether Android 15 is effectively patched after the r13 revert. The resolver reports 15 as resolved, but the 3440500 files are absent at r17.",
      "Which SQLite version Android 17 builds (the 15 commit only states a plan for main to adopt 3.50.x).",
      "Why only 3/8 lines of the Android 14 patched region are present at android-security-14.0.0_r29.",
      "The attack surface and trigger conditions for the EoP. The bulletin gives only severity and type."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48612": {
     "cve": "CVE-2025-48612",
     "summary": "The NFC default payment app picker in Settings (DefaultPaymentSettings) no longer parses the selected preference key string into a ComponentName and user id. It now looks the key up in a map of PaymentAppInfo objects enumerated from PaymentBackend, so only a known candidate payment app can be set as default.",
     "component": "platform/packages/apps/Settings, NFC default payment app selector (src/com/android/settings/nfc)",
     "weakness": "Improper input validation / trust of a string key (string injection): the preference key was parsed with split(\" \"), ComponentName.unflattenFromString and Integer.parseInt, and the results were passed directly to setDefaultPaymentApp.",
     "root_cause": "DefaultPaymentSettings.setDefaultKey(String key), an override from DefaultAppPickerFragment, treated the key as \"flattenedComponentName userId\". It split the key on a space, converted the two parts to a ComponentName and an int, and passed them straight to PaymentBackend.setDefaultPaymentApp(). The fragment did not check that the resulting component/user pair matched one of the payment apps enumerated by PaymentBackend.getPaymentAppInfos(). So a key not produced by getDefaultKey() would be acted on by Settings with whatever component and user id it encoded.",
     "fix_analysis": [
      "src/com/android/settings/nfc/PaymentBackend.java: PaymentAppInfo gains getKey() (returns Integer.toString(hashCode())), plus hashCode() and equals() defined over (componentName, userHandle). The key is now an opaque identifier derived from the object, not a parseable encoding.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java:onAttach: mAppInfos changes from List<PaymentAppInfo> to Map<String, PaymentAppInfo>, built from PaymentBackend.getPaymentAppInfos() with Collectors.toMap(PaymentAppInfo::getKey, Function.identity()).",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java:getDefaultKey: returns defaultAppInfo.getKey() instead of the 'flattenToString() + \" \" + userId' string.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java:setDefaultKey: looks the key up in mAppInfos. If the key is absent it returns true without doing anything. Otherwise it calls mPaymentBackend.setDefaultPaymentApp(appInfo.componentName, appInfo.userHandle.getIdentifier()) with the stored values that the backend enumerated. The ComponentName import and all string parsing are removed.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java:getCandidates: iterates mAppInfos.values() instead of the list. Candidate construction is otherwise unchanged.",
      "On Android 15 the fix landed as adac3d79c0f3 (android-security-15.0.0_r12). It was reverted by 1db9b27682b8 ('update patch to fix regression') and relanded by 7791c4097d93 (Revert^2), both in android-security-15.0.0_r16. The reland hunks are identical to the original, although the blob indices differ (d75844bb0ff..b5057ed64f2 versus ddac08beb00..440f09e3d9b). The final state at android-security-15.0.0_r17 contains the fixed code (8/8 lines present)."
     ],
     "completeness": [
      "The fix closes the only parsing site shown. setDefaultKey can now select only a (componentName, userHandle) pair that PaymentBackend.getPaymentAppInfos() returned at onAttach time, and keys that do not match are ignored.",
      "PaymentBackend.setDefaultPaymentApp itself is not changed; all of the validation is in the picker fragment. Any other caller of setDefaultPaymentApp (none is shown in the material) is not covered by this change.",
      "Keys are the decimal string of Objects.hash(componentName, userHandle). The map lookup can only return an entry that was enumerated, so a crafted key cannot widen the set of selectable targets. Collectors.toMap without a merge function rejects duplicate keys, so duplicate or hash-colliding entries from getPaymentAppInfos() would make onAttach throw instead of being handled."
     ],
     "behavior_changes": [
      "Preference keys for the payment app radio buttons change from 'component userId' strings to opaque hash strings.",
      "setDefaultKey with an unknown or malformed key now returns true and does nothing. Before, it called setDefaultPaymentApp, or Integer.parseInt threw on a non-numeric user id.",
      "onAttach may now throw an IllegalStateException if Collectors.toMap sees duplicate keys (duplicate or hash-colliding PaymentAppInfo entries)."
     ],
     "preconditions": [
      "The DefaultPaymentSettings fragment (the NFC default payment app picker in Settings) must be instantiated, and its setDefaultKey() must be called with a key not generated by getDefaultKey()/getCandidates(). The material does not show how such a key reached setDefaultKey in the vulnerable version."
     ],
     "per_major": {
      "14": "same fix: c90fee1f1ea4 in android-security-14.0.0_r24, with a diff identical to the Android 15 commit; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: adac3d79c0f3 in android-security-15.0.0_r12 (described as a backwards compatible port of ag/35084316). It was then reverted (1db9b27682b8) and relanded with identical hunks (7791c4097d93) in android-security-15.0.0_r16; 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: aa744e8988f0 in android-security-16.0.0_r3, with an identical diff; 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix, present in the base release: 1553d28f0f57 in android-17.0.0_r1, with an identical diff; 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Take both files together: DefaultPaymentSettings depends on the new PaymentAppInfo.getKey()/hashCode()/equals() in PaymentBackend.java. The same two-file diff was used on 14, 15, 16 and 17. For a Settings fork, apply the final relanded state; you do not need to replay the Android 15 revert/reland pair.",
      "The Android 15 revert cites a regression, but the reland hunks are identical to the original. If a separate regression fix exists, it is not in the material, so test the picker on a device with multiple payment apps and work/managed profiles after backporting."
     ],
     "confidence": "high",
     "unknowns": [
      "How an untrusted key reached setDefaultKey in the vulnerable code. The material shows only the fragment, not the DefaultAppPickerFragment selection path or any external entry point.",
      "Whether PaymentBackend.setDefaultPaymentApp or code further downstream validates the component or user.",
      "The nature of the regression that prompted the Android 15 revert, and whether any other change accompanied the reland. The reland hunks shown are identical to the original.",
      "The content of ag/35084316, the original main-branch change that this commit ports.",
      "Whether mAppInfos is refreshed when the set of payment apps changes while the fragment is shown.",
      "What device features (for example NFC card emulation) gate whether the picker can be reached."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48615": {
     "cve": "CVE-2025-48615",
     "summary": "Adds a 1024-character bound (package name + class name + 1) on media button receiver component names in MediaButtonReceiverHolder: commit 1 covers components resolved from a PendingIntent, and commit 2 covers ComponentNames passed directly as broadcast receivers.",
     "component": "platform/frameworks/base, system_server media session service (services/core/java/com/android/server/media/MediaButtonReceiverHolder.java)",
     "weakness": "Missing length/bounds validation on app-supplied component names (improper input validation)",
     "root_cause": "MediaButtonReceiverHolder accepted component names of unbounded length from the app that owns a MediaSession. In getComponentName(PendingIntent, componentType), a resolved component whose package matched the PendingIntent creator package was turned into a ComponentName with no size check. In create(int userId, ComponentName broadcastReceiver), the caller-supplied ComponentName was stored as-is. The unflattenFromString javadoc says the receiver's component name is kept in persistent storage across boot, so overly long names reached system_server state unchecked. The first commit message notes that the case cannot be tested on main because PackageManager prevents it.",
     "fix_analysis": [
      "Commit 1 (8f8c16cee29b, 'Check length of MBR component name properties'), MediaButtonReceiverHolder.java: adds private static final int MAX_COMPONENT_NAME_LENGTH = 1024. In getComponentName(PendingIntent, componentType), inside the loop over resolved components and after the existing creator-package and non-null checks, it computes componentInfo.packageName.length() + componentInfo.name.length() + 1. If the result is greater than 1024, it logs a warning ('detected and ignored component name with overly long package or name') and continues to the next candidate instead of returning a ComponentName.",
      "Commit 2 (595ed9805382, 'MediaSession: Enforce max name length on broadcast receivers too'), MediaButtonReceiverHolder.java: adds private static boolean componentNameTooLong(ComponentName), which applies the same package + class + 1 > MAX_COMPONENT_NAME_LENGTH test using getPackageName()/getClassName(). create(int userId, ComponentName broadcastReceiver) now throws IllegalArgumentException(\"receiver name too long\") before constructing the holder if the check fails. getComponentName(PendingIntent, componentType) is refactored to build the ComponentName first and call componentNameTooLong on it, keeping commit 1's skip-and-log behavior.",
      "Commit 2's message says commit 1 only covered names set via MediaSession.setMediaButtonReceiver(PendingIntent), not MediaSession.setMediaButtonBroadcastReceiver(ComponentName) (b/433250316#comment72)."
     ],
     "completeness": [
      "Covered after both commits: the PendingIntent-resolved component path (getComponentName) and the direct ComponentName path (create(int, ComponentName)). Commit 1 alone leaves the ComponentName path unchecked.",
      "Not touched by either commit: unflattenFromString(Context, String). In the r17 context (lines 85-94) it rebuilds a ComponentName from the persisted string via ComponentName.unflattenFromString(tokens[0]) after only empty and token-count checks. The rest of the function is not shown, so whether names read back from storage are length-checked is unknown.",
      "The limit is applied to package + class + 1. The reasoning behind the 1024 threshold is not in the material.",
      "In the PendingIntent path an over-long component is skipped, not rejected. If no other candidate qualifies, getComponentName returns null and create(int, PendingIntent, String) falls through to the 'Unresolvable implicit intent' branch. That branch builds a holder with the session package name and COMPONENT_TYPE_INVALID (context lines 125-134), so the PendingIntent is still kept."
     ],
     "behavior_changes": [
      "Components resolved from a media button receiver PendingIntent whose package + class name length exceeds 1024 are ignored with a Log.w. If no other candidate qualifies, the holder falls back to the unresolvable-intent form (session package name, COMPONENT_TYPE_INVALID).",
      "MediaButtonReceiverHolder.create(int, ComponentName) now throws IllegalArgumentException(\"receiver name too long\") for over-long broadcast receiver ComponentNames. Before the fix it always succeeded.",
      "New constant MAX_COMPONENT_NAME_LENGTH = 1024 and new helper componentNameTooLong(ComponentName)."
     ],
     "preconditions": [
      "An app that owns a MediaSession sets a media button receiver in one of two ways. A PendingIntent goes through getComponentName in the holder; a broadcast receiver ComponentName goes through create(int, ComponentName). The commit messages map these to MediaSession.setMediaButtonReceiver(PendingIntent) and MediaSession.setMediaButtonBroadcastReceiver(ComponentName).",
      "For the PendingIntent path, the resolved component must belong to the PendingIntent creator package and have a combined package + class name longer than 1024 characters.",
      "Commit 1 states this cannot be tested on main because PackageManager prevents it. Whether PackageManager on affected releases allows such component names is not shown."
     ],
     "per_major": {
      "14": "Commit 1 ('Check length of MBR component name properties', a1bbb371980e) applied unchanged in android-security-14.0.0_r24; 8/8 lines of the patched region present at android-security-14.0.0_r29. The material does not show whether commit 2 (broadcast receiver check) is on Android 14.",
      "15": "Both commits applied unchanged: commit 1 (8f8c16cee29b) in android-security-15.0.0_r12 and commit 2 (595ed9805382) in android-security-15.0.0_r13. The context at android-security-15.0.0_r17 shows MAX_COMPONENT_NAME_LENGTH and the throwing create(int, ComponentName); 8/8 lines of the patched region are present.",
      "16": "Commit 1 (a5795fc0cf1f) applied unchanged in android-security-16.0.0_r3; 8/8 lines present at android-security-16.0.0_r8. The material does not show whether commit 2 is on Android 16.",
      "17": "Commit 2 (013bb57034ee) is in android-17.0.0_r1. Its diff base is the post-commit-1 file (index ffa2bcbd6fd2), so commit 1 was already present. 6/8 lines of the patched region are present at android-security-17.0.0_r1; the material does not explain the difference."
     },
     "backport_notes": [
      "Apply commit 1 before commit 2: commit 2's diff is based on commit 1's file state (commit 1 introduces MAX_COMPONENT_NAME_LENGTH, and commit 2 replaces its inline length computation with componentNameTooLong). Both are confined to MediaButtonReceiverHolder.java and add no imports (Log and TextUtils are already imported).",
      "On Android 14 and 16 branches, check whether commit 2 is present: look for componentNameTooLong and the IllegalArgumentException in create(int, ComponentName). The material only shows commit 1 on those branches."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete consequence of an over-long component name (what failed in system_server or persistence) is not shown; the bulletin only rates it High EoP.",
      "Whether commit 2 was applied to the Android 14 and 16 security branches.",
      "Why only 6/8 lines of the patched region are present on Android 17.",
      "How the IllegalArgumentException from create(int, ComponentName) propagates to the calling MediaSession API (the caller is not in the material).",
      "Whether PackageManager on affected releases permits component names long enough to hit the new limit.",
      "Whether unflattenFromString validates name length after line 94."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48616": {
     "cve": "CVE-2025-48616",
     "summary": "KeyguardViewMediator now asks KeyguardUpdateMonitor.isUserInLockdown (the strong auth tracker that fires onStrongAuthStateChanged) instead of LockPatternUtils.isUserInLockdown at all three lockdown checks, so the keyguard shows on lockdown even when it is externally disabled (e.g. app pinning).",
     "component": "platform/frameworks/base, SystemUI keyguard (KeyguardViewMediator)",
     "weakness": "Inconsistent state source: the lockdown decision was read from LockPatternUtils, which can be out of sync with the KeyguardUpdateMonitor strong auth tracker that triggered the callback. As a result the lock screen was sometimes not shown on lockdown.",
     "root_cause": "KeyguardViewMediator receives onStrongAuthStateChanged from KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged (per index search). It then decided whether to force the keyguard by calling mLockPatternUtils.isUserInLockdown(). The commit message says LockPatternUtils and the KeyguardUpdateMonitor StrongAuthTracker can be out of sync, so at callback time LockPatternUtils could still report \"not in lockdown\". While that was the case, three things went wrong: the callback did not call doKeyguardLocked(), lockdown did not override setKeyguardEnabled(false), and doKeyguardLocked() skipped showing because !mExternallyEnabled was true and the stale lockdown check was false. The commit's manual test (pin an app, lock down the device, check that the keyguard always shows) matches this failure to show the lock screen.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java, the KeyguardUpdateMonitorCallback implementation's onStrongAuthStateChanged: replaces mLockPatternUtils.isUserInLockdown(selectedUser) with mUpdateMonitor.isUserInLockdown(selectedUser) before calling doKeyguardLocked(null). The check now uses the same monitor that sent the callback.",
      "KeyguardViewMediator.java:setKeyguardEnabled: the early return logged as 'keyguardEnabled(false) overridden by user lockdown' now uses mUpdateMonitor.isUserInLockdown.",
      "KeyguardViewMediator.java:doKeyguardLocked: the '!mExternallyEnabled && !isUserInLockdown' skip condition now uses mUpdateMonitor.isUserInLockdown, so lockdown forces the keyguard to show even when it is externally disabled.",
      "packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java:onStrongAuthStateChanged: the javadoc now tells implementers to treat KeyguardUpdateMonitor (e.g. isUserInLockdown) as the source of truth. This is a comment-only change.",
      "packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java: the two lockdown tests now stub mUpdateMonitor.isUserInLockdown instead of mLockPatternUtils.isUserInLockdown."
     ],
     "completeness": [
      "All three isUserInLockdown call sites in KeyguardViewMediator shown in the diff are converted. No mLockPatternUtils.isUserInLockdown remains in the patched regions.",
      "The fix does not change LockPatternUtils or the StrongAuthTracker, so the underlying desynchronization remains. It only switches this consumer to the KeyguardUpdateMonitor source. The material shows no other SystemUI users of LockPatternUtils.isUserInLockdown, and this commit touches none.",
      "The other mLockPatternUtils uses in KeyguardViewMediator (isSecure and getDevicePolicyManager in the biometric callbacks) are not lockdown checks and are unchanged.",
      "The code context at android-security-15.0.0_r17 shows onStrongAuthStateChanged using mUpdateMonitor.isUserInLockdown. The per-major facts report 6/8 lines of the patched region present at that tag."
     ],
     "behavior_changes": [
      "If lockdown happens while the keyguard is externally disabled (mExternallyEnabled false), onStrongAuthStateChanged now shows the keyguard based on KeyguardUpdateMonitor state.",
      "setKeyguardEnabled(false) is now refused (logged as 'keyguardEnabled(false) overridden by user lockdown') whenever KeyguardUpdateMonitor reports lockdown, instead of when LockPatternUtils does.",
      "No new APIs, error codes or flags (Flag: EXEMPT CVE_FIX). The only change is which object answers the lockdown query."
     ],
     "preconditions": [
      "The keyguard is externally disabled through KeyguardViewMediator.setKeyguardEnabled(false), for example by pinning an app (per the commit message test steps).",
      "The user triggers lockdown, so KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged calls onStrongAuthStateChanged on registered callbacks (per index search).",
      "LockPatternUtils.isUserInLockdown still returns false at that moment (the out-of-sync state described in the commit message)."
     ],
     "per_major": {
      "14": "adapted fix: commit 991998d5464f in android-security-14.0.0_r24. Same logic, but the user id comes from KeyguardUpdateMonitor.getCurrentUser() instead of mSelectedUserInteractor.getSelectedUserId(). At android-security-14.0.0_r29, 6/8 lines of the patched region are present.",
      "15": "same fix: commit 9adff3df0285 in android-security-15.0.0_r12 (cherry-pick of 5c25e76eb886). At android-security-15.0.0_r17, 6/8 lines of the patched region are present.",
      "16": "same fix: commit aa9fbb1b74eb in android-security-16.0.0_r3, with the same code changes as 15 (mSelectedUserInteractor variant). At android-security-16.0.0_r8, 6/8 lines of the patched region are present.",
      "17": "resolved in base at android-17.0.0_r1 via commit 52a6d6dfda83 (RESTRICT AUTOMERGE). The diff shown uses the KeyguardUpdateMonitor.getCurrentUser() form. At android-security-17.0.0_r1, 6/8 lines of the patched region are present."
     },
     "backport_notes": [
      "One self-contained commit. Across branches the only adaptation is how the selected user id is obtained: KeyguardUpdateMonitor.getCurrentUser() on 14 and 17, mSelectedUserInteractor.getSelectedUserId() on 15 and 16. The fix relies on KeyguardUpdateMonitor.isUserInLockdown(int) and on mUpdateMonitor being available in KeyguardViewMediator, which the diffs in all shown branches use.",
      "Forks that added more mLockPatternUtils.isUserInLockdown calls in KeyguardViewMediator or in other keyguard callbacks should convert them to mUpdateMonitor.isUserInLockdown as well, as the updated javadoc advises."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show why LockPatternUtils and the StrongAuthTracker get out of sync (ordering, caching or IPC timing).",
      "The material does not say what information is disclosed (bulletin type ID). It only shows the keyguard failing to appear on lockdown.",
      "The Android 17 diff uses the same KeyguardUpdateMonitor.getCurrentUser() form as Android 14, and its KeyguardUpdateMonitorCallback.java index hashes match Android 14. The material does not explain why.",
      "Which 2 of the 8 patched-region lines are missing at each latest security tag is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48648": {
     "cve": "CVE-2025-48648",
     "summary": "NotificationManagerService stops using PackageManagerInternal.isSameApp (which, per the commit message, has an SDK sandbox exception) for its package-ownership check and compares app ids directly via UserHandle.isSameApp against getPackageUid, rejects SDK sandbox uids in areNotificationsEnabledForPackage, and adjusts dependent call sites.",
     "component": "platform/frameworks/base, NotificationManagerService (services/core/java/com/android/server/notification)",
     "weakness": "Improper authorization: the \"caller is same app as package\" check delegated to PackageManagerInternal.isSameApp, which the commit message says contains an SDK sandbox exception, so a sandbox uid could be treated as the client app's package owner.",
     "root_cause": "The package-ownership check helper in NotificationManagerService.java (the hunk with the ROOT_UID/ROOT_PKG bypass followed by SecurityException \"Package ... is not owned by uid ...\"; method name not shown in the hunk) enforced ownership with mPackageManagerInternal.isSameApp(pkg, uid, userId). Per the commit message (\"Only check uid, without a sandbox sdk exception\"), that helper accepts an SDK sandbox uid as the same app as its client package, so this identity gate passed for sandbox callers. Two further sites (the Wellbeing check for restricted AutomaticZenRule types and getEffectsSuppressor) called mPackageManagerInternal.isSameApp directly, and areNotificationsEnabledForPackage had no sandbox handling. On Android 15/16 resolveNotificationUid also mapped USER_ALL to USER_SYSTEM, which the fix changes to the calling uid's user.",
     "fix_analysis": [
      "NotificationManagerService.java (package-ownership check helper) - replaces `mPackageManagerInternal.isSameApp(pkg, uid, userId)` with `UserHandle.isSameApp(uid, mPackageManagerInternal.getPackageUid(pkg, 0L, userId))`: the package's uid is resolved for the given user and compared by app id with the caller's uid, with no sandbox special case. If the lookup returns INVALID_UID (as tests stub for missing packages) the comparison fails and SecurityException is thrown. The ROOT_UID/ROOT_PKG bypass above it is unchanged.",
      "NotificationManagerService.java:areNotificationsEnabledForPackage (binder) - new early `if (Process.isSdkSandboxUid(uid)) return false;` before enforceSystemOrSystemUIOrSamePackage, so sandbox uids are reported as notifications-disabled without reaching the ownership check.",
      "NotificationManagerService.java:getConversationNotificationChannel - condition reordered to `isCallerSystemOrSystemUiOrShell() || canNotifyAsPackage(callingPkg, targetPkg, userId)`, so system/SystemUI/shell callers short-circuit before canNotifyAsPackage is evaluated (its body is not in the material).",
      "NotificationManagerService.java (Wellbeing restricted-rule-type check in the AutomaticZenRule path) - `mPackageManagerInternal.isSameApp(wellbeingPackage, uid, userId)` replaced with `isCallerSameApp(wellbeingPackage, uid, userId)` (Android 15/16 only; the body of isCallerSameApp is not in the material).",
      "NotificationManagerService.java:getEffectsSuppressor - `mPackageManagerInternal.isSameApp(suppressor.getPackageName(), Binder.getCallingUid(), ...)` replaced with `isCallerSameApp(...)` (Android 15/16 only).",
      "NotificationManagerService.java:resolveNotificationUid - when userId == USER_ALL it now uses `UserHandle.getUserId(callingUid)` instead of USER_SYSTEM, so the subsequent isCallerSameApp lookup is done in the caller's user (Android 15/16 only).",
      "Test changes: UiServiceTestCase gains per-user UID_* fields and getPackageUid stubs on the shared mPmi mock; NotificationManagerServiceTest drops its own PackageManagerInternal mock and the overly broad isSameApp answer (b/317957802), and tests now stub getPackageUid explicitly; PreferencesHelperTest expected XML is split into per-package fragments that interpolate the UID_* fields."
     ],
     "completeness": [
      "The package-ownership check helper is fixed, so every path that goes through it gets the uid-only comparison; whether canNotifyAsPackage and isCallerSameApp delegate to it is not shown.",
      "On Android 15/16 the two direct mPackageManagerInternal.isSameApp call sites visible in the diff (Wellbeing check, getEffectsSuppressor) are redirected to isCallerSameApp; whether other direct isSameApp uses remain in NotificationManagerService or sibling services is not shown.",
      "The SDK sandbox early return is added only to the INotificationManager binder method areNotificationsEnabledForPackage; the NotificationManagerInternal.areNotificationsEnabledForPackage path used by ServiceRecord (per index search) is not shown as changed, and its implementation is not in the material.",
      "The Android 14 and Android 17 diffs shown contain only the areNotificationsEnabledForPackage, getConversationNotificationChannel reorder, helper rename and ownership-check hunks; the Wellbeing, getEffectsSuppressor and resolveNotificationUid hunks are absent there (whether those code sites exist in those branches is not shown)."
     ],
     "behavior_changes": [
      "areNotificationsEnabledForPackage returns false for any SDK sandbox uid, before any permission or ownership enforcement.",
      "The ownership check now throws SecurityException unless UserHandle.isSameApp(uid, getPackageUid(pkg, 0, userId)) holds; per the commit message, sandbox uids no longer pass as their client app.",
      "getConversationNotificationChannel: system/SystemUI/shell callers no longer have canNotifyAsPackage evaluated; non-privileged callers must pass canNotifyAsPackage.",
      "resolveNotificationUid (Android 15/16): a USER_ALL notification is resolved against the calling uid's user rather than USER_SYSTEM.",
      "Test setup is stricter: isSameApp is no longer universally mocked true for mUid; tests must stub getPackageUid per package/user."
     ],
     "preconditions": [
      "Caller reaches a NotificationManagerService binder API whose authorization relies on the ownership check or the sites changed here (e.g. getConversationNotificationChannel via NotificationManager.getNotificationChannel per index search, areNotificationsEnabledForPackage via Settings NotificationBackend.getNotificationsBanned per index search).",
      "Per the commit message, the caller is an SDK sandbox uid for which PackageManagerInternal.isSameApp applied its sandbox exception; the exact API sequence and the DoS effect rated High by the bulletin are not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix: 699e4677367e in android-security-14.0.0_r25; same areNotificationsEnabledForPackage sandbox return, getConversationNotificationChannel reorder (with rename isCallerIsSystemOrSysemUiOrShell -> isCallerIsSystemOrSystemUiOrShell) and ownership-check change; no Wellbeing/getEffectsSuppressor/resolveNotificationUid hunks; test base class also gains mUid/mUserId/mPkg fields. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "reference fix: 3d502bcba495 in android-security-15.0.0_r13 (cherry-pick of f5605ae25b15), six service hunks plus test rework. 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: b88956f3198d in android-security-16.0.0_r4, same six service hunks with shifted line numbers. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "resolved in base (44ee96106f25 in android-17.0.0_r1); the diff provided has the Android 14 shape (helper rename, no Wellbeing/getEffectsSuppressor/resolveNotificationUid hunks) and adds compatibility-device-util-axt-minus-dexmaker to services/tests/uiservicestests/Android.bp. 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "When porting the ownership-check change on a 15/16-shaped tree, also port the resolveNotificationUid USER_ALL -> UserHandle.getUserId(callingUid) change and the redirection of the direct mPackageManagerInternal.isSameApp sites (Wellbeing check, getEffectsSuppressor) to isCallerSameApp, as the 15/16 commits do.",
      "The test rework depends on UiServiceTestCase owning the PackageManagerInternal mock (mPmi) with getPackageUid stubs, and on SystemUtil from compatibility-device-util (added to Android.bp in the 17 diff); forks with a different test base may need to adapt."
     ],
     "confidence": "medium",
     "unknowns": [
      "The name of the patched ownership-check method is not shown in the hunk (likely checkCallerIsSameApp).",
      "Implementation of isCallerSameApp and canNotifyAsPackage is not in the material; whether they delegate to the patched ownership check is inferred.",
      "The exact semantics of PackageManagerInternal.isSameApp's sandbox exception are only described by the commit message, not shown in code.",
      "The concrete DoS trigger and the API sequence behind the High rating are not shown.",
      "Whether NotificationManagerInternal.areNotificationsEnabledForPackage (used by ServiceRecord per index search) shares the fixed binder implementation.",
      "The Android 17 diff provided matches the Android 14 code shape (line numbers and helper rename), so whether it reflects the actual 17 tree or a material mismatch is unclear."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48649": {
     "cve": "CVE-2025-48649",
     "summary": "Threads a restorePregrantedPermissions flag through the clearApplicationUserData path (ActivityManager -> AMS -> PackageManagerService -> PermissionManager). An app that clears its own data through the public ActivityManager.clearApplicationUserData() no longer gets default-granted or role-granted runtime permissions re-granted. The restoring path stays available but now requires CLEAR_APP_USER_DATA.",
     "component": "platform/frameworks/base, ActivityManagerService / PackageManagerService / permission services (PermissionManagerServiceImpl and AppIdPermissionPolicy)",
     "weakness": "Improper privilege management: an unprivileged self-initiated data clear triggered a runtime permission reset that re-granted default/role pregranted permissions",
     "root_cause": "The public, no-arg ActivityManager.clearApplicationUserData() called IActivityManager.clearApplicationUserData for the caller's own package. AMS permitted the call through the own-uid exemption (applicationInfo.uid == uid) without requiring CLEAR_APP_USER_DATA. AMS then called IPackageManager.clearApplicationUserData -> PackageManagerService.clearApplicationUserDataLIF -> mPermissionManager.resetRuntimePermissions(pkg, userId). In PermissionManagerServiceImpl.resetRuntimePermissionsInternal, every permission flagged FLAG_PERMISSION_GRANTED_BY_DEFAULT or FLAG_PERMISSION_GRANTED_BY_ROLE was unconditionally re-granted via grantRuntimePermissionInternal as SYSTEM_UID (\"make sure it is\"). The Kotlin AppIdPermissionPolicy.resetRuntimePermissions likewise unconditionally ORed in RUNTIME_GRANTED for ROLE/PREGRANT flags. Because any app could reach this reset for its own package, an app could get pregranted permissions restored, including ones the user had revoked, just by clearing its own data.",
     "fix_analysis": [
      "core/java/android/app/ActivityManager.java: the public self-clear clearApplicationUserData() now calls a new private clearApplicationUserData(packageName, observer, restorePregrantedPermissions=false), which invokes the new binder method IActivityManager.clearApplicationUserDataWithoutPermissionReset. The hidden two-arg clearApplicationUserData(packageName, observer) passes true and keeps using IActivityManager.clearApplicationUserData.",
      "core/java/android/app/IActivityManager.aidl: adds clearApplicationUserDataWithoutPermissionReset(packageName, keepState, observer, userId).",
      "services/core/java/com/android/server/am/ActivityManagerService.java: the binder clearApplicationUserData delegates with restorePregrantedPermissions=true and clearApplicationUserDataWithoutPermissionReset delegates with false. Both end up in the private clearApplicationUserData(..., isRestore, observer, userId, restorePregrantedPermissions). The own-uid exemption is tightened to (applicationInfo.uid == uid && !restorePregrantedPermissions) || CLEAR_APP_USER_DATA granted, so a self-clear that would restore pregrants now requires CLEAR_APP_USER_DATA. The flag is forwarded to pm.clearApplicationUserData. LocalService.clearApplicationUserData (system-internal, isRestore path) passes true.",
      "core/java/android/content/pm/IPackageManager.aidl and ApplicationPackageManager.clearApplicationUserData: IPackageManager.clearApplicationUserData gains a boolean restorePregrantedPermissions parameter and keeps @EnforcePermission CLEAR_APP_USER_DATA. ApplicationPackageManager passes true. The abstract PackageManager.clearApplicationUserData gets @RequiresPermission(CLEAR_APP_USER_DATA), which is an annotation only.",
      "services/core/java/com/android/server/pm/PackageManagerService.java: the binder clearApplicationUserData and clearApplicationUserDataLIF accept the flag and pass it to mPermissionManager.resetRuntimePermissions(pkg, userId, restorePregrantedPermissions).",
      "services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java: resetRuntimePermissions and both resetRuntimePermissionsInternal overloads take restorePregrantedPermissions. In the per-package loop, the grantRuntimePermissionInternal call for FLAG_PERMISSION_GRANTED_BY_DEFAULT / FLAG_PERMISSION_GRANTED_BY_ROLE permissions now runs only when restorePregrantedPermissions is true. Otherwise that branch does nothing. resetRuntimePermissionsForUser and the uninstall-with-keep-pregrants path (~line 5037) pass true.",
      "services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt: resetRuntimePermissions(packageName, userId, restorePregrants) ORs in PermissionFlags.RUNTIME_GRANTED for ROLE/PREGRANT permissions only when restorePregrants is true. Otherwise it leaves newFlags unchanged for those permissions. onPackageUninstalled passes true. PermissionService.kt forwards the flag for the per-package reset and passes true in resetRuntimePermissionsForUser.",
      "Interface plumbing: PermissionManagerServiceInterface, PermissionManagerServiceInternal, PermissionManagerService (internal impl), PermissionManagerServiceLoggingDecorator, PermissionManagerServiceTracingDecorator, PermissionManagerServiceTestingShim and LegacyPermissionManagerService (which passes true) are updated for the new signature. The tests CrossUserPackageVisibilityTests and AppIdPermissionPolicyPermissionResetTest are updated to pass true."
     ],
     "completeness": [
      "Both permission backends are covered: the grant in the legacy PermissionManagerServiceImpl.resetRuntimePermissionsInternal and the RUNTIME_GRANTED restoration in the Kotlin AppIdPermissionPolicy.resetRuntimePermissions are both gated by the flag.",
      "The unprivileged entry point (public ActivityManager.clearApplicationUserData()) passes false. Several paths still restore pregrants: the hidden two-arg ActivityManager API, ApplicationPackageManager.clearApplicationUserData, AMS LocalService (restore path), LegacyPermissionManagerService, resetRuntimePermissionsForUser and the uninstall path. Through AMS and IPackageManager, restoring now requires CLEAR_APP_USER_DATA. The other restoring paths are system-internal.",
      "In PermissionService.kt the sibling call with(devicePolicy) { resetRuntimePermissions(packageName, userId) } is left without the new parameter. The material does not show what that policy does with pregranted state.",
      "The AMS block after pm.clearApplicationUserData ('Restore already established notification state and permission grants', appInfo != null) is not modified. The material does not show which grants it restores.",
      "With restorePregrantedPermissions=false, the legacy branch for GRANTED_BY_DEFAULT/GRANTED_BY_ROLE permissions neither grants nor revokes. User-revoked pregrants stay revoked and pregrants that are still granted stay granted."
     ],
     "behavior_changes": [
      "The public ActivityManager.clearApplicationUserData() (self-clear) no longer re-grants default-granted or role-granted runtime permissions. Their current grant state is left untouched.",
      "The hidden @UnsupportedAppUsage ActivityManager.clearApplicationUserData(packageName, observer) now goes through the restoring path. In the AMS permitted check, the own-uid exemption no longer covers that path, so CLEAR_APP_USER_DATA is required even when the target is the caller's own package.",
      "The IPackageManager.clearApplicationUserData binder signature changes (new boolean parameter) and IActivityManager gains clearApplicationUserDataWithoutPermissionReset. Direct IPackageManager callers must be updated (CrossUserPackageVisibilityTests was).",
      "The PermissionManagerServiceLoggingDecorator log line now includes shouldRestorePregrants."
     ],
     "preconditions": [
      "Before the fix: the caller is an installed, non-isolated app invoking ActivityManager.clearApplicationUserData() on its own package. No permission is required because of the own-uid exemption in AMS.",
      "The app holds runtime permissions flagged FLAG_PERMISSION_GRANTED_BY_DEFAULT or FLAG_PERMISSION_GRANTED_BY_ROLE (ROLE/PREGRANT in the Kotlin policy) whose grant state was later changed, for example revoked, and that are not SYSTEM/POLICY fixed (the Kotlin policy skips SYSTEM_OR_POLICY_FIXED_MASK).",
      "Per index search, Settings AppStorageSettings.initiateClearUserData and Car Settings StorageApplicationActionButtonsPreferenceController.initiateClearUserData call the two-arg ActivityManager.clearApplicationUserData(packageName, observer), which now uses the restoring path."
     ],
     "per_major": {
      "14": "Adapted fix (RESTRICT AUTOMERGE c1f49d2c2cac in android-security-14.0.0_r25). Same flag plumbing, with these differences: PackageManagerService uses enforceCallingOrSelfPermission instead of @EnforcePermission; AMS has no isRestore overload and no LocalService change in the diff; PermissionService.kt resetRuntimePermissions is a TODO stub, so AppIdPermissionPolicy.kt is not touched; TracingDecorator and TestingShim are not in the diff. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "Same fix (452f0beb15a9 in android-security-15.0.0_r13, cherry-pick of 8dba23c13100). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix (7b9a906980a3 in android-security-16.0.0_r4) with a minor adaptation in AppIdPermissionPolicy.kt (the condition also includes isSystemOrInstalled) and a differently named TestingShim parameter. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Resolved in base (23da448adbb0 in android-17.0.0_r1). 8/8 lines present at android-security-17.0.0_r1. The 17 diff in the material is nearly identical to the Android 14 diff: same RESTRICT AUTOMERGE title, same stubbed PermissionService.kt, mostly the same blob hashes. Only a few files (PackageManager.java, ActivityManagerService.java, PermissionManagerService.java) have different pre-image blobs, so the diff shown may not reflect the actual 17 tree."
     },
     "backport_notes": [
      "This is a cross-layer signature change. IPackageManager.aidl, IActivityManager.aidl, PermissionManagerServiceInterface and PermissionManagerServiceInternal all change, so every implementor and every direct IPackageManager caller must be updated in the same commit. On 14-style branches where PermissionService.kt resetRuntimePermissions is a TODO stub, AppIdPermissionPolicy.kt does not need changing (follow the 14 diff).",
      "The security logic sits in three places: the AMS permitted check (&& !restorePregrantedPermissions), the if (restorePregrantedPermissions) guard around grantRuntimePermissionInternal in PermissionManagerServiceImpl, and the restorePregrants conditional in AppIdPermissionPolicy.kt. Verify all three after merging."
     ],
     "confidence": "high",
     "unknowns": [
      "What AMS does when permitted is false (the exception/return path is outside the shown context).",
      "What the unchanged AMS post-clear block that restores 'notification state and permission grants' covers.",
      "Whether the devicePolicy.resetRuntimePermissions path in PermissionService.kt restores any pregranted state (its implementation is not in the material).",
      "The real content of the Android 17 commit 23da448adbb0, since the 17 diff in the material is nearly identical to the 14 diff.",
      "Whether the ActivityManagerTest named in the commit's Test: line was changed in this commit (no test diff for it is shown).",
      "Whether the Settings / Car Settings callers hold CLEAR_APP_USER_DATA (not shown in the material)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48652": {
     "cve": "CVE-2025-48652",
     "summary": "PackageInstaller's InstallStart no longer lets a caller that holds INSTALL_PACKAGES skip the unknown-sources user-restriction checks when it starts the installer with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent. The bypass now applies only to non-intent installs by INSTALL_PACKAGES holders and to privileged callers that set EXTRA_NOT_UNKNOWN_SOURCE.",
     "component": "platform/frameworks/base, packages/PackageInstaller (InstallStart activity, and the PIA v2 InstallRepository on 16/17)",
     "weakness": "Incorrect authorization: an over-broad trust condition caused the unknown-sources user restriction check to be skipped",
     "root_cause": "In InstallStart.onCreate, checkDevicePolicyRestrictions(isTrustedSource) decided which user restrictions to enforce. When isTrustedSource was true, only DISALLOW_INSTALL_APPS was checked. Otherwise the unknown-sources restrictions were checked as well. Judging from the removed Kotlin helper isInstallRequestFromTrustedSource on 16/17 and from the commit message, isTrustedSource was true when the caller was a privileged app that set EXTRA_NOT_UNKNOWN_SOURCE OR when the caller held INSTALL_PACKAGES. The INSTALL_PACKAGES condition did not look at how the installer was launched. As a result, an intent-driven install (ACTION_VIEW / ACTION_INSTALL_PACKAGE) from an INSTALL_PACKAGES holder skipped the unknown-sources user restrictions.",
     "fix_analysis": [
      "packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java:onCreate: stores intentAction = intent.getAction() and computes isIntentInstall = ACTION_VIEW or ACTION_INSTALL_PACKAGE.",
      "InstallStart.java:onCreate: computes bypassUnknownSourceRestrictions = (!isIntentInstall && isInstallPkgPermissionGranted) || isPrivilegedAndKnown from the existing locals isInstallPkgPermissionGranted and isPrivilegedAndKnown, and passes it to checkDevicePolicyRestrictions in place of isTrustedSource.",
      "InstallStart.java:checkDevicePolicyRestrictions: the parameter is renamed to bypassUnknownSourceRestrictions. When it is true, only DISALLOW_INSTALL_APPS is checked; when false, the unknown-sources restrictions are checked too.",
      "On Android 16 and 17 the commit also patches packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt. The isInstallRequestFromTrustedSource helper is inlined as isPrivilegedAndKnown and isInstallPkgPermissionGranted, with an INVALID_UID guard: new on 16 using callingUid, already present on 17 using originatingUid. isTrustedSource becomes their OR, and getDevicePolicyRestrictions receives the same bypassUnknownSourceRestrictions expression. The Android 15 commit does not touch this file.",
      "The commit is titled 'Reapply' and reverts the revert 29962260bd04. Its Cherrypick-From lines list 81f83fdb0944, 6e0b7c02e54a and 9d20d0ae8fef."
     ],
     "completeness": [
      "Covers the legacy InstallStart.java path. Intent installs (ACTION_VIEW, ACTION_INSTALL_PACKAGE) now bypass the unknown-sources restrictions only for privileged callers that set EXTRA_NOT_UNKNOWN_SOURCE.",
      "Non-intent installs by INSTALL_PACKAGES holders keep the bypass. The commit message says this is intended.",
      "On Android 15 only InstallStart.java is patched. The code at android-security-15.0.0_r17 shows a usePiaV2() branch that forwards to InstallLaunch before the patched logic runs. The Android 15 commit does not modify v2/model/InstallRepository.kt, while the 16 and 17 commits do. The material does not show whether PIA v2 is enabled on Android 15, or what that path does there.",
      "The restriction lists themselves are not changed. The fix only changes which callers get the reduced list."
     ],
     "behavior_changes": [
      "Apps holding INSTALL_PACKAGES that launch PackageInstaller with ACTION_VIEW or ACTION_INSTALL_PACKAGE are now subject to the unknown-sources user restrictions. When such a restriction is set they are blocked (admin support flow).",
      "No change for non-intent installs by INSTALL_PACKAGES holders, or for privileged apps that set EXTRA_NOT_UNKNOWN_SOURCE on an intent install.",
      "Android 16 Kotlin path only: when callingUid is INVALID_UID, isPermissionGranted is no longer called and the isInstallPermissionGrantedOrRequested abort is skipped. On both 16 and 17, isInstallRequestFromTrustedSource is removed."
     ],
     "preconditions": [
      "An unknown-sources user restriction is set for the user. The cited CTS tests are MixedProfileOwnerTest and MixedManagedProfileOwnerTest testPackageInstallUserRestrictions.",
      "The caller holds INSTALL_PACKAGES and starts the installer with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent, not through a PackageInstaller session.",
      "On Android 15 the patched code is the legacy InstallStart path (usePiaV2() false). On 16/17 both paths are patched."
     ],
     "per_major": {
      "14": "Not affected: the bulletin does not list Android 14. 6/8 lines of the patched region are present at android-security-14.0.0_r29. The material identifies no fix commit for 14.",
      "15": "Fixed by 0985478406c2 in android-security-15.0.0_r13 (Change-Id Ib917acb2c4738f6a4758b8ca149b80943f00acca). Only InstallStart.java is patched. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same change (4dc009cd724c, android-security-16.0.0_r4), plus a hunk that patches v2/model/InstallRepository.kt in the same way: the trust computation is inlined with a callingUid INVALID_UID guard, and bypassUnknownSourceRestrictions is passed to getDevicePolicyRestrictions. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Commit 81f83fdb0944 in android-17.0.0_r1 (matched by related_bug), which is listed as a Cherrypick-From source of the 15 commit. It patches InstallStart.java, where no new intentAction local is added, and InstallRepository.kt, using originatingUid. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Java hunk depends on the locals isInstallPkgPermissionGranted, isPrivilegedAndKnown and intentAction in InstallStart.onCreate. A target tree must already compute them, or the computation must be added.",
      "If the target tree has the PIA v2 path (usePiaV2() / InstallRepository.kt), consider taking the Kotlin hunk from the Android 16 commit as well. The Android 15 commit leaves that path unchanged."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether PIA v2 (usePiaV2()) is enabled on Android 15 builds, and whether the Android 15 InstallRepository.kt has the same unpatched logic.",
      "Why the original landing was reverted (29962260bd04) and which issues the reland fixed.",
      "The exact definitions of isTrustedSource, isInstallPkgPermissionGranted and isPrivilegedAndKnown in the Android 15 InstallStart.java, and any other uses of isTrustedSource there.",
      "The exact list of unknown-sources restriction constants in checkDevicePolicyRestrictions (the diff cuts it off)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0009": {
     "cve": "CVE-2026-0009",
     "summary": "Removes the Photopicker's automatic finish-on-first-selection behaviour in single-select mode and instead always enables the selection bar so the user must explicitly confirm the selected media before it is returned to the calling app.",
     "component": "platform/packages/providers/MediaProvider, Photopicker (Compose photopicker: MainActivity, SelectionImpl, SelectionBarFeature)",
     "weakness": "Missing user confirmation step: in single-select mode, selecting a grid item immediately confirmed the selection. No explicit confirm action was required.",
     "root_cause": "MainActivity.onCreate called listenForSelectionIfSingleSelect(). When configurationManager.configuration.value.selectionLimit == 1, this function collected selection.get().flow and invoked onMediaSelectionConfirmed() as soon as the set reached size 1. SelectionBarFeature.Registration.isEnabled returned config.selectionLimit > 1 (on Android 16/17, only for the ACTIVITY runtime env), so single-select had no confirm UI. On 16/17, MediaGrid also suppressed the selected indicator when selectionLimit was 1. As a result, selecting one media item confirmed the selection and ended the session without a separate confirmation.",
     "fix_analysis": [
      "Commit 1 (Revert^2 'Fix: add confirmation step in single select mode': e7bcd640810f on 15, 61808742c17a on 16, bf034ab4e140 on 17), photopicker/src/com/android/photopicker/MainActivity.kt: deletes listenForSelectionIfSingleSelect() and its call in onCreate. The activity no longer calls onMediaSelectionConfirmed() automatically when the selection size becomes 1. Per the removed comment, confirmation comes from the selection bar click handler or Event.MediaSelectionConfirmed.",
      "photopicker/src/com/android/photopicker/core/selection/SelectionImpl.kt:toggle: replaces the if/else with a when. The new branch `configuration.value.selectionLimit == 1` clears _selection and adds the new item, so in single-select a tap on a different item replaces the current selection instead of failing with FAILURE_SELECTION_LIMIT_EXCEEDED. Removal of an existing item and the ensureSelectionLimitLocked path for multi-select are unchanged.",
      "photopicker/src/com/android/photopicker/features/selectionbar/SelectionBarFeature.kt:Registration.isEnabled: now always returns true. It previously returned selectionLimit > 1, and on 16/17 it was also gated on PhotopickerRuntimeEnv. The selection bar with its confirm action is now composed in single-select sessions.",
      "photopicker/src/com/android/photopicker/features/selectionbar/SelectionBar.kt:SelectionBar: on 15, visibility becomes currentSelection.isNotEmpty() || action == MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP. On 16, the local variable is renamed to showSelectionBar. On 17, the EMBEDDED-specific `selectionLimit > 1` gating of `visible` is removed, so the bar follows showSelectionBar in all runtime envs.",
      "photopicker/src/com/android/photopicker/core/components/mediagrid/MediaGrid.kt:defaultBuildMediaItem and SelectedIconOverlay (16/17 only; the 15 hunk is whitespace): removes shouldIndicateSelected (isSelected && selectionLimit > 1, plus an embedded check on 17). The selected padding, rounded clip and check/number overlay are now drawn in single-select too.",
      "Tests: SelectionImplTest adds testToggleWithSelectionLimitOneReplacesExistingItem (and testSelectionGetSize on 15/16). SelectionBarFeatureTest renames testSelectionBarNotEnabledForSingleSelect* to testSelectionBarIsEnabledForSingleSelectInActivityMode and now expects true. On 15 it also adds testSelectionBarIsAlwaysEnabledInEmbeddedMode.",
      "Commit 2 (Remove duplicate MediaSelectionConfirmed event, 6d81d42dbc7a, android-security-15.0.0_r16), photopicker/src/com/android/photopicker/core/events/Event.kt: deletes a duplicate `data class MediaSelectionConfirmed` declaration in the Event interface. It keeps the declaration documented as fired from the preview screen. The commit carries the same bug id."
     ],
     "completeness": [
      "Covers the activity-mode single-select path. The only auto-confirm code path shown (listenForSelectionIfSingleSelect) is deleted entirely rather than gated, and the selection bar is always enabled. Every configuration now requires the confirm action or Event.MediaSelectionConfirmed.",
      "Event.MediaSelectionConfirmed still finishes the session and is unchanged. Per the Event.kt doc comment, it is fired when the user confirms the selection from the preview screen.",
      "On Android 15 the MediaGrid.kt hunk is whitespace only. The r17 context shows padding driven directly by isSelected. The android-15.0.0_r36 release branch still contains the `shouldIndicateSelected = isSelected && selectionLimit > 1` gating in defaultBuildMediaItem (per index search). That affects the visual indicator, not the confirmation step.",
      "Android 17 also removes the EMBEDDED-mode `selectionLimit > 1` visibility gate in SelectionBar.kt. No such gate appears in the shown 15/16 code.",
      "The second commit only removes a duplicate declaration and adds no behavioural protection."
     ],
     "behavior_changes": [
      "Single-select Photopicker sessions no longer finish on the first selection. A selection bar with a confirm action is shown, and the result is returned only after the user confirms.",
      "In single-select mode, tapping a second item replaces the current selection: SelectionImpl.toggle returns SUCCESS instead of FAILURE_SELECTION_LIMIT_EXCEEDED.",
      "SelectionBarFeature is enabled for every configuration, including selectionLimit == 1 and, on 16/17, both the ACTIVITY and EMBEDDED runtime envs.",
      "On 16/17, selected items in single-select mode now show the selected visual state: shrink padding, rounded corners, and a check icon or order number.",
      "The selection bar stays visible with an empty selection for MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP. The 15 commit adds this; 16/17 already had it.",
      "After the second commit, Event.MediaSelectionConfirmed has a single declaration."
     ],
     "preconditions": [
      "The Compose Photopicker in MediaProvider (photopicker/ directory) is in use. The patched files are absent on Android 14.",
      "Photopicker is launched with selectionLimit == 1 (single-select), for example ACTION_PICK_IMAGES or ACTION_GET_CONTENT without a multi-select extra, as covered by the changed SelectionBarFeatureTest cases.",
      "A media item becomes selected. Before the fix, this triggered the MainActivity collector to confirm immediately."
     ],
     "per_major": {
      "14": "Not affected per the bulletin; code absent. The patched files (photopicker/src/com/android/photopicker/MainActivity.kt, MediaGrid.kt, SelectionImpl.kt) are not present at android-security-14.0.0_r29.",
      "15": "Adapted fix. Revert^2 e7bcd640810f in android-security-15.0.0_r13 applies the same SelectionImpl and SelectionBarFeature logic. Its MainActivity hunk also inserts the PhotopickerEventLogger start lines, its MediaGrid hunk is whitespace only, and SelectionBar gains the ACTION_USER_SELECT_IMAGES_FOR_APP visibility clause. Follow-up commit 6d81d42dbc7a in android-security-15.0.0_r16 removes the duplicate Event.MediaSelectionConfirmed. At android-security-15.0.0_r17 the code shows the fixed toggle() and isEnabled() = true.",
      "16": "Adapted fix: 61808742c17a in android-security-16.0.0_r4. It also removes shouldIndicateSelected from MediaGrid.kt and the ACTIVITY/EMBEDDED branching from SelectionBarFeature.isEnabled, and renames visible to showSelectionBar in SelectionBar.kt.",
      "17": "Fixed in the base: bf034ab4e140 in android-17.0.0_r1. Same changes as 16, plus removal of the EMBEDDED `selectionLimit > 1` visibility gate in SelectionBar.kt and the isEmbedded checks in MediaGrid.kt."
     },
     "backport_notes": [
      "Apply the Revert^2 commit first, then the Event.kt duplicate-removal commit. Pick the MediaGrid.kt and SelectionBar.kt variant that matches the target branch. Branches with shouldIndicateSelected or PhotopickerRuntimeEnv gating, such as android-15.0.0_r36 per index search, need the 16/17 hunks.",
      "In the Android 15 r17 context, the new isEnabled(config, deferredPrefetchResultsMap) signature and the MediaStore and LocalPhotopickerConfiguration uses in SelectionBar.kt have no visible imports. SelectionBarFeatureTest still calls isEnabled with one argument. Check that the 15 tree compiles with the surrounding imports and overloads before reusing it."
     ],
     "confidence": "medium",
     "unknowns": [
      "The original reverted commit c5d89a70a250, and why its CTS tests failed, are not in the material.",
      "The material does not show whether the duplicate MediaSelectionConfirmed declaration was introduced by the Revert^2 cherry-pick itself.",
      "The material does not show why the Android 15 MainActivity hunk also adds the PhotopickerEventLogger start lines.",
      "The body of onMediaSelectionConfirmed, and what exactly the calling app received, are not described beyond the EoP rating."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0018": {
     "cve": "CVE-2026-0018",
     "summary": "AccessibilityManagerService now validates each app-declared accessibility QS tile service through PackageManagerInternal, outside the service lock: the service must resolve, be exported, be protected by BIND_QUICK_SETTINGS_TILE and be enabled. AccessibilityUserState records a feature-to-tile mapping only when the tile component is in that validated set.",
     "component": "platform/frameworks/base, services/accessibility (AccessibilityManagerService, AccessibilityUserState, new AccessibilityTileUtils)",
     "weakness": "Improper input validation. system_server used an app-supplied component name (the tileService attribute in accessibility service or shortcut metadata) without checking that it names a real, exported, permission-protected, enabled TileService. Bulletin type: DoS.",
     "root_cause": "AccessibilityUserState.updateTileServiceMapForAccessibilityServiceLocked() and updateTileServiceMapForAccessibilityActivityLocked() built mA11yServiceToTileService and mA11yActivityToTileService directly from the string returned by AccessibilityServiceInfo.getTileServiceName() and AccessibilityShortcutInfo.getTileServiceName(). Each non-empty string became a ComponentName(featurePackage, tileServiceName) and was stored without any check that the component exists, is exported, is protected by BIND_QUICK_SETTINGS_TILE, or is enabled.",
     "fix_analysis": [
      "Commit f3ef4dd5056e (android-security-15.0.0_r13) adds the new file AccessibilityTileUtils.java. getValidA11yTileServices(context, PackageManagerInternal, serviceInfos, shortcutInfos, userId) walks both lists, builds ComponentName(featurePackage, tileServiceName) for each non-empty tile name, and keeps it only if isComponentValidTileService() passes.",
      "isComponentValidTileService() calls pm.resolveService() with an Intent(TileService.ACTION_QS_TILE) targeted at the component (flags 0, the given userId, Process.myUid()). It rejects a null ResolveInfo or serviceInfo, a service that is not exported, and a serviceInfo.permission other than exactly Manifest.permission.BIND_QUICK_SETTINGS_TILE. It also rejects the component when resolveEnabledComponent(pm.getComponentEnabledSetting(...), serviceInfo.enabled) is false: ENABLED gives true, DEFAULT gives the manifest value, anything else gives false. If pm is null the result is an empty set.",
      "AccessibilityManagerService.java: the valid set is computed with AccessibilityTileUtils.getValidA11yTileServices(...) before entering synchronized (mLock) or mManagerService.getLock() at all three sites that parse infos. The sites are the user-switch/initialization path (the method that checks mCurrentUserId == userId && mInitialized), the PackageMonitor onSomePackagesChanged path, and the PackageMonitor handler that tracks reboundAService. As a result, PackageManager calls no longer run while the AMS lock is held (per the commit message). The set is passed through new @NonNull Set<ComponentName> validA11yTileServices parameters of onSomePackagesChangedLocked, readConfigurationForUserStateLocked, readInstalledAccessibilityServiceLocked and readInstalledAccessibilityShortcutLocked.",
      "readInstalledAccessibilityServiceLocked and readInstalledAccessibilityShortcutLocked: the map rebuild moves out of the 'lists changed' branch and now runs unconditionally, with the changed flag still returned. The code comment says PackageManager may not resolve the TileService on the initial load.",
      "AccessibilityUserState.updateTileServiceMapForAccessibilityServiceLocked(Set) and updateTileServiceMapForAccessibilityActivityLocked(Set) clear the map, return early if the valid set is empty, and call put(a11yFeature, tileService) only when validA11yTileServices.contains(tileService).",
      "Tests: new AccessibilityTileUtilsTest and a new test helper, utils/TileServiceUtil, which mocks PackageManagerInternal.resolveService and getComponentEnabledSetting. AccessibilityManagerServiceTest and AccessibilityUserStateTest are updated to pass the valid set.",
      "Commit 40729ba7fc18 (android-security-15.0.0_r16) is test-only. It adds the @Mock PackageManagerInternal mMockPackageManagerInternal field and its LocalServices registration to AccessibilityManagerServiceTest, which fixes the Android 15 test build ('missing variable')."
     ],
     "completeness": [
      "Both maps are covered. The accessibility-service map and the accessibility-activity (shortcut) map both filter against the validated set.",
      "All three parse sites in AccessibilityManagerService that feed readConfigurationForUserStateLocked compute the valid set and pass it on. Because the update methods' signatures changed, the old no-argument form has no remaining caller.",
      "The tile ComponentName is always built with the feature's own package name. Validation requires that the service resolves, is exported, has exactly the BIND_QUICK_SETTINGS_TILE permission, and is effectively enabled.",
      "The valid set is computed outside the lock and used later inside it. Within one pass, a package change between those two points is not re-checked.",
      "In the service branch, getValidA11yTileServices dereferences a11yServiceInfo.getResolveInfo().serviceInfo without a null check.",
      "The fix validates only when the maps are built. The material does not show whether the code that consumes the maps runs any further check at use time."
     ],
     "behavior_changes": [
      "Accessibility features whose declared tileService does not resolve, is not exported, lacks BIND_QUICK_SETTINGS_TILE protection, or is disabled are no longer associated with a QS tile in AccessibilityUserState. A Slog.w with the reason is logged for each rejected component.",
      "If PackageManagerInternal is unavailable (null), the valid set is empty and both maps end up empty.",
      "The feature-to-tile maps are now rebuilt on every readConfigurationForUserStateLocked pass, not only when the installed service or shortcut lists changed.",
      "Each parse pass makes one resolveService call per non-empty declared tile name, plus a getComponentEnabledSetting call for tiles that pass the resolve, export and permission checks. All of this runs before the lock is taken."
     ],
     "preconditions": [
      "An installed app declares an accessibility service (AccessibilityServiceInfo) or an accessibility shortcut target (AccessibilityShortcutInfo) with a non-empty tileService attribute.",
      "The code runs in system_server on the user-switch/initialization path, on PackageMonitor onSomePackagesChanged, and in the rebinding PackageMonitor handler. The rebinding handler returns early for non-current users."
     ],
     "per_major": {
      "14": "Not affected per the bulletin (not listed). At android-security-14.0.0_r29 only 5/8 lines of the patched region are present. The material does not show whether the tile-service map code exists there.",
      "15": "Same fix: f3ef4dd5056e cherry-picked into android-security-15.0.0_r13, plus the test-only follow-up 40729ba7fc18 in android-security-15.0.0_r16, which adds mMockPackageManagerInternal to AccessibilityManagerServiceTest. 6/8 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "Adapted fix: 7783a5265907 in android-security-16.0.0_r4. AccessibilityTileUtils and the logic are identical. readInstalledAccessibilityShortcutLocked carries the Flags.clearShortcutsWhenActivityUpdatesToService() block. 6/8 lines are present at android-security-16.0.0_r8.",
      "17": "Adapted fix: the original 637f5b4aa4fb in android-17.0.0_r1 (base). It uses mInstalledServicesMap, buildInstalledServicesMapLocked and getInstalledServices() instead of mInstalledServices, and adds extra test setup in AccessibilityManagerServiceTest. 6/8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply f3ef4dd5056e first. Then apply 40729ba7fc18 if the target AccessibilityManagerServiceTest lacks a PackageManagerInternal mock registered through LocalServices (Android 15 needed it).",
      "AccessibilityTileUtils.java is identical across branches. The version-specific hunks are the installed-services container (mInstalledServices on 15 and 16, getInstalledServices()/buildInstalledServicesMapLocked on 17) and the context around readInstalledAccessibilityShortcutLocked (a flag-guarded shortcut-clearing block on 16, the same block unguarded on 17)."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which consumer of mA11yServiceToTileService or mA11yActivityToTileService fails when given an invalid component, and so the concrete DoS mechanism. The material shows only how the maps are built.",
      "Whether the vulnerable tile-service map code exists on Android 14. The bulletin lists 14 as not affected, and the material shows only a partial region match.",
      "What the second linked bug (441506794) covers, and what moving the PackageManager calls outside mLock addressed in practice.",
      "Whether a package change between computing the valid set and using it under the lock is always followed by a recomputation."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0036": {
     "cve": "CVE-2026-0036",
     "summary": "StageCoordinator.startAnimation now detects a \"malformed\" split-enter transition (opening tasks that belong to a split stage but cover at most one stage while split is not active) and starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea instead of letting them be shown outside of split.",
     "component": "platform/frameworks/base, libs/WindowManager/Shell split screen (StageCoordinator / SplitTransitionUtils)",
     "weakness": "Improper handling of an unexpected transition state (missing validation of a split-enter transition that reaches the wrong handler); the bulletin classifies it as ID",
     "root_cause": "Per the commit message and diff: when two tasks are launched immediately one after the other and the second is an adjacent launch, both launches can end up in a single shell transition whose transition request reports the first task as the trigger. StageCoordinator is therefore not resolved as the handler for that transition. In StageCoordinator.startAnimation, for a transition not pending in mSplitTransitions, the code only did `if (!mMainStage.isActive()) return false;`. The transition then fell through to the default transition handler, which showed the adjacent task (a task that resolves to a split stage) on its own, without split being active. No code checked whether a transition containing split-stage children actually had tasks for both stages before giving up on it.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitTransitionUtils.java (new): handleMalformedEnterTransition(TransitionInfo, Function<RunningTaskInfo, StageTaskListener>) iterates over info.getChanges(). For each change with a non-null getTaskInfo(), it resolves the stage through the passed resolver and keeps the task only if the stage is non-null and isOpeningMode(change.getMode()) is true (TRANSIT_OPEN or TRANSIT_TO_FRONT). If at least one such task exists but the set of distinct stages has size <= 1, the transition is treated as malformed: it logs to WM_SHELL_SPLIT_SCREEN and returns a WindowContainerTransaction that calls wct.reparent(task.token, null /* parent */, true /* onTop */) for each collected task, which reparents it to the TaskDisplayArea. Otherwise it returns null.",
      "libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java:startAnimation: in the non-pending-transition branch, adds a ProtoLog line and replaces `if (!mMainStage.isActive()) return false;` with a block that calls SplitTransitionUtils.handleMalformedEnterTransition(info, (taskInfo) -> getStageOfTask(taskInfo)). If a WCT is returned, it calls mTransitions.startTransition(TRANSIT_CLOSE, wct, null) to queue a cleanup transition. It still returns false, so the original transition is left to other handlers.",
      "StageCoordinator.java constructors: store the Transitions instance in a new final field mTransitions (before, it was only used locally for transitions.addHandler(this)) so that startAnimation can start the new transition.",
      "libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java: class visibility changed from package-private to public so SplitTransitionUtils in com.android.wm.shell.common.split can reference it.",
      "libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionUtilsTests.java (new): covers the not-malformed (two stages), one stage (WCT returned), no stages, non-opening mode and null task info cases."
     ],
     "completeness": [
      "The detection only runs in StageCoordinator.startAnimation, for transitions not pending in mSplitTransitions, and only when !mMainStage.isActive() (A14/A15/A17 diffs) or !isSplitActive() (A16 diff). Transitions that arrive while split is already active take the existing house-keeping path and the new code does not check them.",
      "Only changes with mode TRANSIT_OPEN or TRANSIT_TO_FRONT whose task resolves to a stage via getStageOfTask are considered. A stage child that appears with another mode (e.g. TRANSIT_CHANGE) does not count, so such a transition is not classified as malformed.",
      "The fix is remedial rather than preventive. The original transition still falls through to the default handler (startAnimation returns false), and the cleanup is a separate TRANSIT_CLOSE transition queued via mTransitions.startTransition. This commit does not change the cause described in the commit message (the transition request reporting the first launch as the trigger task, so StageCoordinator is not chosen as handler).",
      "The fix detects the condition by stage count (<= 1 distinct stage among the collected tasks). A transition with tasks for two stages that still reaches this branch is treated as well-formed and left alone."
     ],
     "behavior_changes": [
      "New ProtoLog debug messages: one in startAnimation ('startAnimation: transition=%d isSplitActive=%b') and one in SplitTransitionUtils ('Malformed enter transition=%d detected').",
      "When a malformed enter transition is detected while split is inactive, a new TRANSIT_CLOSE transition is started that reparents the affected task(s) to the TaskDisplayArea on top. Per the commit message, the adjacent task is restored to the TaskDisplayArea instead of being treated as an adjacent launch.",
      "StageTaskListener is now a public class. A new public utility class SplitTransitionUtils is added (and, on A14/A15/A17, a new field StageCoordinator.mTransitions)."
     ],
     "preconditions": [
      "Shell transitions path: StageCoordinator is registered as a transition handler and receives startAnimation for a transition it did not request (not in the mSplitTransitions pending set).",
      "Split screen is not active (mMainStage.isActive() is false, or isSplitActive() is false on the A16 variant).",
      "The transition contains at least one opening-mode (TRANSIT_OPEN/TRANSIT_TO_FRONT) task change whose task resolves to a split stage (getStageOfTask non-null), and all such tasks belong to a single stage.",
      "Per the commit message, this happens when two tasks are started immediately one after the other and the second is an adjacent launch, so both launches end up in one transition whose trigger task is the first launch. The material does not show the exact launch API or flags."
     ],
     "per_major": {
      "14": "Same fix, applied in android-security-14.0.0_r28 (commit 3ef64c4444d5) with identical logic. It also adds `import static android.view.WindowManager.TRANSIT_CLOSE` and the mTransitions field/constructor changes. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "Fix commit fa1c418fd8aa in android-security-15.0.0_r16 (analysed here). It adds the mTransitions field and SplitTransitionUtils with a local isOpeningMode, and makes StageTaskListener public. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix in android-security-16.0.0_r7 (commit 04814cb8a86e). The check uses isSplitActive() instead of mMainStage.isActive(). SplitTransitionUtils imports isOpeningMode from com.android.wm.shell.shared.TransitionUtil and uses com.android.internal.protolog.ProtoLog. The diff adds no mTransitions field and makes no constructor or StageTaskListener changes (presumably because these already exist on A16; the material does not show it). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Resolved in base android-17.0.0_r1 (commit f60c7771358c). The diff shown in the material has the same shape as the A14 change (mMainStage.isActive() check, mTransitions field added, TRANSIT_CLOSE import, StageTaskListener made public). 7/8 lines of the patched region present at android-security-17.0.0_r1, so the region differs slightly from the other branches there."
     },
     "backport_notes": [
      "Dependencies to check in the target tree: a stored Transitions reference in StageCoordinator (add mTransitions if only a constructor local exists), the TRANSIT_CLOSE static import, StageTaskListener visibility (it must be public for the common.split package), and whether an isOpeningMode helper already exists in shared TransitionUtil (A16 reuses it, A14/A15/A17 define a local one). Use isSplitActive() where the branch has already replaced mMainStage.isActive()."
     ],
     "confidence": "high",
     "unknowns": [
      "What information is disclosed and to whom: the bulletin only gives type ID and severity High, and the material does not describe the concrete security impact of the adjacent task being shown outside split.",
      "The exact launch sequence/flags used by the test app from b/405392600.",
      "Why the A17 commit diff shown matches the A14 shape, and which line differs in the 7/8 match at android-security-17.0.0_r1.",
      "Whether a separate server-side (WM core) change addressing the trigger-task selection exists; the material includes none.",
      "The windowing mode of the restored task after the reparent to the TaskDisplayArea."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0045": {
     "cve": "CVE-2026-0045",
     "summary": "In the Bluetooth stack, btm_sec_is_upgrade_possible() now returns true when the peer is temporarily bonded and the service requires authentication. Separately, bta_jv_rfcomm_connect() no longer registers an RFC_MUX security service record with the client sec_mask.",
     "component": "platform/packages/modules/Bluetooth, system/stack/btm (BTM security manager) and system/bta/jv (RFCOMM client, JV layer)",
     "weakness": "Improper authentication: no security-upgrade decision for temporarily bonded peers when authentication is required",
     "root_cause": "In system/stack/btm/btm_sec.cc, btm_sec_is_upgrade_possible() sets is_possible = false as soon as the device record has BTM_SEC_LINK_KEY_KNOWN. It sets it back to true only when a MITM upgrade of an unauthenticated link key is possible. A link key from a temporary bond (p_dev_rec->sec_rec.is_bond_type_temporary()) therefore counted as an existing link key, and no upgrade was reported even when security_required had BTM_SEC_OUT_AUTHENTICATE / BTM_SEC_IN_AUTHENTICATE set. Separately, on Android 15 bta_jv_rfcomm_connect() (behind flag rfcomm_always_use_mitm) called BTM_SetSecurityLevel(true, \"RFC_MUX\", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, ...). This attached the client's security mask to the RFCOMM MUX service record. The title of the Android 16 commit states that the RFCOMM MUX connection request must not start bonding.",
     "fix_analysis": [
      "system/stack/btm/btm_sec.cc:btm_sec_is_upgrade_possible: inside the BTM_SEC_LINK_KEY_KNOWN branch, after the existing MITM-upgrade check, a new condition sets is_possible = true. It applies when (security_required & (is_originator ? BTM_SEC_OUT_AUTHENTICATE : BTM_SEC_IN_AUTHENTICATE)) is non-zero and p_dev_rec->sec_rec.is_bond_type_temporary() is true. The added comment says this should trigger pairing.",
      "system/bta/jv/bta_jv_act.cc:bta_jv_rfcomm_connect: the rfcomm_always_use_mitm-gated call to get_btm_client_interface().security.BTM_SetSecurityLevel(true, \"RFC_MUX\", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0) is removed. sec_mask is still passed to RFCOMM_CreateConnectionWithSecurity(..., sec_mask). The Android 16 commit title ('RFCOMM MUX connection request must not start bonding') indicates the purpose of this removal."
     ],
     "completeness": [
      "The new rule is direction-aware: OUT_AUTHENTICATE for the originator and IN_AUTHENTICATE for the acceptor. The flagged version already in Android 16 checked only BTM_SEC_OUT_AUTHENTICATE, and the 16 commit corrects that.",
      "The rule applies only inside the BTM_SEC_LINK_KEY_KNOWN branch. When no link key is known, is_possible is already initialized to true.",
      "Only the temporary-bond case is added. A known non-temporary unauthenticated link key with an authentication-only (non-MITM) requirement is still treated as sufficient, as before.",
      "On Android 15 the btm_sec.cc check is unconditional (no aconfig flag). In contrast, the variant already in Android 16 was behind a flag."
     ],
     "behavior_changes": [
      "When the peer is only temporarily bonded and the service requires authentication, btm_sec_is_upgrade_possible() now reports that an upgrade is possible. Per the code comment, this triggers pairing.",
      "RFCOMM client connections from the JV layer no longer register an RFC_MUX security service record with the client's sec_mask in bta_jv_rfcomm_connect. On 15 this registration only happened when rfcomm_always_use_mitm was enabled. The sec_mask passed to RFCOMM_CreateConnectionWithSecurity is unchanged."
     ],
     "preconditions": [
      "A device record for the peer with BTM_SEC_LINK_KEY_KNOWN and a temporary bond type (is_bond_type_temporary()).",
      "A service access with BTM_SEC_OUT_AUTHENTICATE (originator) or BTM_SEC_IN_AUTHENTICATE (acceptor) set in security_required. Per index search, the JV path is reached via BTA_JvRfcommConnect -> bta_jv_rfcomm_connect in system/bta/jv/bta_jv_api.cc.",
      "On Android 15 the removed bta_jv block was only active when flag rfcomm_always_use_mitm was enabled."
     ],
     "per_major": {
      "14": "adapted fix (commit 2e2a7bf09a3f, android-security-14.0.0_r28): only the btm_sec.cc condition is added, using p_dev_rec->security_required and p_dev_rec->is_bond_type_temporary() (no sec_rec sub-struct). No bta_jv_act.cc change. 4/4 lines of the patched region present at r29.",
      "15": "this fix (commit c1a809d2333e, android-security-15.0.0_r16): adds the btm_sec.cc condition using sec_rec fields and removes the rfcomm_always_use_mitm-gated BTM_SetSecurityLevel RFC_MUX block in bta_jv_act.cc. The code context at r17 shows both hunks applied. The per-major facts report 3/8 lines of the patched region present at r17.",
      "16": "adapted fix (commit 2ad8e1e0e0e5, android-security-16.0.0_r7, titled 'RFCOMM MUX connection request must not start bonding'). The condition already existed behind flag upgrade_temp_bonding_on_auth_req and checked only BTM_SEC_OUT_AUTHENTICATE. The commit removes the flag from flags/security.aconfig, makes the check unconditional and direction-aware, and removes the unflagged BTM_SetSecurityLevel RFC_MUX block from bta_jv_rfcomm_connect. 4/8 lines present at r8.",
      "17": "reported resolved in base android-17.0.0_r1 (commit 92611bfc4d26). However, the diff shown uses the p_dev_rec->security_required / BTM_TRACE_DEBUG style, and 0/4 lines of the patched region are present at android-security-17.0.0_r1. The material does not confirm the state on 17."
     },
     "backport_notes": [
      "On 15 and 16 Google changed btm_sec.cc and removed the RFC_MUX BTM_SetSecurityLevel block in bta_jv_rfcomm_connect in the same commit. On 14 only btm_sec.cc was changed. Keep the hunks together where the block exists.",
      "Field access differs by branch: p_dev_rec->security_required / is_bond_type_temporary() on 14 versus p_dev_rec->sec_rec.* on 15 and 16. If the target already has the flagged upgrade_temp_bonding_on_auth_req variant (16-style), replace it with the unflagged direction-aware check and drop the aconfig flag."
     ],
     "confidence": "medium",
     "unknowns": [
      "The actual state of Android 17: the cited 17 commit diff does not look like the code shown for the other 15/16-era branches, and the patched region is reported 0/4 present at android-security-17.0.0_r1.",
      "How btm_sec_check_upgrade() and its callers act on the new is_possible result. The material shows only the start of that function.",
      "Whether the RFC_MUX removal on 15 is needed because of the new upgrade rule. Only the Android 16 commit title suggests this link.",
      "How a temporary bond is established, and which profile or service the bulletin's EoP refers to.",
      "Why the line-match figure for Android 15 is 3/8, given that the code context at r17 shows both hunks applied."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0046": {
     "cve": "CVE-2026-0046",
     "summary": "Changes the owner of the Letterbox input interceptor's InputWindowHandle from system_server's PID/UID to the letterboxed app's session PID/UID, so input obscured-touch checks no longer treat the letterbox as a trusted system window.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/Letterbox.java)",
     "weakness": "Incorrect identity assignment to an input window: the letterbox input handle was registered as a trusted system-owned window, which defeated tapjacking/obscured-touch protection",
     "root_cause": "In the Letterbox.InputInterceptor constructor, the InputWindowHandle created for the letterbox black bars (type TYPE_INPUT_CONSUMER, inputConfig NOT_FOCUSABLE | SLIPPERY) got ownerPid = WindowManagerService.MY_PID and ownerUid = WindowManagerService.MY_UID, which is the identity of system_server. The handle is SLIPPERY, so a touch can slip out of the letterbox onto a window underneath. Because the system owned the letterbox, input security logic treated it as a trusted system window. As a result, the existing tapjacking prevention did not work correctly for touches reaching lower windows of other activities that the letterbox covered (per commit message and diff).",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/Letterbox.java : InputInterceptor.InputInterceptor(String namePrefix, WindowState win): replaces mWindowHandle.ownerPid = WindowManagerService.MY_PID and mWindowHandle.ownerUid = WindowManagerService.MY_UID with mWindowHandle.ownerPid = win.mSession.mPid and mWindowHandle.ownerUid = win.mSession.mUid. The letterbox input handle now carries the identity of the app window it surrounds.",
      "With the app's UID as owner, input security checks no longer treat the letterbox as a trusted system window, so the obscured-touch logic is no longer bypassed (per the in-code comment). This commit changes nothing else (type, inputConfig, touchable region)."
     ],
     "completeness": [
      "Covers the single InputWindowHandle created in Letterbox.InputInterceptor. In the Android 14/15/16 commits the identity change is the whole fix (5 insertions, 2 deletions).",
      "SLIPPERY (or SPY on 16 behind a flag) and the layoutParamsType are left unchanged. The fix relies on the existing UID-based trust/obscured-touch checks rather than stopping the slip itself.",
      "The commit lists three Cherrypick-From sources, but the diff provided has only one file hunk. Whether those internal changes contained anything beyond this hunk is not shown."
     ],
     "behavior_changes": [
      "Letterbox input windows are now attributed to the letterboxed app's PID/UID instead of system_server's. This affects how input security checks classify them (per the in-code comment).",
      "Touches that slip from a letterbox onto windows of other UIDs may now go through obscured-touch handling, which the system-owned letterbox previously bypassed."
     ],
     "preconditions": [
      "An activity is shown in letterbox mode, so Letterbox.InputInterceptor creates an input window handle with a non-empty touchable region (updateTouchableRegion called with a non-empty frame).",
      "Another activity's window lies underneath the letterbox region and can receive the slipped touch (per commit message)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 8284ae9c703a in android-security-14.0.0_r28. Identical hunk at a different line offset (around line 304). 7/7 lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 8eb831ece18d in android-security-15.0.0_r16. 7/7 lines present at android-security-15.0.0_r17.",
      "16": "same owner PID/UID change with different surrounding context: platform/frameworks/base 678475b0c23c in android-security-16.0.0_r7. layoutParamsType is chosen between TYPE_APPLICATION_OVERLAY and TYPE_INPUT_CONSUMER, and inputConfig uses Flags.scrollingFromLetterbox() ? SPY : SLIPPERY. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin. At android-security-17.0.0_r1 only 5/7 lines of the patched region are present. The material does not show the exact state."
     },
     "backport_notes": [
      "Single-hunk change: in the Letterbox.InputInterceptor constructor, switch the two ownerPid/ownerUid assignments from WindowManagerService.MY_PID/MY_UID to win.mSession.mPid/win.mSession.mUid. Surrounding lines (layoutParamsType, inputConfig) vary by release and should stay as they are on the target branch.",
      "Requires the WindowState passed to InputInterceptor to have a non-null mSession. The same constructor already dereferences win.mWmService."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the exact input-side check (which InputDispatcher trust/obscured-touch logic) that exempted the system-UID letterbox.",
      "What the two additional internal Cherrypick-From changes contained beyond the single hunk shown.",
      "Why Android 17 is considered not affected, and what its Letterbox handle setup looks like (only a 5/7 line match is reported).",
      "Whether the owner change has other side effects, for example on dumpsys output or owner-based accounting."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0048": {
     "cve": "CVE-2026-0048",
     "summary": "When WindowState.hide() is called with doAnimation=true for a force-hidden window (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility, or mPermanentlyHidden) that is animating, it now cancels the running animation and, if the window is playing an exit animation, hides it immediately without a hide animation.",
     "component": "platform/frameworks/base, services/core WindowManager (com.android.server.wm.WindowState)",
     "weakness": "Improper enforcement of window visibility policy (forced hide of an overlay window deferred while an existing exit animation runs), allowing an overlay to stay visible when it should be hidden",
     "root_cause": "In services/core/java/com/android/server/wm/WindowState.java, hide(boolean doAnimation, boolean requestAnim) forces a window hidden regardless of whether the client wants it shown (per its comment). When doAnimation was true and the window was already playing an exit animation (e.g. the window was removed with a long exit animation), WindowStateAnimator#applyAnimationLocked skipped the hide animation, per the commit message. The hide operation then waited for the previous exit animation to finish, which per the commit message could take a long time. Per the commit's test description, the removed window was not hidden immediately when an activity was launched with the hide-overlay flag.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowState.java:hide(): inside the existing `if (doAnimation)` block, after the `!mToken.okToAnimate()` check, a new block is added that applies only when the window is in a force-hide state: `mForceHideNonSystemOverlayWindow || mHiddenWhileSuspended || !mAppOpVisibility || mPermanentlyHidden`.",
      "If such a window `isAnimating()`, the fix calls `cancelAnimation()`. If `mAnimatingExit` is set, it also sets `doAnimation = false`, so `current` is computed from isLegacyPolicyVisibility() rather than mLegacyPolicyVisibilityAfterAnim, and per the commit message the window is hidden right away instead of waiting for the exit animation to end.",
      "The four conditions mirror the four early-return checks already present in show() (lines 3030-3046 at android-security-15.0.0_r17)."
     ],
     "completeness": [
      "The new logic is only inside `if (doAnimation)`. Callers of hide() that pass doAnimation=false are unaffected.",
      "Only the four force-hide states are covered. hide() calls for other reasons on an animating window keep the previous behavior.",
      "For a force-hidden window that is animating but not exiting, the fix cancels the running animation but leaves doAnimation true. The material does not show what the rest of hide() then does.",
      "No change to WindowStateAnimator#applyAnimationLocked itself.",
      "Single-file, single-commit fix. No automated tests in the diff (the Test line describes a manual test)."
     ],
     "behavior_changes": [
      "Force-hidden windows (hidden non-system overlay, suspended package, app-op hidden, permanently hidden) that are playing an exit animation are now hidden immediately without a hide animation when hide() is called with doAnimation=true.",
      "Any running animation on such a window is cancelled via cancelAnimation() when hide(doAnimation=true) is called, so an exit animation is cut short."
     ],
     "preconditions": [
      "A window is in one of the force-hide states, most notably mForceHideNonSystemOverlayWindow (per the commit's test: an activity launched with the hide-overlay flag).",
      "The window is already animating, specifically an exit animation (mAnimatingExit) with a long duration, when WindowState.hide(doAnimation=true, ...) is called.",
      "Window Manager runs in system_server (general platform knowledge)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 7c5437456c5d in android-security-14.0.0_r28, identical 10-line hunk in WindowState.hide() (@@ -3102); 4/4 lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base c9bc382318dd in android-security-15.0.0_r16, identical hunk (@@ -3081); 4/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 36a774d72399 in android-security-16.0.0_r7, identical hunk (@@ -2964); 4/4 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base 6ca1d6b26237 in android-17.0.0_r1 (base), identical hunk (@@ -3096); 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The hunk is identical on all four majors and only uses existing WindowState fields and methods (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, mAppOpVisibility, mPermanentlyHidden, mAnimatingExit, isAnimating(), cancelAnimation()). It should apply cleanly to any fork whose hide() still contains the `if (!mToken.okToAnimate()) { doAnimation = false; }` anchor."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact security impact behind the bulletin's EoP rating is not stated in the material beyond the commit message.",
      "The rest of hide() after the shown hunk and the body of WindowStateAnimator#applyAnimationLocked are not included, so how the hide animation was skipped is known only from the commit message.",
      "What hide() does for a force-hidden window whose non-exit animation was cancelled while doAnimation stays true is not shown.",
      "Which callers of WindowState.hide() with doAnimation=true are reachable in the force-hide scenario is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0050": {
     "cve": "CVE-2026-0050",
     "summary": "AdapterService.handleBondStateChanged now also revokes the stored per-device access permissions (message, phonebook and a third stored permission) when a device's bond state leaves BOND_BONDED (fromState == BOND_BONDED), not only when it reaches BOND_NONE. A device that reuses a bonded address and tries to rebond therefore loses the previously granted permissions, and the user is asked for consent again.",
     "component": "platform/packages/modules/Bluetooth, btservice/AdapterService (bond state handling and per-device access permissions)",
     "weakness": "Incomplete cleanup of per-device trust state on bond state change: access permissions granted earlier stayed in place when a device rebonded with the same address, without the user consenting again",
     "root_cause": "In AdapterService.handleBondStateChanged, the stored device permissions (setMessageAccessPermission, setPhonebookAccessPermission and a third permission set in the same block) were reset to ACCESS_UNKNOWN only when toState == BOND_NONE. The commit message says that when a device with the same address as a previously bonded device tries to bond again, the adapter service may not receive a BOND_NONE state change. The new test models this as a direct BOND_BONDED to BOND_BONDING transition. As a result, the permissions granted to the original device stayed in place for the new, possibly different, device, and the user was not asked for consent again.",
     "fix_analysis": [
      "android/app/src/com/android/bluetooth/btservice/AdapterService.java:handleBondStateChanged: the revocation condition is widened from `toState == BOND_NONE` to `toState == BOND_NONE || fromState == BOND_BONDED`. Any transition out of the BONDED state, including BONDED -> BONDING on a rebond attempt, now resets the stored access permissions to BluetoothDevice.ACCESS_UNKNOWN. A static import of BluetoothDevice.BOND_BONDED is added.",
      "On Android 17 (921aaec06df0) the new clause is behind a flag: `toState == BOND_NONE || (Flags.rebokePermissionOnUnbond() && fromState == BOND_BONDED)`. The Android 15 and 16 cherry-picks apply the clause without any flag check.",
      "android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java: the new test testRemovePermissionBondedToBonding mocks SharedPreferences and checks that handleBondStateChanged(device, BOND_BONDED, BOND_BONDING) causes three SharedPreferences.Editor.remove calls. On 17 the test carries @EnableFlags(Flags.FLAG_REBOKE_PERMISSION_ON_UNBOND)."
     ],
     "completeness": [
      "Covers every transition whose fromState is BOND_BONDED, whatever the toState, in addition to the existing toState == BOND_NONE case. mDatabaseManager.handleBondStateChanged is still called before the permission block.",
      "Only the permission block in handleBondStateChanged changes. The diff does not touch other per-device state that may be keyed on the address (for example profile connection policies handled by mDatabaseManager), so this commit does not show whether that state also carries over on a rebond.",
      "The fix depends on the stack delivering a bond state change with fromState == BOND_BONDED for the rebond attempt. This change does not cover a path where no bond state callback is delivered at all.",
      "On Android 17 the new behavior depends on the runtime value of Flags.rebokePermissionOnUnbond(). With the flag off, the original condition still applies."
     ],
     "behavior_changes": [
      "When a bonded device's bond state moves from BOND_BONDED to any other state (for example BOND_BONDING on a rebond), its message, phonebook and third stored access permissions are reset to ACCESS_UNKNOWN. Per the commit message, the user is then asked for consent again.",
      "On such transitions, three SharedPreferences entries for the device are removed, as the new unit test checks."
     ],
     "preconditions": [
      "A remote device was bonded earlier and had been granted message, phonebook or the third stored access permission.",
      "A device using the same Bluetooth address tries to bond again, and the adapter service does not receive a BOND_NONE state change (commit message).",
      "On Android 17, the com.android.bluetooth.flags.reboke_permission_on_unbond flag must be enabled for the new clause to take effect."
     ],
     "per_major": {
      "14": "Not affected per the bulletin (not listed). At android-security-14.0.0_r29 only 4/8 lines of the patched region are present. No fix was applied there.",
      "15": "Same fix, applied as cherry-pick da365e87dce9 (from 921aaec06df0) in android-security-15.0.0_r16, Change-Id Id63a61908fb22c426c49d0979bba0c6a8b9dba03. The new clause has no flag check. 7/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix, applied as d0f7d128d436 in android-security-16.0.0_r7, also with no flag check. 7/8 lines present at android-security-16.0.0_r8.",
      "17": "Base fix 921aaec06df0 in android-17.0.0_r1. The new clause is gated by Flags.rebokePermissionOnUnbond(), and the test is annotated @EnableFlags(Flags.FLAG_REBOKE_PERMISSION_ON_UNBOND). 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit with one functional change: add the static import of BluetoothDevice.BOND_BONDED and widen the `if (toState == BOND_NONE)` condition in handleBondStateChanged. The 15 and 16 cherry-picks apply the clause with no flag check, even though the 15 commit message still lists the reboke_permission_on_unbond flag. The 17 form calls Flags.rebokePermissionOnUnbond().",
      "The new unit test mocks mMockContext.getSharedPreferences in AdapterServiceTest. On heavily customized trees the test may need changes, but the production change does not depend on it."
     ],
     "confidence": "high",
     "unknowns": [
      "Which permission is the third one reset in the block. The diff shows only setMessageAccessPermission and setPhonebookAccessPermission, while the test expects three SharedPreferences removes.",
      "Why the stack can skip the BOND_NONE transition on a rebond with the same address. The commit message only says it is possible, and the native/stack side is not in the material.",
      "Whether other per-device trust state outside this permission block also survives such a rebond.",
      "Where the reboke_permission_on_unbond flag is defined. The material does not include the aconfig definition."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0055": {
     "cve": "CVE-2026-0055",
     "summary": "PackageInstallerService.createSessionInternal now validates SessionParams.volumeUuid with StorageManager.convert() when INSTALL_FORCE_VOLUME_UUID is requested, rejecting the session with IllegalArgumentException instead of accepting an arbitrary string that later crashed the system.",
     "component": "platform/frameworks/base, package manager / PackageInstallerService (services/core/java/com/android/server/pm)",
     "weakness": "Missing input validation of a caller-supplied volume UUID string (path-like values such as \"../../../data\" accepted) in install session parameters",
     "root_cause": "In PackageInstallerService.createSessionInternal, when the caller set INSTALL_FORCE_VOLUME_UUID (and not INSTALL_INTERNAL), the code only ORed in INSTALL_INTERNAL and kept params.volumeUuid exactly as supplied by the caller. No check that the string was a valid volume UUID was performed at session creation; per the commit message the invalid value propagated down the stack and crashed the system later. The commit's test output shows the rejected value \"../../../data\", i.e. a path-traversal-style string being passed as a volume UUID.",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/PackageInstallerService.java:createSessionInternal: inside the `else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0)` branch, after `params.installFlags |= PackageManager.INSTALL_INTERNAL`, the fix calls `StorageManager.convert(params.volumeUuid)` in a try block.",
      "If StorageManager.convert throws IllegalArgumentException, the fix rethrows a new IllegalArgumentException with the message \"Invalid volumeUuid value in session params: <volumeUuid>\", aborting session creation.",
      "The return value of convert() is discarded; the call is used purely as a validator. No downstream code (the later crash site referenced in the commit message) is modified by this commit."
     ],
     "completeness": [
      "Covers only the INSTALL_FORCE_VOLUME_UUID branch of createSessionInternal; the INSTALL_INTERNAL branch (which calls InstallLocationUtils.fitsOnInternal) does not gain a volumeUuid validation in this diff, and the default branch overwrites volumeUuid via InstallLocationUtils.resolveInstallVolume so it is not caller-controlled there.",
      "The fix is a fail-early check at the session-creation entry point; the downstream code that crashed on the invalid value is untouched, so any other path that feeds an unvalidated volumeUuid into that code is not hardened by this commit (the material does not show whether such paths exist).",
      "Validity is defined by whatever StorageManager.convert accepts; its acceptance rules are not shown in the material."
     ],
     "behavior_changes": [
      "Creating an install session with INSTALL_FORCE_VOLUME_UUID and a volumeUuid that StorageManager.convert rejects now fails immediately with IllegalArgumentException(\"Invalid volumeUuid value in session params: ...\") from createSessionInternal (surfaces to the `pm install` shell command as a thrown exception, per the commit's test output).",
      "Sessions with a value accepted by StorageManager.convert are unaffected."
     ],
     "preconditions": [
      "Caller can create a PackageInstaller session (reaches PackageInstallerService.createSessionInternal).",
      "SessionParams.installFlags has INSTALL_FORCE_VOLUME_UUID set and INSTALL_INTERNAL clear, and SessionParams.volumeUuid is caller-controlled and not a valid volume UUID.",
      "Whether an unprivileged app can set INSTALL_FORCE_VOLUME_UUID, or whether only shell/privileged installers reach this branch, is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 06fb4af542b7 in android-security-14.0.0_r28, identical 8-line hunk in PackageInstallerService.java (at line ~874); 4/4 lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 613fa6465e4d in android-security-15.0.0_r16, identical hunk (at line ~945); 4/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 62ec466efd80 in android-security-16.0.0_r7, identical hunk (at line ~956); 4/4 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base c17553943026 in android-17.0.0_r1 (base), identical hunk; 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk: the diff adds no import or other file change and relies on the existing INSTALL_FORCE_VOLUME_UUID branch in createSessionInternal; the hunk applied unchanged across 14 through 17, differing only in line offset."
     ],
     "confidence": "high",
     "unknowns": [
      "Where downstream the invalid volumeUuid caused the crash and what the resulting EoP consisted of; the material only says the system crashed later.",
      "Which callers (unprivileged apps vs shell/privileged installers) can set INSTALL_FORCE_VOLUME_UUID and reach this branch.",
      "The exact set of values StorageManager.convert accepts (e.g. null, well-known UUID aliases)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0056": {
     "cve": "CVE-2026-0056",
     "summary": "Adds two validation checks on ResStringPool_header.styleCount in ResStringPool::setTo (libs/androidfw/ResourceTypes.cpp). A string pool whose style offset array would not fit before stringsStart, or whose styleCount would overflow when multiplied by sizeof(uint32_t), is now rejected with BAD_TYPE instead of being accepted.",
     "component": "platform/frameworks/base, libs/androidfw (ResStringPool resource string pool parser)",
     "weakness": "Missing bounds check on a header count (styleCount) leading to out-of-bounds read; also a guarded integer overflow in styleCount * sizeof(uint32_t)",
     "root_cause": "ResStringPool::setTo checked stringCount against the data size (headerSize + stringCount*sizeof(uint32_t) <= size), checked stringsStart and stylesStart against mSize, and required stylesStart > stringsStart. It did not check that the styleCount uint32_t style offsets, which start at the position of mEntryStyles inside the chunk, fit before stringsStart. A malformed styleCount could therefore describe style offsets beyond what the chunk layout allows. Per the commit message, this could lead to out-of-bounds reads beyond the bounds of the string pool chunk. The product styleCount * sizeof(uint32_t) was also not guarded against overflow (the new log text reads 'potential integer overflow when finding style entries').",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo, right after an existing check that logs stylesStart against header.size and before mStylePoolSize is computed: the first new check rejects (ALOGW + return mError = BAD_TYPE) when mHeader->styleCount > numeric_limits<decltype(styleCount)>::max() / sizeof(uint32_t). This prevents overflow in the multiplication that follows.",
      "libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo: the second new check computes styleOffsetsStart = mEntryStyles - mHeader (byte offset of the style offsets within the chunk). It returns BAD_TYPE when mHeader->styleCount * sizeof(uint32_t) > (mHeader->stringsStart - styleOffsetsStart), meaning the declared style offsets would extend past the start of string data. Both checks run before mStylePoolSize is computed.",
      "libs/androidfw/tests/ResourceTypes_test.cpp (new) and libs/androidfw/Android.bp: two tests are added to libandroidfw_tests. Each builds a valid one-style pool with StringPool::FlattenUtf8, then sets header->styleCount to UINT32_MAX and to 2 respectively, and asserts that setTo returns BAD_TYPE in both cases."
     ],
     "completeness": [
      "Covers: the declared style offsets (styleCount entries starting at mEntryStyles) must now fit between their start and stringsStart, and the size multiplication is guarded against overflow.",
      "The new bound uses the unsigned subtraction mHeader->stringsStart - styleOffsetsStart. The material does not show a check that stringsStart is at or beyond styleOffsetsStart. If it were smaller, the subtraction would wrap and this check would not reject. Whether another part of setTo prevents that layout is not visible in the provided context.",
      "The fix only validates at load time. The consuming accessors that index the style offsets are not changed and are not shown in the material, so the material does not establish whether the load-time check alone is sufficient."
     ],
     "behavior_changes": [
      "ResStringPool::setTo now returns BAD_TYPE, with an ALOGW 'Bad string block: ...' log, for string pools whose styleCount exceeds UINT32_MAX/4 or whose styleCount*4 bytes of style offsets do not fit before stringsStart.",
      "APKs or resource data containing such a malformed string pool fail to load. The commit's manual test notes that an APK with an invalid styleCount failed to install.",
      "New tests ResourceTypesTest.ResStringPool_PotentialOverflowFindingStyles and ResStringPool_HeaderStyleCountExceedsStyleOffsetCount are added to libandroidfw_tests."
     ],
     "preconditions": [
      "A malformed resource string pool chunk (for example from an APK's resources) is parsed by ResStringPool::setTo. The commit's manual test shows that the APK install path reaches this code.",
      "The pool declares an inflated styleCount and still passes the pre-existing checks on stringCount, stringsStart and stylesStart."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base f1c0bc9dd174 in android-security-14.0.0_r28. Same 14-line hunk in ResStringPool::setTo and the same new test file; only line offsets differ (ResourceTypes.cpp @@ -652, Android.bp @@ -177). 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base e80f76ded67a in android-security-15.0.0_r16 (the analysed commit; ResourceTypes.cpp @@ -670, Android.bp @@ -222). 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base d5a52f737cab in android-security-16.0.0_r7, identical hunk (ResourceTypes.cpp @@ -673, Android.bp @@ -229). 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base d69b0c6010e5 in android-17.0.0_r1, identical hunk and test file. 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The ResourceTypes.cpp hunk is self-contained and identical from 14 through 17. It relies only on mEntryStyles, mHeader and std::numeric_limits. The new test file uses androidfw/StringPool.h (StringPool, StyleString, Span, FlattenUtf8, NoOpDiagnostics), BigBuffer and android::util::Copy. On trees where those helpers differ, the test can be adapted without affecting the fix."
     ],
     "confidence": "high",
     "unknowns": [
      "The consuming code that performed the out-of-bounds read (the style lookup that indexes mEntryStyles) is not included in the material.",
      "Where mEntryStyles is assigned, and the enclosing condition (for example styleCount != 0) of the new hunk, are not shown.",
      "Whether setTo elsewhere enforces stringsStart >= styleOffsetsStart is not visible. The new subtraction-based check implicitly relies on it.",
      "The two Cherrypick-From sources are internal and their individual contents are not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0059": {
     "cve": "CVE-2026-0059",
     "summary": "Adds remaining-buffer-length tracking (a bytes_left in/out parameter) to sdpu_build_attrib_seq and introduces base-size accounting in process_service_attr_rsp, so the SDP client no longer writes an attribute ID sequence past the end of the SDP_DATA_BUF_SIZE request buffer.",
     "component": "platform/packages/modules/Bluetooth, system/stack/sdp (SDP client discovery, sdp_discovery.cc / sdp_utils.cc)",
     "weakness": "Missing bounds check leading to heap buffer overflow (out-of-bounds write) when building an SDP request",
     "root_cause": "sdpu_build_attrib_seq (system/stack/sdp/sdp_utils.cc) wrote a data-element header plus 3 bytes per attribute (num_attrs * 3, computed in a uint16_t) into the caller's buffer with no knowledge of how much space remained. Its callers in sdp_discovery.cc, process_service_attr_rsp and process_service_search_attr_rsp, build the next request into a fixed osi_malloc(SDP_DATA_BUF_SIZE) buffer. process_service_attr_rsp did no space accounting at all, not even for the header, handle, max attr count or the remote-supplied continuation state length *p_reply. process_service_search_attr_rsp accounted for its fixed fields but then called sdpu_build_attrib_seq without passing the remaining length. With a large num_attr_filters, the attribute sequence could be written beyond the buffer.",
     "fix_analysis": [
      "system/stack/sdp/sdpint.h and sdp_utils.cc:sdpu_build_attrib_seq: the signature gains `uint16_t& bytes_left`. The function computes content_len as an int (5 for the wildcard, otherwise num_attrs * 3) and header_len (3 if content_len > 255, else 2). If bytes_left < content_len + header_len, it hits DCHECK(0) \"SDP: No space for attrib seq\" and returns p_out without writing anything. Otherwise it subtracts header_len + content_len from bytes_left and emits the sequence as before.",
      "sdp_discovery.cc:process_service_attr_rsp: adds `uint16_t bytes_left = SDP_DATA_BUF_SIZE`. After reserving the param-len field, it computes base_bytes = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 3 (PDU header) + 2 (param len) + 4 (handle) + 2 (max attr count) + 1 (cont state len) + (*p_reply if p_reply). If base_bytes > bytes_left, it calls sdp_disconnect(p_ccb, tSDP_STATUS::SDP_INVALID_CONT_STATE), frees p_msg and returns. Otherwise it subtracts base_bytes and passes bytes_left to both sdpu_build_attrib_seq calls (filtered and wildcard).",
      "sdp_discovery.cc:process_service_search_attr_rsp: keeps the existing base_bytes check, which is numerically identical (8 fixed bytes plus the continuation length). In the Android 15 commit only the comments are relabelled ('param len, max service records', 'continuation state'). bytes_left is now passed to both sdpu_build_attrib_seq calls.",
      "sdp_discovery.cc:sdp_snd_service_search_req: the same base_bytes sum (3 + 2 + 2 + 1 + cont_len) gets new comments and no numeric change. As the code context shows, this path already passed bytes_left to sdpu_build_uuid_seq.",
      "sdp_utils.cc: unrelated cleanup that moves loop index declarations into the for statements in sdpu_find_ccb_by_cid, sdpu_find_ccb_by_db, sdpu_allocate_ccb, sdpu_get_active_ccb_cid, sdpu_process_pend_ccb_same_cid, sdpu_process_pend_ccb_new_cid, sdpu_clear_pend_ccb, sdpu_is_base_uuid and sdpu_get_attrib_seq_len. No functional effect."
     ],
     "completeness": [
      "Both callers of sdpu_build_attrib_seq shown in the diff (process_service_attr_rsp and process_service_search_attr_rsp) now pass bytes_left. The prototype change in sdpint.h means any other caller has to be updated before it compiles.",
      "process_service_attr_rsp now accounts for all fixed fields and the remote continuation state before the attribute sequence, in line with process_service_search_attr_rsp and sdp_snd_service_search_req.",
      "On the failure path, sdpu_build_attrib_seq hits DCHECK(0) and returns p_out without writing the sequence. The callers shown do not check for this, so a request without an attribute ID list may be built. The continuation state written afterwards is already reserved in base_bytes. The rest of the send path is not shown in the material.",
      "The fix limits what is written. It does not reduce or validate num_attr_filters where the discovery DB is populated, and that code is not shown in the material."
     ],
     "behavior_changes": [
      "process_service_attr_rsp: if the fixed fields plus the continuation state do not fit in SDP_DATA_BUF_SIZE, the SDP connection is now dropped with tSDP_STATUS::SDP_INVALID_CONT_STATE instead of continuing.",
      "sdpu_build_attrib_seq: when the attribute sequence does not fit, it writes nothing (and hits a DCHECK in debug builds), so the request is built without an attribute ID list.",
      "sdpu_build_attrib_seq computes content_len as an int, so the size check sees the full num_attrs * 3 and rejects an oversized sequence before writing."
     ],
     "preconditions": [
      "The local device acts as an SDP client doing service discovery against a remote SDP server. The code runs while processing ServiceAttribute / ServiceSearchAttribute responses from the remote (process_service_attr_rsp, process_service_search_attr_rsp).",
      "A discovery DB (p_ccb->p_db) with a large num_attr_filters, and/or a remote-supplied continuation state length (*p_reply) that uses up buffer space. The exact threshold relative to SDP_DATA_BUF_SIZE is not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix: ce4be454822b in android-security-14.0.0_r28, marked [RESTRICT AUTOMERGE]. Same logic (bytes_left in sdpu_build_attrib_seq, new base_bytes check in process_service_attr_rsp), adapted to older surrounding code (memset-based ccb reset) and with reworded base_bytes comments in process_service_search_attr_rsp.",
      "15": "same fix: 59f3f429e37e in android-security-15.0.0_r16, a cherry-pick of ce1de9ef78ca. The code context at android-security-15.0.0_r17 shows the patched sdp_snd_service_search_req and the new sdpu_build_attrib_seq prototype.",
      "16": "adapted fix: 94566a29d020 in android-security-16.0.0_r7. Same logic on reformatted code (tSDP_STATE:: enums, braces). The loop cleanup also touches sdpu_dump_all_ccb.",
      "17": "fix present in base android-17.0.0_r1 (commit 5288e3e85ba9, [RESTRICT AUTOMERGE]). Same logic as the Android 14 patch, with matching sdp_discovery.cc and sdpint.h blob hashes. The sdp_utils.cc blob hashes and line offsets differ."
     },
     "backport_notes": [
      "Single commit. The diff context in every branch shows the existing base_bytes / bytes_left handling in sdp_snd_service_search_req that this fix builds on. Because the sdpint.h prototype changed, any downstream caller of sdpu_build_attrib_seq must be updated. The loop-variable cleanups in sdp_utils.cc are not functional and can be dropped to reduce conflicts.",
      "The latest security tags show only partial line matches for the patched region (5/8 or 6/8). Check that sdpu_build_attrib_seq takes uint16_t& bytes_left and that process_service_attr_rsp has the base_bytes check."
     ],
     "confidence": "high",
     "unknowns": [
      "The value of SDP_DATA_BUF_SIZE and the maximum num_attr_filters allowed in tSDP_DISCOVERY_DB are not shown. The material therefore cannot show how much remote influence (via the continuation state length) versus local configuration is needed to reach the overflow.",
      "Why the latest security tags match only 5/8 or 6/8 lines of the patched region is not explained by the material.",
      "The relationship to the earlier b:239414876 fix is only mentioned in the commit message as 'similar validation'."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0060": {
     "cve": "CVE-2026-0060",
     "summary": "Hardens the \"Experimental: Enable ANGLE\" developer option in Settings. On Android 15 (and 16/17) the switch summary and the reboot dialog are reworded to warn users to back up data and that a factory reset may be needed (plus a translation import). On Android 14 the switch is also gated behind the debug.graphics.angle.developeroption.enable system property.",
     "component": "platform/packages/apps/Settings, Developer options / graphics driver (GraphicsDriverEnableAngleAsSystemDriverController, res/values*/strings.xml)",
     "weakness": "Insufficiently safeguarded dangerous developer setting: switching the system OpenGL ES driver to ANGLE can, per the commit message, leave a commercial device unusable until factory reset. It was exposed as a toggle with no adequate warning (15+) and, on 14, gated only by ANGLE support",
     "root_cause": "The developer-options switch handled by GraphicsDriverEnableAngleAsSystemDriverController makes ANGLE the system OpenGL ES driver (the controller defines PROPERTY_PERSISTENT_GRAPHICS_EGL and ANGLE_DRIVER_SUFFIX = \"angle\"; the exact write path is not shown). The fix commit message says the switch is meant for dogfooders and developers, and that on a released commercial device it \"can brick the device into unusable state and need a factory reset to fix\". Before the fix, the switch summary mentioned only camera/video app incompatibility, and the reboot dialog said only \"A reboot is required to change the system OpenGL ES driver\". A user could therefore persistently disable their own device (DoS) without being warned. On Android 14 the switch was titled \"Enable ANGLE\" and was gated only by the ANGLE-support check (isAngleSupported).",
     "fix_analysis": [
      "Android 15, commit 46f81fda8cd3 (res/values/strings.xml): adds 'Please backup your device data before turning on the switch' to enable_angle_as_system_driver_summary. Adds 'Please back up your device data before rebooting. In case the reboot failed, you need a factory reset to restore the device' to reboot_dialog_enable_angle_as_system_driver. No Java code is changed.",
      "Android 15, commit 73a18cca95cc (res/values/strings.xml): rewords both strings to their final form ('Warning: ANGLE will be set as the default OpenGL ES driver ... Back up any important data before turning this on.' and 'To change the system OpenGL ES driver, the device will restart. Back up any important data before restarting. If restarting fails, a factory reset will be needed to use the device.').",
      "Android 15, commit 2b5834d47f28 (84 res/values-<locale>/strings.xml files, 'Import translations. DO NOT MERGE ANYWHERE'): replaces the two localized strings and changes their msgids (2170215556348477481 to 1552818142467085308, and 2619263039763150810 to 14147627096976157). The translated text matches the first (46f81fda8cd3) wording, not the final one. values-fr-rCA contains the English source text instead of a translation.",
      "Android 14, commit 2097b24534af (GraphicsDriverEnableAngleAsSystemDriverController, GraphicsDriverSystemPropertiesWrapper, strings.xml, tests): adds PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION = 'debug.graphics.angle.developeroption.enable' and isAngleDeveloperOptionEnabled(). After the existing isAngleSupported() handling, the preference is forced to setEnabled(false) unless that property is true. Also adds getBoolean(key, def) to the wrapper and the Injector, logs the persist.graphics.egl value, retitles the switch 'Experimental: Enable ANGLE' with the 'Warning: ... in experiment ...' summary (no backup wording), and updates the Robolectric and JUnit tests to set the debug property to true."
     ],
     "completeness": [
      "On Android 15/16/17 the fix only changes text: the summary and reboot-dialog strings. No Java code changes, so the controller works as before. No gate, extra confirmation or recovery mechanism is added.",
      "The localized strings (2b5834d47f28) carry the first English wording. The material has no translations of the final 73a18cca95cc wording, and fr-rCA shows untranslated English.",
      "On Android 14 the fix is stronger: unless debug.graphics.angle.developeroption.enable is set, the preference is disabled, so the switch cannot be used from the UI by default. The 14 fix strings do not include the 'back up your device data' wording.",
      "The material does not show whether the 15/16 branches already contain the debug-property gate added on 14."
     ],
     "behavior_changes": [
      "Android 15/16: the ANGLE switch summary and the reboot confirmation dialog show longer warning text about backing up data and a possible factory reset. No functional change.",
      "Android 15: 84 locale string files are updated with new msgids for the two strings.",
      "Android 14: the 'Experimental: Enable ANGLE' preference is disabled unless debug.graphics.angle.developeroption.enable is true, even when ANGLE is reported as supported. The persist.graphics.egl value is logged with Log.v. GraphicsDriverSystemPropertiesWrapper gains a getBoolean method that every implementer must provide. The title changes from 'Enable ANGLE' to 'Experimental: Enable ANGLE'."
     ],
     "preconditions": [
      "Developer options are enabled and the ANGLE switch is reachable in Settings.",
      "The device reports ANGLE support (PROPERTY_RO_GFX_ANGLE_SUPPORTED equals \"true\", checked by isAngleSupported per the 14 diff). Otherwise the preference is already disabled.",
      "On Android 14 after the fix, debug.graphics.angle.developeroption.enable must also be true (e.g. via adb shell setprop).",
      "The user toggles the switch and accepts the reboot. The harm depends on ANGLE failing as the system GLES driver on the device, which the material does not show."
     ],
     "per_major": {
      "14": "adapted fix: commit 2097b24534af in android-security-14.0.0_r28 adds the debug.graphics.angle.developeroption.enable gate in GraphicsDriverEnableAngleAsSystemDriverController, the 'Experimental' title and the first-generation warning summary (no backup wording). 5/8 lines of the patched region are present at android-security-14.0.0_r29. Which wording is present there is not shown.",
      "15": "strings-only fix: 46f81fda8cd3 (adds the backup warning), then 73a18cca95cc (rewording) in res/values/strings.xml, plus the translation import 2b5834d47f28, all in android-security-15.0.0_r16. 6/8 lines are present at android-security-15.0.0_r17, and res/values/strings.xml there has the final English wording.",
      "16": "same strings fix: 32c6cb147d3e 'Update enable ANGLE warning message' in android-security-16.0.0_r7 applies the final wording. Its diff base already has the first backup-warning wording. 6/8 lines are present at android-security-16.0.0_r8.",
      "17": "fixed in base: translation import 1c2cf9256912 in android-17.0.0_r1 carries the first-generation localized warnings. 4/8 lines are present at android-security-17.0.0_r1. Whether the final English wording is present is not shown."
     },
     "backport_notes": [
      "Apply in order: 46f81fda8cd3, then 73a18cca95cc, then 2b5834d47f28. The last one is marked DO NOT MERGE ANYWHERE and is tied to the 15 locale layout, so expect conflicts if locale files differ. The translations match only the first wording.",
      "A 14-style backport (2097b24534af) also needs getBoolean added to GraphicsDriverSystemPropertiesWrapper and its Injector, and the tests updated to set debug.graphics.angle.developeroption.enable=true. Otherwise the controller tests will see the preference disabled."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete failure mode, and how the controller writes persist.graphics.egl, appear only in the commit message, not in the code shown.",
      "Whether Android 15 and 16 already contain the debug.graphics.angle.developeroption.enable gate added on 14.",
      "Whether translations of the final (73a18cca95cc) wording exist on any branch.",
      "Which string revision is present on android-security-14.0.0_r29 and android-security-17.0.0_r1.",
      "Whether non-Settings paths (e.g. setting the property directly from a shell) are in scope; the fix only touches the Settings UI."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0061": {
     "cve": "CVE-2026-0061",
     "summary": "WindowState now explicitly hides/shows its own server-owned surface (mSurfaceControl) in the pending transaction whenever policy visibility changes, instead of relying only on the WindowStateAnimator hiding the client-accessible surface.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Server-side visibility policy enforced on a client-controllable object (client can re-show the surface the server hid); improper enforcement of a security decision",
     "root_cause": "When WindowManager decided a window must be hidden by policy (hide(), whose log tag is hideLw, or checkPolicyVisibilityChange after an animation), it only called mWinAnimator.hide(...), which acts on the client surface that the client's ViewRootImpl can access. Per the commit message, the client can change the visibility of that surface itself, so a policy-hidden window (e.g. an overlay that should not be visible while Settings is shown) could become visible again. The WindowState's own surface (mSurfaceControl), which the client cannot reach, was not hidden by the policy path.",
     "fix_analysis": [
      "WindowState.java:checkPolicyVisibilityChange(): after mWinAnimator.hide(getPendingTransaction(), \"checkPolicyVisibilityChange\") when !isVisibleByPolicy(), also call getPendingTransaction().hide(mSurfaceControl) if mSurfaceControl != null, so the post-animation policy hide applies to the server-owned surface.",
      "WindowState.java:show(boolean doAnimation, boolean requestAnim): after setting LEGACY_POLICY_VISIBILITY and mLegacyPolicyVisibilityAfterAnim = true, call getPendingTransaction().show(mSurfaceControl) if non-null, before the optional TRANSIT_ENTER animation.",
      "WindowState.java:hide(boolean doAnimation, boolean requestAnim) (log tag hideLw): after the focus handling (mFocusMayChange) inside the policy-hide block, call getPendingTransaction().hide(mSurfaceControl) if non-null. The enclosing condition is not shown in the diff.",
      "WindowStateTests.java:testIsOnScreen_hiddenByPolicy: verifies mTransaction.hide(window.mSurfaceControl) after hide(false,false) and mTransaction.show(window.mSurfaceControl) after show(false,false)."
     ],
     "completeness": [
      "Covers three policy-visibility points in WindowState: the hide in hide(), the deferred hide in checkPolicyVisibilityChange(), and the re-show in show().",
      "All three additions are guarded by mSurfaceControl != null. The diff does not show whether a WindowState surface created after the window was already policy-hidden starts out hidden, or whether policy is re-applied at that point.",
      "Only the WindowState's mSurfaceControl is touched. The existing mWinAnimator.hide call is kept. Other code paths that hide windows without going through hide()/checkPolicyVisibilityChange() are not in the diff, and the material does not show their state.",
      "For an animated hide, the test shows the window stays policy-visible during the animation and becomes not visible by policy after cancelAnimation(). The exact path that then hides mSurfaceControl is inferred to be checkPolicyVisibilityChange() but is not shown."
     ],
     "behavior_changes": [
      "Window hide/show by policy now issues SurfaceControl.Transaction hide/show on WindowState.mSurfaceControl via the pending transaction, in addition to the animator-level hide.",
      "show() now shows the WindowState surface when policy visibility is restored, whether or not doAnimation is set.",
      "No API changes are visible. WindowStateTests now verifies the transaction calls."
     ],
     "preconditions": [
      "A window whose visibility WindowManager controls by policy (hide()) while the app still has a live surface. The commit's manual test uses an overlay window whose client repeatedly shows its ViewRootImpl surface while Settings is launched.",
      "Reaching the changed code requires the server to hide the window by policy (hide()), complete a deferred hide (checkPolicyVisibilityChange()), or restore it (show()). mSurfaceControl must be non-null for the new calls to take effect."
     ],
     "per_major": {
      "14": "same fix applied to different context: b53981d87f80 in android-security-14.0.0_r28. The three mSurfaceControl hide/show insertions are identical, but checkPolicyVisibilityChange() uses mWinAnimator.hide(getGlobalTransaction(), ...) and the test context uses ANIMATION_TYPE_WINDOW_ANIMATION. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: 0922d84b1c64 in android-security-15.0.0_r16 (cherry-picked from 837d1ffa54b6, Change-Id I326fcaf71f901a11cf4aee3d734494090a1d0fb3). 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: 4518f78f8b34 in android-security-16.0.0_r7, identical hunks at different line offsets. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix in base: e58fd9cd11e4 in android-17.0.0_r1 (context uses getGlobalTransaction() in checkPolicyVisibilityChange). 7/8 lines of the patched region present at android-security-17.0.0_r1. The material does not show which line is missing."
     },
     "backport_notes": [
      "Single commit with three small insertions in WindowState.java, keyed on mSurfaceControl and getPendingTransaction(). Google kept the new calls on getPendingTransaction() whether the adjacent mWinAnimator.hide uses getPendingTransaction() (15/16) or getGlobalTransaction() (14/17).",
      "Test context differs per branch only in the SurfaceAnimator animation type constant used by the existing test (TOKEN_TRANSFORM on 15/16, WINDOW_ANIMATION on 14/17)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which policy (e.g. which flag or caller) triggers hide() for the overlay case in the commit message, nor how the client re-shows the ViewRootImpl surface.",
      "The material does not show whether surface creation for a window that is already policy-hidden (mSurfaceControl null at hide time) honors policy.",
      "The material does not show which line of the patched region is missing at android-security-17.0.0_r1 (7/8 present).",
      "The material does not show the enclosing condition around the new hide call in hide()."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0067": {
     "cve": "CVE-2026-0067",
     "summary": "In external/dng_sdk, the libdng_sdk build is changed so that integer overflows detected by UBSan no longer abort the process through the ubsan-minimal runtime. Instead they call a library-local runtime (ubsan-replacement/ubsan_throwing_runtime.cpp) that throws std::runtime_error, so a crafted DNG that used to cause an abort now raises a catchable C++ exception.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG/raw image parsing used by image decoding)",
     "weakness": "Integer overflow (signed/unsigned) caught by the sanitizer, which aborted the process (crash, DoS). The diff does not show the exact overflow sites.",
     "root_cause": "libdng_sdk inherits from libdng_sdk-defaults a sanitize block with misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] (Android.bp lines 144-149). Per the commit comment, this configuration used the ubsan_minimal runtime, and per the commit message that runtime handles an error with a hard abort. Any instrumented overflow reached while processing input therefore killed the hosting process instead of being reported as an error. The commit message says C++ exceptions are already enabled in the library (rtti: true and cppflags [\"-fexceptions\"] in the defaults), so throwing an exception is a workable error path instead of aborting. The commit references seven bug IDs, but the material does not describe them.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so that Soong's sanitizer configuration, and with it the ubsan_minimal runtime, is not applied to this module. The comment says Soong has no dedicated flag for this, but setting never in combination with -fsanitize= cflags works.",
      "Android.bp, cc_library libdng_sdk: replaces cflags [\"-DqDNGValidate=0\"] with [\"-DqDNGValidate=0\", \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\", \"-fno-sanitize-link-runtime\"]. The same two overflow checks stay instrumented, no sanitizer runtime is linked, and the library itself supplies the __ubsan_handle_* symbols.",
      "Android.bp, cc_library libdng_sdk: adds the source ubsan-replacement/ubsan_throwing_runtime.cpp. On 14/15/16 this is a new srcs block in the module; on Android 17 the file is appended to the module's existing srcs list after source/dng_xy_coord.cpp.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new file): defines five extern \"C\", hidden-visibility handlers, __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message, so a detected overflow now unwinds as a C++ exception instead of aborting.",
      "OWNERS (14/15/16 only): adds bug component 1075130, includes the frameworks/base graphics OWNERS and marks the existing owners as LAST_RESORT_SUGGESTION. This has no functional effect."
     ],
     "completeness": [
      "The fix does not correct any individual overflowing computation in the dng_sdk sources. It only changes what happens when an instrumented overflow is detected (throw instead of abort).",
      "Only the libdng_sdk cc_library module changes. libdng_sdk-defaults still sets sanitize.misc_undefined, so any other module that uses those defaults (none is shown in the material) keeps the old abort behavior.",
      "Only five handler entry points are provided (add/sub/mul/negate/divrem), matching the two overflow sanitizers enabled by the new -fsanitize flag. No other UBSan checks are enabled, which matches the previous misc_undefined configuration.",
      "The material does not show whether every caller of libdng_sdk catches the thrown std::runtime_error. An exception that is not caught would still terminate the process.",
      "The handlers are declared without parameters, while the compiler passes arguments to them. This is not a faithful signature."
     ],
     "behavior_changes": [
      "A detected signed or unsigned integer overflow inside libdng_sdk now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting the process. Callers that catch the exception see a failure instead of a crash.",
      "libdng_sdk is built with sanitize.never = true, so Soong no longer applies its sanitizer configuration to this module. Instrumentation is added back by hand through -fsanitize, for the two overflow checks only (general platform knowledge: never: true also suppresses other sanitizers Soong might otherwise add to the module).",
      "The ubsan-minimal runtime is no longer linked into libdng_sdk (-fno-sanitize-link-runtime). The handler symbols are local to the library and have hidden visibility."
     ],
     "preconditions": [
      "libdng_sdk must parse a DNG/raw image that makes an instrumented arithmetic operation overflow. The commit's test (ImageDecoderTest#testInterestingDngs) points to the Android image decoding path for DNG files as the relevant entry point.",
      "The DngCreator.cpp and DngUtils.cpp index hits write DNGs through img_utils/TiffWriter and do not appear to call libdng_sdk. They are not a confirmed reachability path (per index search)."
     ],
     "per_major": {
      "14": "Same fix: commit 42e65547eb33 in android-security-14.0.0_r28, with an Android.bp hunk identical to 15 (1978b39..3d95a7b) and the same new runtime file. The OWNERS hunk differs only in the owner lines that already existed. At android-security-14.0.0_r29, the per-major check found only 3/5 lines of the patched region.",
      "15": "Same fix: commit 1ec41401b3d4 in android-security-15.0.0_r16. The code context at android-security-15.0.0_r17 shows the new Android.bp block (lines 171-187) and the runtime file. The line-matching check reports 3/4 lines of the patched region.",
      "16": "Same fix: commit 74a5a91d169a in android-security-16.0.0_r7, with an identical Android.bp hunk and runtime file. The OWNERS hunk is adapted to the existing Android 16 OWNERS content. At android-security-16.0.0_r8, the per-major check found only 3/5 lines of the patched region.",
      "17": "Adapted fix: original change 81ded662eee0 in android-17.0.0_r1, linked through a related bug. The Android.bp layout differs (hunk at line 105), the new source is appended to the module's existing srcs list after source/dng_xy_coord.cpp, and there is no OWNERS change. At android-security-17.0.0_r1, the per-major check found 5/6 lines of the patched region."
     },
     "backport_notes": [
      "Single commit. On 14/15/16 the hunk adds a new srcs block to libdng_sdk next to the cflags change. Where libdng_sdk already lists its own srcs (the 17 layout), append ubsan-replacement/ubsan_throwing_runtime.cpp to that list instead. The fix depends on libdng_sdk-defaults already setting rtti: true and cppflags [\"-fexceptions\"]."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow. The material does not describe the seven referenced bugs.",
      "Whether all in-platform callers of libdng_sdk catch the new exception, rather than letting it terminate the process.",
      "Whether any other Android.bp module uses libdng_sdk-defaults and still gets the aborting ubsan-minimal runtime.",
      "Why the per-major line-matching check reports only partial presence of the patched region (3/5 on 14 and 16, 3/4 on 15, 5/6 on 17) in the latest security tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0070": {
     "cve": "CVE-2026-0070",
     "summary": "DevicePolicyManagerService.setApplicationHidden now refuses to hide packages listed in a new framework resource array application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings), in addition to the existing policy-exempt list returned by listPolicyExemptAppsUnchecked.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (device policy / managed profile)",
     "weakness": "Missing validation of the target package in a privileged policy API: critical system packages were not excluded from setApplicationHidden, allowing a device-breaking state (DoS).",
     "root_cause": "In services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java, setApplicationHidden() only rejected packages returned by listPolicyExemptAppsUnchecked(mContext). The related policy_exempt_apps array is empty in AOSP core/res. Within the shown check, nothing else protected the packages the system needs to function. Per the commit message, hiding such critical apps in the work profile could cause the device to fail to boot.",
     "fix_analysis": [
      "core/res/res/values/policy_exempt_apps.xml: adds a new string-array application_hidden_policy_exempt_apps containing com.android.nfc, com.android.settings and com.android.providers.settings, documented as apps never affected by DevicePolicyManager.setApplicationHidden.",
      "core/res/res/values/symbols.xml: exposes the new array to framework code via <java-symbol type=\"array\" name=\"application_hidden_policy_exempt_apps\" />.",
      "DevicePolicyManagerService.java: new private helper isPackageExemptFromHiding(String packageName) reads R.array.application_hidden_policy_exempt_apps from mContext.getResources() and returns true on an exact String.equals match.",
      "DevicePolicyManagerService.java:setApplicationHidden: the existing check `if (exemptApps.contains(packageName))` becomes `if (exemptApps.contains(packageName) || isPackageExemptFromHiding(packageName))`. On a match it logs 'ignoring %s as it's on policy-exempt list' and returns false without changing the hidden state. On Android 15 the check sits after enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, ...).",
      "DevicePolicyManagerService.java:dumpResources: dumps the new array alongside policy_exempt_apps and vendor_policy_exempt_apps, so it shows up in dumpsys."
     ],
     "completeness": [
      "Covers only the setApplicationHidden() entry point in DevicePolicyManagerService. No other DPM method or PackageManager path is modified in the diff.",
      "Protection is a fixed list of three exact package names in a core resource. Any other package the device needs is not protected unless an OEM overlays the array. The new array has no vendor_ counterpart (unlike vendor_policy_exempt_apps), so OEM additions would need an overlay of the core resource.",
      "Matching is an exact String.equals on the package name. The added condition does not test the target user, so within the shown check it is not limited to managed profiles.",
      "The diff contains no logic to un-hide packages that were already hidden before the patch. It only prevents new hide requests."
     ],
     "behavior_changes": [
      "setApplicationHidden() now returns false and logs a debug message instead of hiding com.android.nfc, com.android.settings or com.android.providers.settings.",
      "dumpsys device_policy output gains an 'application_hidden_policy_exempt_apps' section.",
      "New overlayable framework resource array application_hidden_policy_exempt_apps."
     ],
     "preconditions": [
      "Caller must pass enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, ...) in setApplicationHidden (from the Android 15 diff context).",
      "The target package is one of the critical system packages now listed. Per the commit message, the failure mode was observed when hiding them in a work (managed) profile."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 987b313aefa0 in android-security-14.0.0_r28. The logic is identical and only the surrounding context differs: symbols.xml lacks the config_securityStatePackages line, and the line before the check in setApplicationHidden is a closing brace. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 060fe5399350 in android-security-15.0.0_r16 (the commit analysed here). 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base dc3785a739bc in android-security-16.0.0_r7. The hunks are identical; the symbols.xml context also includes stoppable_fgs_system_apps. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base 4a97f5239d57 in android-17.0.0_r1, with identical logic. 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit across three files (policy_exempt_apps.xml, symbols.xml, DevicePolicyManagerService.java). The resource array, the java-symbol and the service changes must land together, or the R.array reference will not compile. Forks that have edited core/res/res/values/policy_exempt_apps.xml must merge the new array."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the exact boot failure mechanism beyond the commit message statement that hiding these apps in the work profile may cause failure to boot.",
      "Which resources listPolicyExemptAppsUnchecked reads is not shown in the material.",
      "Whether other DPM/PackageManager paths that can disable or suspend these packages were considered is not shown.",
      "Why these three specific packages were chosen, and whether the list is complete, is not shown.",
      "Whether earlier code in setApplicationHidden applies user-specific handling is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0074": {
     "cve": "CVE-2026-0074",
     "summary": "Adds hidden process-level and thread-level default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and has the Launcher (Quickstep) process register a default listener that rejects non-allowlisted image mime types and downscales images whose estimated decoded size exceeds the configured 300 MB limit, so that arbitrarily large images are no longer decoded at full size through ImageDecoder in the Launcher process.",
     "component": "platform/frameworks/base (graphics/java/android/graphics/ImageDecoder.java) and platform/packages/apps/Launcher3 (quickstep, QuickstepProcessInitializer / LauncherProcessImageListener)",
     "weakness": "Missing resource limit on decoded image size (uncontrolled memory allocation leading to DoS)",
     "root_cause": "ImageDecoder.callHeaderDecoded only invoked the listener passed to the individual decode call; there was no way to enforce process-wide limits on decode parameters. The Launcher process decoded images through ImageDecoder without any process-wide bound on the image dimensions or mime type, so an image with very large declared dimensions (the new code comments: \"Remote size may have returned a giant image\") could be decoded at full size and consume excessive memory in the Launcher process. The bulletin classifies the result as DoS (High).",
     "fix_analysis": [
      "platform/frameworks/base, graphics/java/android/graphics/ImageDecoder.java: adds static sDefaultProcessListener (guarded by sListenerLock) and a ThreadLocal sDefaultThreadListener, with @hide setters/getters setDefaultProcessListener/getDefaultProcessListener and setDefaultThreadListener/getDefaultThreadListener.",
      "ImageDecoder.callHeaderDecoded(listener, src): now reads both defaults; if any of the per-call listener, thread default or process default is non-null it builds ImageInfo and invokes the thread default if set, else the process default, then always invokes the per-call listener last. Previously it only ran the per-call listener when non-null.",
      "platform/packages/apps/Launcher3, quickstep/src/com/android/quickstep/LauncherProcessImageListener.kt (new): implements ImageDecoder.OnHeaderDecodedListener. onHeaderDecoded throws IOException if info.mimeType is null or (lowercased) not in allowedMimeTypes; otherwise computes getPreferredSize(width, height, maxMemoryBytes) assuming 8 bytes per pixel (RGBA_F16 worst case) and, if width*height*8 > maxMemoryBytes, calls decoder.setTargetSize with aspect-preserving scaled dimensions (minimum 1x1).",
      "quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java: init() now calls setupImageDecoder(context), which reads R.integer.max_launcher_memory_mb (300, converted as 300*1024*1024 bytes) and R.array.allowed_image_mime_types (png, jpeg, webp, gif, bmp, x-ico, vnd.wap.wbmp, heif, heic, avif) from quickstep/res/values/config.xml and registers a LauncherProcessImageListener via ImageDecoder.setDefaultProcessListener.",
      "Tests: LauncherProcessImageListenerTest.kt (unit tests via ImageDecoderWrapper fake) and new CtsGraphicsTestCases ImageDecoderTest cases testDefaultProcessListener, testDefaultThreadListener, testDefaultsAndLocalListener (named in the commit message, not in the diff)."
     ],
     "completeness": [
      "The limit is enforced only for decodes that go through android.graphics.ImageDecoder (hooked in callHeaderDecoded); the material does not show whether the Launcher process decodes images through any other path.",
      "The default listener is registered only in QuickstepProcessInitializer under quickstep/; Launcher3 builds that do not use the Quickstep process initializer do not register it.",
      "Ordering: the process default runs first and the per-call listener runs last, so a per-call listener in Launcher that calls setTargetSize itself can override the downscale chosen by LauncherProcessImageListener. Likewise, any code setting a thread-level default via setDefaultThreadListener on a thread causes the process default to be skipped on that thread (none shown in the material).",
      "The 300 MB bound is an estimate based on header dimensions times 8 bytes per pixel; animated image frame count is not factored in (ImageInfo.isAnimated is not consulted by the listener).",
      "The commit messages cite flag com.android.graphics.hwui.flags.image_decoder_default_listener, but neither diff contains a flag check; the new code is unconditionally active in the shown diffs."
     ],
     "behavior_changes": [
      "In the Launcher (Quickstep) process, any ImageDecoder decode of an image whose mime type is null or not in allowed_image_mime_types now fails with IOException(\"Image mime type (...) is not allowed.\").",
      "In the Launcher process, images with width*height*8 bytes > 300 MB are now decoded downscaled (aspect ratio preserved) rather than at native size.",
      "ImageDecoder.callHeaderDecoded now constructs an ImageInfo and invokes default listeners even when the caller passed no listener, if a process or thread default is set; the per-call listener always runs last.",
      "New @hide static API on ImageDecoder: setDefaultProcessListener/getDefaultProcessListener and setDefaultThreadListener/getDefaultThreadListener."
     ],
     "preconditions": [
      "An image from an untrusted or remote source is decoded in the Launcher process via android.graphics.ImageDecoder (the new code comments refer to a remote source possibly returning a giant image); the exact Launcher decode path(s) involved are not shown in the material.",
      "Launcher is built with the quickstep variant so QuickstepProcessInitializer.init runs at process start."
     ],
     "per_major": {
      "14": "adapted fix: Launcher3 c466fd8d9215 in android-security-14.0.0_r28 (same LauncherProcessImageListener, config and QuickstepProcessInitializer change, with context differences such as @TargetApi annotation and taskbar_icon_size 48.4); 8/8 lines present at android-security-14.0.0_r29. The corresponding frameworks/base ImageDecoder commit for 14 is not shown in the material but is required for setDefaultProcessListener to exist.",
      "15": "same fix: frameworks/base e3cb029648f4 (ImageDecoder default listeners) plus Launcher3 8fd9af41d56b in android-security-15.0.0_r16; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: frameworks/base 4abc0b8da1e9 in android-security-16.0.0_r7 (identical ImageDecoder diff, offset by two lines); 7/7 lines present at android-security-16.0.0_r8. The Launcher3 counterpart for 16 is not listed in the per-major facts.",
      "17": "same fix: Launcher3 56e4dc5d2b9a in android-17.0.0_r1 (same content, slightly different QuickstepProcessInitializer context); 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Two-repo dependency: the Launcher3 change calls the @hide ImageDecoder.setDefaultProcessListener, so the frameworks/base ImageDecoder commit (Change-Id I694d45deb8755aab5e994c12d2627fe08b5ce78c) must land first or in the same build; the Launcher3 change (Change-Id I79d705160a12efd0f4235a4c6bfd104b5b873dfa) adds a Kotlin source file and two new resources in quickstep/res/values/config.xml.",
      "Despite the Flag: lines in the commit messages, neither diff gates the code on the aconfig flag, so no flag plumbing is needed to make the backport active."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which specific Launcher code path decodes the untrusted image (the diffs only add a process-wide default, not a change at a call site).",
      "Whether the Launcher process decodes untrusted images through APIs other than ImageDecoder, which this change would not cover.",
      "Whether the frameworks/base ImageDecoder commit was applied to Android 14 and the Launcher3 commit to Android 16 (not listed in the per-major facts).",
      "The content of the new CtsGraphicsTestCases ImageDecoderTest cases (only named in the commit message).",
      "Why the Android 17 patched region shows 7/8 lines present at android-security-17.0.0_r1."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0075": {
     "cve": "CVE-2026-0075",
     "summary": "ContactsProvider2 now replaces SQLiteException messages containing \"json\" with a generic \"Stripped exception message\" for callers that lack READ_PERMISSION (query) or WRITE_PERMISSION (insert/update/delete). query() also now reads the cursor count inside the try block, so SQLite errors thrown lazily are caught and stripped as well.",
     "component": "platform/packages/providers/ContactsProvider, ContactsProvider2 (CP2) query/insert/update/delete entry points",
     "weakness": "Information exposure through error messages (SQLite exception text returned to unprivileged callers, usable as a side channel)",
     "root_cause": "Per the commit message and diff, ContactsProvider2.query/insert/update/delete caught any Exception, logged it and rethrew it unchanged to the caller. CP2 imposes no strict SQL checks on what callers supply, so a caller without READ_CONTACTS could send malformed queries and parse the text of the resulting SQLiteException as a side channel. The fix singles out messages containing \"json\". In query(), cursor.getCount() was called only in the finally block, so exceptions raised while the cursor was being filled never passed through the catch block.",
     "fix_analysis": [
      "src/com/android/providers/contacts/ContactsProvider2.java: adds a private helper maybeStripAndThrowSQLiteExceptionWithJson(Exception e, String permission, LogFields.Builder logBuilder). It acts only when all three hold: e is a SQLiteException, getContext().checkCallingPermission(permission) is not PERMISSION_GRANTED, and e.getMessage() lower-cased with Locale.ROOT contains \"json\". It then builds a new SQLiteException(\"Stripped exception message\"), records it in logBuilder and throws it in place of the original. The code comment says checkCallingPermission (not checkCallingOrSelfPermission) is used on purpose, so calls from non-binder threads are not given the raw error message.",
      "ContactsProvider2.insert, ContactsProvider2.update, ContactsProvider2.delete: each catch (Exception e) block now calls maybeStripAndThrowSQLiteExceptionWithJson(e, WRITE_PERMISSION, logBuilder) before the existing logBuilder.setException(e); throw e.",
      "ContactsProvider2.query: the catch block calls maybeStripAndThrowSQLiteExceptionWithJson(e, READ_PERMISSION, logBuilder). The try block now calls cursor.getCount() (when cursor != null) and stores the result with logBuilder.setResultCount. Exceptions thrown while the cursor is consumed are therefore raised inside the try and reach the catch/strip logic. The finally block is reduced to LogUtils.log(logBuilder.build()) and no longer calls getCount().",
      "Adds import android.database.sqlite.SQLiteException."
     ],
     "completeness": [
      "Covers the four overridden ContentProvider entry points in ContactsProvider2 (query, insert, update, delete), for callers that do not hold READ_PERMISSION (query) or WRITE_PERMISSION (insert/update/delete).",
      "Filtering is keyword based. Only SQLiteException messages containing \"json\" (case-insensitive) are stripped. Unprivileged callers still receive the text of any other SQLite error unchanged. Exceptions that are not SQLiteException are untouched.",
      "The commit adds no SQL validation and does not restrict which SQLite functions callers can use. It only sanitizes the error text (the commit message itself notes CP2 does not impose strict SQL checks).",
      "Callers holding the permission still receive raw messages. The commit treats this as acceptable because such callers can read the database anyway.",
      "Other provider entry points (e.g. call(), bulkInsert, applyBatch, openAssetFile) are not touched in the diff. The material does not show whether they route through the patched methods."
     ],
     "behavior_changes": [
      "When an unprivileged caller's operation fails with a SQLiteException mentioning \"json\", the caller now receives a SQLiteException with the fixed message \"Stripped exception message\" instead of the original text. The original exception is not chained as a cause.",
      "query() now calls cursor.getCount() inside the try block before returning the cursor. Errors raised while the cursor is consumed surface through the catch block in query() rather than from the finally block.",
      "For stripped cases, the provider's LogFields log records the stripped exception rather than the original. The result count is set only when a cursor was obtained (previously 0 was logged for a null cursor)."
     ],
     "preconditions": [
      "Caller can reach ContactsProvider2.query without READ_PERMISSION, or insert/update/delete without WRITE_PERMISSION, and can control SQL input (e.g. selection, sort order, values) so that SQLite raises an exception whose message contains \"json\".",
      "The material does not show which CP2 URIs or paths are reachable without those permissions."
     ],
     "per_major": {
      "14": "same fix: commit 7d344b678bde in android-security-14.0.0_r28, same hunks as the 15 change at different offsets; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 930817423647 in android-security-15.0.0_r16 (cherry-pick of 6044ca5db93f); 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit bb9f36b3ff6d in android-security-16.0.0_r7, same hunks at different offsets (query hunk near line 6020); 8/8 present at android-security-16.0.0_r8.",
      "17": "same fix: commit 819ebe4a7c68 in base android-17.0.0_r1 (same content as the 14 change); 8/8 present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit in ContactsProvider2.java. It relies only on the existing LogFields.Builder/LogUtils logging and the READ_PERMISSION/WRITE_PERMISSION constants. If a target branch lacks the LogFields logging wrapper around query/insert/update/delete, the helper's logBuilder parameter must be adapted."
     ],
     "confidence": "high",
     "unknowns": [
      "Which SQLite json functions or error strings were used in the original report, and exactly what the messages leaked. The material only shows the \"json\" keyword filter.",
      "Whether other CP2 entry points (call, bulkInsert, applyBatch, file open paths) can surface SQLiteException text to unprivileged callers. They are not covered by the diff and not shown in the material.",
      "Which permission strings READ_PERMISSION and WRITE_PERMISSION resolve to (the commit message only names READ_CONTACTS).",
      "Whether a regression test exists for the 'Test: atest ContactsProviderTests' line. No test diff is included."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0076": {
     "cve": "CVE-2026-0076",
     "summary": "Adds a check in ResXMLTree::validateNode (libs/androidfw/ResourceTypes.cpp) that rejects a START_ELEMENT node whose ResXMLTree_attrExt::attributeSize is smaller than sizeof(ResXMLTree_attribute), returning BAD_TYPE instead of accepting the node based only on the attributeSize*attributeCount product check.",
     "component": "platform/frameworks/base, libs/androidfw (ResourceTypes binary XML parser, ResXMLTree)",
     "weakness": "Missing minimum-size validation on an attacker-supplied element size, leading to out-of-bounds read (per commit message)",
     "root_cause": "ResXMLTree::validateNode validated a START_ELEMENT node's attribute block only by checking that attributeStart + (attributeSize * attributeCount) fits within the node (size - headerSize). It never required attributeSize to be at least sizeof(ResXMLTree_attribute). A binary XML chunk declaring an attributeSize below the real struct size (the test uses sizeof(ResXMLTree_attribute) - 1) passes the product check with an attributeCount that the node cannot actually hold, so per the commit message the accessing code, which uses the minimum struct size rather than the declared size, reads beyond the end of the node data.",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp : ResXMLTree::validateNode: inside the existing branch that already confirmed size >= headerSize + sizeof(ResXMLTree_attrExt) and attrExt > node, a new check compares dtohs(attrExt->attributeSize) against sizeof(ResXMLTree_attribute); if smaller it logs 'Bad XML block: attribute size %d is smaller than min expected %d' via ALOGW and returns BAD_TYPE before the attrSize product and the attributeStart + attrSize <= size - headerSize check are evaluated.",
      "libs/androidfw/tests/ResourceTypes_test.cpp: adds TEST(ResourceTypesTest, ResXMLTree_ValidateNode_SmallAttributeSize), which builds a MockResXMLTree (ResXMLTree_header, empty ResStringPool_header, a RES_XML_START_ELEMENT_TYPE node with size sizeof(ResXMLTree_node)+sizeof(ResXMLTree_attrExt)+20, attrExt with attributeStart = sizeof(ResXMLTree_attrExt), attributeSize = sizeof(ResXMLTree_attribute) - 1, attributeCount = 1) and asserts ResXMLTree::setTo returns BAD_TYPE, confirming validateNode is reached through setTo."
     ],
     "completeness": [
      "Covers the lower bound on attributeSize for RES_XML_START_ELEMENT_TYPE nodes; validateNode returns NO_ERROR early for all other node types, which is consistent with attrExt only existing on start elements.",
      "The check is placed inside the branch guarded by size >= headerSize + sizeof(ResXMLTree_attrExt) && attrExt > node, so attrExt is within the chunk when attributeSize is read; nodes failing that guard already returned BAD_TYPE.",
      "No upper bound on attributeSize is added, and attributeStart is still only validated through the combined attributeStart + attrSize <= size - headerSize check; whether accessor code handles attributeSize larger than sizeof(ResXMLTree_attribute) safely is not shown in the material.",
      "The product attributeSize * attributeCount is computed in size_t from two uint16 values so it cannot overflow; the fix does not change that arithmetic.",
      "The #if 0 block inside validateNode (after the final 'return err;') remains disabled code and is untouched."
     ],
     "behavior_changes": [
      "ResXMLTree::setTo / validateNode now returns BAD_TYPE for binary XML whose start-element attrExt declares attributeSize < sizeof(ResXMLTree_attribute); such documents were previously accepted if the product check passed.",
      "New ALOGW log line 'Bad XML block: attribute size %d is smaller than min expected %d'.",
      "Malformed compiled XML with an undersized attributeSize is now rejected at parse time rather than parsed."
     ],
     "preconditions": [
      "Attacker-controlled binary (compiled) XML is parsed by ResXMLTree::setTo, which calls validateNode on each node (shown by the added test).",
      "The node is RES_XML_START_ELEMENT_TYPE, with node size >= headerSize + sizeof(ResXMLTree_attrExt), attrExt->attributeSize below sizeof(ResXMLTree_attribute), and attributeStart + attributeSize*attributeCount fitting within size - headerSize.",
      "Which callers feed untrusted binary XML into ResXMLTree in a privileged process is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 40ed3bb3016e in android-security-14.0.0_r28, identical hunks (ResourceTypes.cpp at line ~1915, same test); 8/8 lines present at android-security-14.0.0_r29",
      "15": "same fix: commit b4f448b356b1 in android-security-15.0.0_r16, cherry-pick of 4d869cd4de74 (Change-Id Iaaaca9bb4c196e7acde0895d64da919c52448a44), hunk at line ~1986; 8/8 lines present at android-security-15.0.0_r17",
      "16": "same fix: commit a91d16a02b51 in android-security-16.0.0_r7, identical hunks at line ~1989; 8/8 lines present at android-security-16.0.0_r8",
      "17": "same fix: commit 881e565347a5 in android-17.0.0_r1 (base), identical hunks; 8/8 lines present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Single self-contained commit with no dependencies; the 5-line check applies cleanly wherever ResXMLTree::validateNode still has the 'check for sensical values' branch with the attributeSize*attributeCount product check (identical hunks on 14 through 17, only line offsets differ). The test uses util::HostToDevice16/32 from androidfw/Util.h, already included by the existing tests in ResourceTypes_test.cpp."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact accessor code that reads past the node end (the commit message says it uses min sizes instead of the specified size) is not included in the material.",
      "Which privileged consumers of ResXMLTree (e.g. package or resource parsing paths) make this an EoP, as rated by the bulletin, is not shown.",
      "Whether attributeSize values larger than sizeof(ResXMLTree_attribute) are handled correctly by the accessors is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0078": {
     "cve": "CVE-2026-0078",
     "summary": "DevicePolicyManagerService.setGlobalProxy now rejects proxySpec and exclusionList strings longer than 65535 modified-UTF-8 bytes by calling PolicySizeVerifier.enforceMaxStringLength before it stores them on the ActiveAdmin.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (device policy / global proxy)",
     "weakness": "Missing input length validation (unbounded string stored into policy state)",
     "root_cause": "In services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java, setGlobalProxy took the caller-supplied proxySpec and exclusionList strings without any length check. It stored them directly into admin.globalProxySpec and admin.globalProxyExclusionList and set admin.specifiesGlobalProxy = true. PolicySizeVerifier documents that it throws when a string is \"too long to be serialized\" (over 65535 modified-UTF-8 bytes), so an admin could put a value into device policy state that cannot be serialized. The material does not show the downstream failure mode in detail.",
     "fix_analysis": [
      "DevicePolicyManagerService.java:setGlobalProxy, in the else branch where proxySpec != null: the fix calls PolicySizeVerifier.enforceMaxStringLength(proxySpec, \"proxySpec\") and, if exclusionList != null, PolicySizeVerifier.enforceMaxStringLength(exclusionList, \"exclusionList\"), before the fields are assigned. The check runs after getActiveAdminForCallerLocked, the scan for other admins and the USER_SYSTEM check, and before resetGlobalProxyLocked.",
      "In the PolicySizeVerifier.java added by the Android 14 and Android 17 commits, enforceMaxStringLength counts modified-UTF-8 bytes (1 byte for 0x01..0x7F, 2 bytes for U+0000 and up to 0x7FF, 3 bytes otherwise). It throws IllegalArgumentException(argName + \" too long\") once the count exceeds 65535. These commits also add the import to DevicePolicyManagerService.java. The Android 15 and 16 commits add neither the class nor the import and change only the call site. The Android 15 implementation is not shown; its test comment says it uses ModifiedUtf8.countBytes().",
      "Tests: services/tests/servicestests/res/xml/device_admin_sample.xml gains <set-global-proxy />. DevicePolicyManagerTest.testSetGlobalProxy_tooLongStrings asserts IllegalArgumentException for a 65536-char host in the Proxy and for a 65536-char exclusion list entry."
     ],
     "completeness": [
      "Covers both strings that setGlobalProxy writes to ActiveAdmin: globalProxySpec (always checked when non-null) and globalProxyExclusionList (checked when non-null).",
      "The null proxySpec path (clearing the proxy) is untouched; it stores only nulls.",
      "The limit is the 65535-byte modified-UTF-8 serialization bound; no smaller semantic limit (for example a valid host length) is imposed.",
      "Only setGlobalProxy is changed. The sibling setRecommendedGlobalProxy and any other policy setters that accept strings are not touched by this commit; the material does not show whether they already validate lengths."
     ],
     "behavior_changes": [
      "setGlobalProxy now throws IllegalArgumentException (\"proxySpec too long\" or \"exclusionList too long\") for over-long values. Previously it stored them, reset the global proxy and returned null.",
      "The exception is thrown before the ActiveAdmin fields are modified and before resetGlobalProxyLocked, so a rejected call changes no state."
     ],
     "preconditions": [
      "The caller is an active device admin for the given ComponentName whose policy declares USES_POLICY_SETS_GLOBAL_PROXY (getActiveAdminForCallerLocked); the test uses a device owner.",
      "The calling user is UserHandle.USER_SYSTEM (calls from other users fail silently and return null), and no other admin already specifies the global proxy (otherwise that admin's component is returned early).",
      "proxySpec is non-null; to reach the exclusionList check, exclusionList must also be non-null."
     ],
     "per_major": {
      "14": "adapted fix: commit fa2c8531ae6f in android-security-14.0.0_r28 adds a new core/java/android/app/admin/PolicySizeVerifier.java (enforceMaxStringLength plus a local countBytes), adds the import to DevicePolicyManagerService.java, and applies the same call-site change and tests. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 71bc705a2542 in android-security-15.0.0_r16 changes only the setGlobalProxy call site, the test and device_admin_sample.xml. It does not add PolicySizeVerifier or its import. 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 50cbf4422ac8 in android-security-16.0.0_r7 has the same content as the 15 commit at a shifted line offset. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "adapted fix as shown: commit cf8446b51ae1 in android-17.0.0_r1 has the same content as the Android 14 commit, including the creation of PolicySizeVerifier.java and the import. 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "If the target branch lacks android.app.admin.PolicySizeVerifier, take the Android 14 variant. It adds the class (its unused imports of ModifiedUtf8, Bundle, Parcelable, PersistableBundle, ComponentName, ArrayDeque and Queue can be dropped) and the import in DevicePolicyManagerService.java. Otherwise only the call-site change is needed.",
      "The new test comes with the <set-global-proxy /> entry in services/tests/servicestests/res/xml/device_admin_sample.xml, which the test admin needs for the USES_POLICY_SETS_GLOBAL_PROXY check; backport them together."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete consequence of persisting an over-long string (where serialization fails and what state results) is not shown. The only evidence is PolicySizeVerifier's comment that such strings are too long to be serialized.",
      "Whether setRecommendedGlobalProxy or other string setters in DevicePolicyManagerService already apply PolicySizeVerifier is not shown.",
      "The condition behind the early return at the start of setGlobalProxy (likely a feature check) is not shown.",
      "The material does not directly show that PolicySizeVerifier and its import exist on Android 15 and 16 before the fix, or which implementation they use there.",
      "The material does not explain why the Android 17 commit re-creates PolicySizeVerifier.java (same blob ids for the class and the test as the Android 14 commit)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0079": {
     "cve": "CVE-2026-0079",
     "summary": "In platform/external/dng_sdk the libdng_sdk build is changed so that UBSan integer-overflow checks no longer link the aborting ubsan-minimal runtime. They now call library-local handlers that throw std::runtime_error, so an overflow detected while decoding a malformed DNG becomes a catchable C++ exception instead of a process abort.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image decoding library; the commit's Test line points to ImageDecoder as a consumer)",
     "weakness": "Integer overflow in DNG processing hits a sanitizer trap, giving a reachable abort (DoS). The overflow sites themselves are not changed by this fix.",
     "root_cause": "In Android.bp, libdng_sdk-defaults enables sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] (lines 144-149 at android-security-15.0.0_r17). With that setting Soong links the ubsan minimal runtime, whose handlers abort the process when they detect a violation (general platform knowledge). The commit message describes the old behaviour as a hard abort on ubsan errors. Together with the bulletin's DoS classification, this indicates that DNG content could drive instrumented arithmetic to overflow and abort the hosting process. The commit references seven bug IDs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942); none of them is described in the material.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } so Soong does not apply its sanitizer configuration, and so does not link the ubsan-minimal runtime, for this module. The comment says this is a workaround because Soong has no flag that only drops the runtime.",
      "Android.bp, cc_library libdng_sdk: cflags now set \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" explicitly, so the compiler still instruments signed and unsigned overflow. They also add \"-fno-sanitize-link-runtime\" so no compiler-rt ubsan runtime is linked. \"-DqDNGValidate=0\" is kept.",
      "Android.bp, cc_library libdng_sdk: adds srcs \"ubsan-replacement/ubsan_throwing_runtime.cpp\". On 17 the file is appended to the existing explicit srcs list instead.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short message, so a detected overflow unwinds as a C++ exception.",
      "The fix relies on the library already being built with exceptions. libdng_sdk-defaults sets rtti: true and cppflags: [\"-fexceptions\"] (Android.bp lines 141-142), which matches the commit message's statement that C++ exceptions are enabled in this library."
     ],
     "completeness": [
      "The fix only changes how detected overflows are reported. None of the arithmetic in the DNG SDK sources is corrected. It applies only to overflows caught by the signed and unsigned integer-overflow checks. Other crash or abort causes in libdng_sdk are not addressed.",
      "Five handler symbols are provided: add, sub, mul, negate and divrem overflow. They are meant to cover the two enabled checks.",
      "The change is scoped to the libdng_sdk cc_library. libdng_sdk-defaults still declares sanitize.misc_undefined, so any other module built from those defaults (none shown in the material) would still use the aborting minimal runtime.",
      "The material does not show whether the thrown std::runtime_error is caught by the DNG SDK's own exception handling or by the caller. On any path with no handler, the uncaught exception would still terminate the process. The commit's Test line (ImageDecoderTest#testInterestingDngs) shows that at least the ImageDecoder path was tested."
     ],
     "behavior_changes": [
      "When a signed or unsigned integer overflow is detected inside libdng_sdk, a std::runtime_error is thrown (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of the process aborting.",
      "sanitize: { never: true } turns off all Soong-managed sanitizers for libdng_sdk, not only the ubsan runtime (general platform knowledge).",
      "libdng_sdk no longer links a compiler-rt ubsan runtime (-fno-sanitize-link-runtime) and gains one source file. The module is host_supported, so host builds are affected too.",
      "On 14, 15 and 16 the OWNERS file is updated (bug component 1075130, include of the frameworks/base graphics OWNERS). This has no functional effect."
     ],
     "preconditions": [
      "A process must decode attacker-controlled DNG data through libdng_sdk. The commit's test (ImageDecoderTest#testInterestingDngs) points to the ImageDecoder DNG path as a consumer.",
      "The DNG content must make one of the instrumented integer operations overflow. The specific overflow sites are not shown in the material.",
      "Per index search, the matches (android_hardware_camera2_DngCreator.cpp DngCreator_setup and img_utils DngUtils.cpp OpcodeListBuilder) are DNG writer code in frameworks/base and frameworks/av. They do not visibly call libdng_sdk, so their value as reachability evidence is unclear."
     ],
     "per_major": {
      "14": "same fix: 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp hunks are identical; only the removed OWNERS lines differ. Resolved by change_id.",
      "15": "same fix: 1ec41401b3d4 in android-security-15.0.0_r16, the commit analysed here. Resolved by change_id; the code context at android-security-15.0.0_r17 shows the fix in place.",
      "16": "same fix: 74a5a91d169a in android-security-16.0.0_r7. Identical to 15 except for the OWNERS lines removed. Resolved by change_id.",
      "17": "adapted fix: 81ded662eee0 in android-17.0.0_r1 (one of the two Cherrypick-From sources). The sanitize/cflags hunk is the same but sits at a different Android.bp offset (line 105). The new source file is appended to the existing explicit srcs list rather than added as a new srcs block. No OWNERS change. Resolved by related_bug."
     },
     "backport_notes": [
      "Single commit with no dependency. The hunk must land in the libdng_sdk cc_library, not in libdng_sdk-defaults, and the module must already build with -fexceptions/rtti (set in libdng_sdk-defaults). If a downstream Android.bp lists srcs explicitly, append ubsan-replacement/ubsan_throwing_runtime.cpp to that list as was done on 17.",
      "No fork comparison section was provided, so no per-file verdict for a fork can be given."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic sites in the DNG SDK overflow and which DNG structures trigger them. Seven bug IDs are listed, none detailed in the material.",
      "Where the thrown std::runtime_error is caught (inside dng_sdk's own try/catch or in ImageDecoder/caller code), and whether every entry point into libdng_sdk has a handler.",
      "Whether the compiler emits calls to exactly these recoverable handler names under the given flags, and how the parameterless handler signatures interact with the arguments the instrumentation passes.",
      "Whether other modules in dng_sdk's Android.bp build from libdng_sdk-defaults and therefore still use the aborting minimal runtime.",
      "Whether the second Cherrypick-From source (475c8cce973f) contained anything beyond what appears in this squashed diff."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0085": {
     "cve": "CVE-2026-0085",
     "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider resolve the ContentValues key case-insensitively, so that oversized values supplied under a case-variant column name (e.g. \"DaTa1\") are truncated to the simple-field maximum size instead of bypassing the check.",
     "component": "platform/packages/providers/ContactsProvider, DataRowHandler (data row insert/update size enforcement)",
     "weakness": "Improper handling of case sensitivity leading to a bypass of a field size limit (exact-case ContentValues key lookup versus column names the provider accepts case-insensitively)",
     "root_cause": "applySimpleFieldMaxSize(ContentValues cv, String column) read the value with cv.getAsString(column), using the exact column string. The added tests show that a value supplied under a differently cased key such as \"daTa1\" is still written to data1. For such a key, getAsString(column) found no value, so the function returned early and the oversized value was not truncated, defeating ContactsDatabaseHelper.getSimpleFieldMaxSize() enforcement.",
     "fix_analysis": [
      "src/com/android/providers/contacts/DataRowHandler.java:applySimpleFieldMaxSize: replaces cv.getAsString(column) with String key = findKey(cv, column); String v = cv.getAsString(key);, so the length check now sees the value whatever the key's casing. The truncation still writes with cv.put(column, v.substring(0, maxSize)), i.e. under the canonical column name.",
      "src/com/android/providers/contacts/DataRowHandler.java:findKey (new, private static): returns column if cv.containsKey(column). Otherwise it iterates cv.keySet() and returns the first key for which key.equalsIgnoreCase(column). It falls back to column when nothing matches, so getAsString then returns null and the early return is kept.",
      "tests/src/com/android/providers/contacts/ContactsProvider2Test.java: adds three tests. testUpdateFailsWithOnlyCaseMismatchedColumn: an update with only \"daTa1\" is rejected (count 0). testUpdateSucceedsWithAtLeastOneCaseSensitiveColumn: a mixed-case key plus an exact \"data14\" updates both. testCaseMismatchingFieldUpdatesAreTruncatedToMaxSize: the variants data1/daTa1/DATA1/Data1 with maxSize+100 chars are stored truncated. The commit also removes the mMockContactsDatabaseHelper field declaration."
     ],
     "completeness": [
      "The case-insensitive resolution is inside the shared helper and no call sites are changed, so every caller of applySimpleFieldMaxSize gets it.",
      "findKey returns only the first case-insensitive match when no exact key is present. If the same ContentValues contains more than one case variant of the column (e.g. both \"DaTa1\" and \"DATA1\"), only the first key found is examined; this code does not touch the others.",
      "The truncated value is put under the canonical column name, but the original case-mismatched key with the oversized value is not removed from cv, so cv ends up with both keys. What is finally stored depends on how the duplicate column assignment is handled downstream. The new test asserts that the truncated value is stored on the update path; the added tests do not exercise the insert path.",
      "Other exact-case ContentValues lookups in the same class (areAllEmpty, areAnySpecified, visible in the context) are left as they are; they are not size checks."
     ],
     "behavior_changes": [
      "Values for simple fields supplied under a case-variant column key are now truncated to getSimpleFieldMaxSize() and a Log.w \"Truncating field ...\" is emitted.",
      "After truncation the ContentValues also contains the canonical column key with the truncated value, alongside the original mismatched key.",
      "No change for exact-case keys or for values within the limit. On patched code, an update that contains only case-mismatched keys is rejected (count 0), per the added test."
     ],
     "preconditions": [
      "Caller can insert or update Data rows through ContactsProvider (the tests use ContentResolver.update on a structured-name data URI).",
      "The ContentValues supplies a simple-field column (such as data1) under a key whose case differs from the canonical name, with a value longer than ContactsDatabaseHelper.getSimpleFieldMaxSize().",
      "Per the added tests, for an update to take effect at least one exactly cased column must also be present in the ContentValues."
     ],
     "per_major": {
      "14": "same fix: adef234467ab in android-security-14.0.0_r28, identical DataRowHandler.java diff (same blob ids b1295c1f..cdb4df79) and the same tests. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "same fix: 7d266fdeb2ec in android-security-15.0.0_r16, identical DataRowHandler.java diff and tests. At android-security-15.0.0_r17, 7/8 lines of the patched region are present.",
      "16": "same fix, adapted tests: 0d925d762547 in android-security-16.0.0_r7. The DataRowHandler.java logic is identical at a 1-line offset (blob b9becf16..6474c241). The added tests carry @Test annotations, matching the existing tests there. At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "same fix in base: d857ac867433 in android-17.0.0_r1, identical DataRowHandler.java diff and tests. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single self-contained commit. The DataRowHandler.java hunk is identical across 14, 15 and 17 and differs only by a one-line offset on 16. Only the test file needs adapting (presence or absence of @Test annotations, removal of the mMockContactsDatabaseHelper field if present)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which DataRowHandler subclasses call applySimpleFieldMaxSize or with which columns, nor the value of getSimpleFieldMaxSize().",
      "The material does not show how the resulting ContentValues with both the mismatched and the canonical key is turned into SQL on the insert path, or what the concrete DoS effect of the oversized row was.",
      "The material does not show which permission a caller needs to reach this path.",
      "The material does not show how an update with only case-mismatched keys behaved before the fix.",
      "The second Cherrypick-From commit referenced in the message is not shown; whether it contained anything beyond this diff is unknown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0087": {
     "cve": "CVE-2026-0087",
     "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" exactly or hosts ending in \".xyz.com\", instead of any host whose string merely ends in \"xyz.com\".",
     "component": "platform/frameworks/base, PackageManager domain verification (services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java)",
     "weakness": "Improper input validation / incorrect string comparison (missing label-boundary check in wildcard domain match, leading to over-broad authorization)",
     "root_cause": "In the approval-level computation in DomainVerificationService.java, the code first looks up the host exactly in the package's verified stateMap. If that misses, it loops over the verified entries and treats any key starting with \"*.\" as a wildcard. The old test was `domain.startsWith(\"*.\") && host.endsWith(domain.substring(2))`. substring(2) drops the \"*.\" prefix, so for \"*.xyz.com\" the comparison became host.endsWith(\"xyz.com\"). That is a plain string suffix match with no check for a \".\" label boundary. An unrelated host such as \"abcxyz.com\" therefore satisfied it, and the method returned APPROVAL_LEVEL_VERIFIED (\"host verified by wildcard\") for a package that had only verified *.xyz.com.",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java, wildcard loop in the approval-level method (the region that returns APPROVAL_LEVEL_VERIFIED with debug text \"host verified by wildcard\"): the condition becomes `domain.startsWith(\"*.\") && (host.endsWith(domain.substring(1)) || host.equals(domain.substring(2)))`. substring(1) keeps the leading period (\".xyz.com\"), so the suffix match now requires a label boundary. substring(2) is used only for an exact equality check against the bare domain (\"xyz.com\"). A comment documenting the two allowed cases is added.",
      "services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt: the new testWildcardDomain registers a package with autoVerify domain \"*.xyz.com\" and sets it to STATE_SUCCESS. It then asserts that getOwnersForDomain returns no owner for \"abcxyz.com\" and returns DomainOwner(PKG_ONE, false) for both \"abc.xyz.com\" and \"xyz.com\"."
     ],
     "completeness": [
      "Only the single wildcard comparison in the verified-state loop of DomainVerificationService.java is changed. In the shown context, this is the only place where a \"*.\" key is matched against a host.",
      "The exact-host path (stateMap.get(host)) and the user-enabled hosts path (enabledHosts.contains(host)) shown in context use exact lookups and are not affected by the suffix issue.",
      "The fix adds no normalization of host or domain (case, trailing dot, etc.). Matching is still plain String endsWith/equals, as before.",
      "The material does not show whether other components do their own wildcard-domain matching (for example domain collection or UI code)."
     ],
     "behavior_changes": [
      "Stricter matching: a host that ends in the wildcard's suffix string without a preceding period (e.g. abcxyz.com for *.xyz.com) is no longer approved by that wildcard entry. The loop goes on to the remaining verified entries and, if none match, to the user-state checks that follow.",
      "Hosts equal to the bare wildcard domain (xyz.com) and true subdomains (abc.xyz.com) are still approved by the wildcard, so legitimate link handling is unchanged."
     ],
     "preconditions": [
      "An installed package has a wildcard autoVerify domain (\"*.<suffix>\") in a verified state (DomainVerificationState.isVerified) in its domain state map.",
      "A host is evaluated through the domain verification approval logic (e.g. via getOwnersForDomain, as in the test) whose string ends in <suffix> but is neither <suffix> itself nor a subdomain of it.",
      "The exact-host lookup for that host must miss, so execution reaches the wildcard loop."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base b035d109935c in android-security-14.0.0_r28 (hunk at line 1887; the test uses the single-argument addPackage(pkg) form); 6/6 lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base b81e2355caf1 in android-security-15.0.0_r16 (hunk at line 2030; the test uses addPackage(pkg, null)); 6/6 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 2e3d71d027b0 in android-security-16.0.0_r7, diff identical to Android 15 (same blob ids 5c8215e4bfdb..86c7d432200c); 6/6 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base c225d09f44e6 in android-17.0.0_r1 (the material shows a diff identical to the Android 14 variant: hunk at line 1887, addPackage(pkg)); 6/6 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single, self-contained commit that changes one condition in DomainVerificationService.java and adds no new dependencies. Across branches only the test differs: addPackage(pkg) on 14 and 17, addPackage(pkg, null) on 15 and 16. Adapt the test call to the local DomainVerificationService.addPackage signature."
     ],
     "confidence": "high",
     "unknowns": [
      "The provided context does not show the name of the enclosing approval-level method, only the class and the surrounding lines.",
      "The material does not show which production callers (intent resolution, settings UI, etc.) reach this approval computation. Only the test's getOwnersForDomain path is evidenced.",
      "The material does not show whether the host string is normalized (lowercased, trailing dot stripped) before it reaches this comparison."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0088": {
     "cve": "CVE-2026-0088",
     "summary": "CertInstaller.getCallingAppLabel() now returns appInfo.loadSafeLabel(pm) instead of appInfo.loadLabel(pm), so the calling app's label shown in the CA certificate redirect dialog is sanitized by the system before display.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA certificate install redirect dialog)",
     "weakness": "Improper neutralization of attacker-controlled text in a security UI (UI spoofing via unsanitized application label)",
     "root_cause": "In src/com/android/certinstaller/CertInstaller.java, getCallingAppLabel() resolves the referrer package (mCredentials.getReferrer()) via PackageManager.getApplicationInfo() and returned appInfo.loadLabel(pm), the raw application label controlled by the calling app. createRedirectCaCertificateDialog() formats that label into R.string.redirect_ca_certificate_with_app_info_message and sets it as the AlertDialog message. Per the commit message, a crafted label containing characters such as newlines could push the CA certificate installation warning off-screen in that dialog, so the user may not see the warning text.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java:getCallingAppLabel(): the single return statement is changed from appInfo.loadLabel(pm) to appInfo.loadSafeLabel(pm). The commit message states this makes the system sanitize the label, stripping potentially harmful characters like newlines, so the label can no longer displace the warning in the dialog built by createRedirectCaCertificateDialog().",
      "No other code paths, strings or layouts are modified; the fix is a one-line, single-file change with no new dependencies."
     ],
     "completeness": [
      "Covers the one consumer of the calling app label visible in the material: createRedirectCaCertificateDialog(), which is reached from createRedirectOrSelectUsageDialog() when mCredentials.hasOnlyVpnAndAppsTrustAnchors() is true.",
      "Only the label returned by getCallingAppLabel() is sanitized. Whether other CertInstaller dialogs render other caller-controlled text (for example certificate names or the select-usage dialog) is not shown in the material and is not addressed by this commit.",
      "The exact sanitization behaviour depends on the default flags of ApplicationInfo.loadSafeLabel(PackageManager) in the platform; the material only states that newlines are stripped. In the platform, the no-argument loadSafeLabel variant delegates to TextUtils.makeSafeForPresentation with default trimming/first-line/length limits (general platform knowledge)."
     ],
     "behavior_changes": [
      "The calling app name shown in the CA certificate redirect dialog is now the sanitized label: per the commit message, potentially harmful characters like newlines are stripped, and the label may be trimmed, cut to its first line or length-limited according to loadSafeLabel defaults (general platform knowledge), so very long or multi-line app names display differently than before.",
      "No change in error handling or control flow; null is still returned when the referrer or package cannot be resolved."
     ],
     "preconditions": [
      "A calling app invokes CertInstaller to install a certificate such that mCredentials.getReferrer() is that app's package and the package resolves via getApplicationInfo(callingPkg, MATCH_DISABLED_COMPONENTS).",
      "mCredentials.hasOnlyVpnAndAppsTrustAnchors() is true, so createRedirectOrSelectUsageDialog() shows REDIRECT_CA_CERTIFICATE_DIALOG, whose message embeds getCallingAppLabel().",
      "The calling app declares an application label containing characters that are stripped by loadSafeLabel but passed through by loadLabel (per the commit message, newlines)."
     ],
     "per_major": {
      "14": "same fix: platform/packages/apps/CertInstaller d90ffd033f10 in android-security-14.0.0_r28, identical one-line diff (loadLabel -> loadSafeLabel at the same hunk @@ -414,7); resolved by bug_id.",
      "15": "same fix: platform/packages/apps/CertInstaller d29fcd1eff25 in android-security-15.0.0_r16, identical one-line diff; code at android-security-15.0.0_r17 shows loadSafeLabel at line 417; resolved by bug_id.",
      "16": "same fix: platform/packages/apps/CertInstaller c935d8d07913 in android-security-16.0.0_r7, identical one-line diff; resolved by bug_id.",
      "17": "same fix: platform/packages/apps/CertInstaller 3dced430d0a9 present in the base android-17.0.0_r1 release, identical one-line diff; resolved as base."
     },
     "backport_notes": [
      "Single-commit, single-line change with identical file hashes (883c897 -> 1eff53f) on all four majors, so it applies cleanly wherever getCallingAppLabel() returns appInfo.loadLabel(pm). ApplicationInfo.loadSafeLabel(PackageManager) is a hidden/system API available to CertInstaller as a platform app built against the framework (general platform knowledge); forks that moved getCallingAppLabel() or format the label elsewhere should apply the same substitution at the point where the label is loaded."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether any other CertInstaller dialog or string displays caller-controlled text that is not routed through getCallingAppLabel() (the material only shows createRedirectCaCertificateDialog()).",
      "The exact set of characters and length limits applied by loadSafeLabel in each release; the material only states that newlines are stripped.",
      "The per-major fact '1/2 lines of the patched region present' is consistent with the removed line being absent and the added line present, but the material does not spell this out explicitly for 14, 16 and 17 (the 15 code context confirms loadSafeLabel is present)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 0,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0091": {
     "cve": "CVE-2026-0091",
     "summary": "The fix stops WindowManager from sending a launching app's IApplicationThread token out of system_server. TransitionController now strips it from the RemoteTransition placed in the TransitionRequestInfo and stores the delegate process on the server-side Transition. IActivityTaskManager.setRunningRemoteTransitionDelegate now takes a transition token instead of a caller-supplied IApplicationThread.",
     "component": "platform/frameworks/base, WindowManager shell transitions (TransitionController, ActivityTaskManagerService, WindowOrganizerController, WM Shell transition handlers)",
     "weakness": "Exposure of a privileged binder token across a process boundary, plus a caller-supplied process identity in a priority-boost API (the commit message says the fix \"removes the need to send an IApplicationThread token out of WMCore\")",
     "root_cause": "Before the fix, TransitionController.requestStartTransition passed the RemoteTransition object to the transition player unchanged, inside TransitionRequestInfo. That object includes the launching app's IApplicationThread from getAppThread(). Shell then handed an IApplicationThread back to ActivityTaskManagerService.setRunningRemoteTransitionDelegate(IApplicationThread) to boost the animating process. So an IApplicationThread binder left WMCore, and the server used the caller-supplied IApplicationThread as the delegate. It was still gated by the reportRunning quick path or by the permission and running-remote-transition checks. The test name testActivityManager_stripsAppThreadFromRemoteTransition suggests that stripping the app thread is what the fix targets.",
     "fix_analysis": [
      "Transition.java: adds the field `IApplicationThread mRemoteDelegate`, which records the remote animating process on the server-side Transition.",
      "TransitionController.requestStartTransition: when remoteTransition != null, sets transition.mRemoteDelegate = remoteTransition.getAppThread() and builds a new RemoteTransition(getRemoteTransition(), getDebugName()) without the app thread. The TransitionRequestInfo sent to the player now carries this copy. transition.setRemoteAnimationApp(remoteTransition.getAppThread()) is still called on the original after the request is sent.",
      "WindowContainerTransaction: adds HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE (19 on Android 15) and setAnimationDelegate(IBinder), which carries the binder in the insetsFrameOwner field. toString and type-name support are added too.",
      "WindowOrganizerController: the new hierarchy-op case sets transition.mRemoteDelegate = IApplicationThread.Stub.asInterface(hop.getInsetsFrameOwner()). It logs and skips if there is no transition or no binder.",
      "IActivityTaskManager.aidl / ActivityTaskManagerService.setRunningRemoteTransitionDelegate: the parameter changes from IApplicationThread to IBinder transitionToken. The method resolves the token with Transition.fromBinder, returns early if the result is null, and uses transition.mRemoteDelegate as the delegate. The existing quick path (mRemotePlayer.reportRunning), the CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission check and the check that the caller is already running a remote transition are unchanged.",
      "Shell: Transitions.setRunningRemoteTransitionDelegate now takes the transition IBinder. OneShotRemoteHandler (two call sites), RemoteTransitionHandler and RecentsTransitionHandler.startAnimation pass the transition token. RecentsTransitionHandler.startRecentsTransition and SplitScreenTransitions.startEnterTransition register the delegate through wct.setAnimationDelegate(appThread.asBinder()). The two legacy-adapter calls in StageCoordinator, which passed adapter.getCallingApplication(), are removed."
     ],
     "completeness": [
      "Only the RemoteTransition in the TransitionRequestInfo built by TransitionController.requestStartTransition is stripped. The material does not show whether a RemoteTransition carrying an app thread reaches the player by any other path.",
      "setRunningRemoteTransitionDelegate no longer accepts an arbitrary IApplicationThread. The delegate now comes from server-side state, which is set from RemoteTransition in requestStartTransition or from the SET_ANIMATION_DELEGATE hierarchy op.",
      "The SET_ANIMATION_DELEGATE hop accepts the binder supplied in the WindowContainerTransaction. The diff adds no permission check beyond what already applies to WCT application in WindowOrganizerController.",
      "The StageCoordinator legacy remote-animation paths no longer request a priority boost at all, so they are removed rather than migrated."
     ],
     "behavior_changes": [
      "This is a hidden API change. IActivityTaskManager.setRunningRemoteTransitionDelegate now takes an IBinder transition token, so callers passing an IApplicationThread will not compile.",
      "The transition player now receives a RemoteTransition built only from the remote transition and the debug name, without the app thread.",
      "A new WCT hierarchy op, setAnimationDelegate, is added. setRunningRemoteTransitionDelegate returns silently when the token does not resolve to a Transition.",
      "Split-screen legacy RemoteAnimationAdapter paths in StageCoordinator no longer boost the animating process."
     ],
     "preconditions": [
      "A transition is started with a RemoteTransition that carries an app thread (for example a launch with a remote animation or a split enter), and the transition request is sent to the registered transition player. Recents launches register the delegate through setAnimationDelegate instead.",
      "In setRunningRemoteTransitionDelegate, if mRemotePlayer.reportRunning(delegate) succeeds, the method returns early. Otherwise the caller needs CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS and must itself be running a remote transition (from the ActivityTaskManagerService code)."
     ],
     "per_major": {
      "14": "Adapted fix caffcf6be82b in android-security-14.0.0_r28. The hop uses value 18 and carries the binder through setReparentContainer / hop.getNewParent(). TransitionController uses the older 4-arg TransitionRequestInfo and mTransitionPlayer. RecentsTransitionHandler passes the transition in place of mAnimApp. OneShotRemoteHandler has only one call site.",
      "15": "Fix f7488ad342c2 in android-security-15.0.0_r16 (cherry-pick of a35ab54ec741). The hop uses value 19 and setInsetsFrameOwner / getInsetsFrameOwner. The code context at android-security-15.0.0_r17 shows the fixed code, but the per-major check reports only 3/8 lines of the patched region present at that tag.",
      "16": "Adapted fix 40c30bb5613e in android-security-16.0.0_r7. The hop uses value 27, is added to the HierarchyOpType IntDef, and reads the transition from chain.mTransition. RecentsTransitionHandler threads appThread into startRealRecentsTransition and drops the mAnimApp assignment. The StageCoordinator removal is not part of this diff.",
      "17": "Fix a29de493b054 in android-17.0.0_r1. The diff shown matches the Android 14 form (hop 18, setReparentContainer/getNewParent, 4-arg TransitionRequestInfo)."
     },
     "backport_notes": [
      "The AIDL signature change in IActivityTaskManager and the shell call sites must ship together with the server changes, in one atomic change. The HierarchyOp field used to carry the binder and the op number differ per branch (14/17: mReparent with value 18; 15: mInsetsFrameOwner with 19; 16: mInsetsFrameOwner with 27), so match the target branch's HierarchyOp constants instead of copying the numbers.",
      "On 16, the new case in both toString and WindowOrganizerController directly follows HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS, and the diff shows no break between them, so that case falls through into the new code. Check fall-through when placing the case in other trees."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact misuse scenario and the attacker-controlled component behind the EoP rating are not described in the material.",
      "The test content for ActivityManagerTest#testActivityManager_stripsAppThreadFromRemoteTransition is not shown.",
      "The material does not show whether other code paths still serialize RemoteTransition.getAppThread() to other processes.",
      "The RemoteTransition two-argument constructor body and Transition.fromBinder behaviour for finished transitions are not shown.",
      "The Android 17 diff looks textually identical to the Android 14 diff, and the material does not explain why.",
      "The per-major check reports only partial presence (3/8 or 2/8 lines) of the patched region at the latest tags, and the material does not explain the mismatch."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-opus-5-5",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0093": {
     "cve": "CVE-2026-0093",
     "summary": "Replaces ApplicationInfo.loadLabel with loadSafeLabel (DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where Settings renders the requesting app's name in the credential-management request UI (RequestManageCredentials and CredentialManagementAppAdapter).",
     "component": "platform/packages/apps/Settings, security / RequestManageCredentials (credential management app request screen)",
     "weakness": "UI misrepresentation: unsanitized, app-controlled label rendered in a consent dialog (no length/line restriction)",
     "root_cause": "RequestManageCredentials.java and CredentialManagementAppAdapter.java inserted the requesting application's label from applicationInfo.loadLabel(PackageManager) directly into the title (R.string.request_manage_credentials_title via TextUtils.expandTemplate) and into the per-app name view, with no trimming, first-line or width restriction. Per the commit message, this let an app developer hide from the user what permissions the app requests.",
     "fix_analysis": [
      "src/com/android/settings/security/RequestManageCredentials.java: in the title setup (the try block around getApplicationInfo, ~line 251), applicationInfo.loadLabel(getPackageManager()) is replaced by applicationInfo.loadSafeLabel(getPackageManager(), DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) inside TextUtils.expandTemplate. Static imports for PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and the two TextUtils flags are added.",
      "src/com/android/settings/security/CredentialManagementAppAdapter.java, header view holder (~line 93, mTitleView): same replacement of loadLabel(mPackageManager) with loadSafeLabel(mPackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) inside TextUtils.expandTemplate.",
      "src/com/android/settings/security/CredentialManagementAppAdapter.java, app view holder (~line 156, mAppNameView): String.valueOf(applicationInfo.loadLabel(mPackageManager)) becomes String.valueOf(applicationInfo.loadSafeLabel(mPackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE)).",
      "Effect: the displayed label is trimmed, cut to its first line and limited by DEFAULT_MAX_LABEL_SIZE_PX. The commit message says this trimming is the fix."
     ],
     "completeness": [
      "The three loadLabel call sites shown in the diff hunks are all converted. The single commit covers the activity title and the RecyclerView adapter (header title and per-app name).",
      "The NameNotFoundException fallback paths are unchanged. The adapter app row falls back to mAppNameView.setText(appName), the package name string. The value used in the title fallbacks is not shown in the material.",
      "The fix only constrains the app label. It does not change how other request content is laid out or validated."
     ],
     "behavior_changes": [
      "App names on the credential management request screen are now trimmed, limited to their first line, and limited by DEFAULT_MAX_LABEL_SIZE_PX. Long or multi-line labels display differently than before.",
      "No new error paths, flags or permission checks. The change is marked Flag: EXEMPT BUGFIX and is unconditional."
     ],
     "preconditions": [
      "An installed app triggers the Settings RequestManageCredentials activity, so Settings renders that app's own label.",
      "The requesting app controls its own label. No special permission is evident from the diff.",
      "The misrepresentation only matters when the user sees and acts on the request screen."
     ],
     "per_major": {
      "14": "same fix: commit 47f5449218e4 in android-security-14.0.0_r28. The CredentialManagementAppAdapter.java diff is identical to the 15 fix. RequestManageCredentials.java differs only in surrounding import context (android.annotation.Nullable, blob 6a1d40aef7a). Resolver reports 7/8 patched lines present at android-security-14.0.0_r29.",
      "15": "reference fix: commit 7f9aa5805598 in android-security-15.0.0_r16. 7/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 88ab2340c255 in android-security-16.0.0_r7, diff identical to the 15 fix (same blob hashes 2c1b45219d0 -> 9867a4777ca). 7/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit 9cc2911dc24d is in the base release android-17.0.0_r1. RequestManageCredentials.java context matches the 14 variant (blob 6a1d40aef7a). 7/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit: apply all three substitutions (one in RequestManageCredentials, two in CredentialManagementAppAdapter). The adapter hunks apply unchanged on every major. Only the import context of RequestManageCredentials differs between 14/17 and 15/16.",
      "loadSafeLabel(PackageManager, float, int) and PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX are system APIs, not part of the public SDK. Settings can use them because it builds against the platform (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact visual mechanism of the original report is only described at a high level in the commit message. No test or screenshot is included (Test: Manual with researcher APK).",
      "Whether the hidden 'permissions' refer to the AppUriAuthenticationPolicy contents shown on the screen is not stated in the material.",
      "Whether other loadLabel uses exist elsewhere in these files, outside the diff hunks, is not shown.",
      "The value rendered in the title fallback path (NameNotFoundException) is not shown.",
      "Whether the 7/8 line count reported by the resolver reflects a real missing line or a counting artifact is not shown.",
      "The two internal Cherrypick-From changes are referenced, but their contents and how they relate to the AOSP commit are not provided."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0095": {
     "cve": "CVE-2026-0095",
     "summary": "Widens the local allocation-size variable in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the computed buffer size is no longer truncated before osi_malloc, preventing a heap buffer overflow in the following memcpy.",
     "component": "platform/packages/modules/Bluetooth, system/stack/l2cap (L2CAP flow control / ERTM, l2c_fcr.cc)",
     "weakness": "Integer truncation leading to undersized heap allocation and heap buffer overflow (incorrect allocation size calculation)",
     "root_cause": "In l2c_fcr_clone_buf, the allocation size was computed as no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN and stored in a uint16_t buf_size. Both no_of_bytes and new_offset are uint16_t parameters, so the sum can exceed 65535; storing it in uint16_t truncates it, and osi_malloc(buf_size) then returns a buffer smaller than needed. The function then sets p_buf2->offset = new_offset and p_buf2->len = no_of_bytes and memcpys no_of_bytes bytes to (p_buf2 + 1) + new_offset, writing past the end of the undersized allocation.",
     "fix_analysis": [
      "system/stack/l2cap/l2c_fcr.cc:l2c_fcr_clone_buf: the single changed line replaces 'uint16_t buf_size' with 'uint32_t buf_size'. The arithmetic expression itself is unchanged (it is evaluated in size_t because of sizeof(BT_HDR)); the fix only removes the narrowing store, so osi_malloc receives the full computed size and the subsequent memcpy of no_of_bytes bytes at offset new_offset stays within the allocation.",
      "No validation of no_of_bytes or new_offset is added, and the memcpy and header assignments are untouched; correctness now relies on the allocation matching the copy size rather than on rejecting large inputs."
     ],
     "completeness": [
      "Covers the only allocation in l2c_fcr_clone_buf; with uint32_t the maximum possible sum (65535 + sizeof(BT_HDR) + 65535 + L2CAP_FCS_LEN) cannot overflow, so the truncation is fully eliminated for this function.",
      "The fix does not add bounds checks at the callers that supply no_of_bytes/new_offset; whether any caller can still pass values that are semantically invalid (e.g. no_of_bytes larger than the source p_buf->len, which would cause an over-read from p_buf in the memcpy) is not addressed by this change and cannot be judged from the material.",
      "The header assignments p_buf2->offset = new_offset and p_buf2->len = no_of_bytes are unchanged, and the fix does not touch the BT_HDR definition."
     ],
     "behavior_changes": [
      "None observable in normal operation; for inputs where the sum exceeded 65535, osi_malloc now allocates the correct (larger) size instead of a truncated one. No new error paths, return values or logging."
     ],
     "preconditions": [
      "l2c_fcr_clone_buf must be invoked with no_of_bytes and new_offset whose sum plus sizeof(BT_HDR) + L2CAP_FCS_LEN exceeds 65535 (both parameters are uint16_t, so this requires large values of at least one of them).",
      "The function belongs to the L2CAP flow control and retransmission (FCR/ERTM) code in l2c_fcr.cc; the exact callers and whether their arguments are influenced by a remote peer are not shown in the material (no caller chunks were found per index search)."
     ],
     "per_major": {
      "14": "same fix: commit 006620f1caaa in android-security-14.0.0_r28, identical one-line uint16_t -> uint32_t change in l2c_fcr_clone_buf (two-line function signature, hunk at line 241).",
      "15": "same fix: commit f82bbb37a852 in android-security-15.0.0_r16 (cherry-pick of a06794634568), identical one-line change (hunk at line 242).",
      "16": "same fix: commit abc8b696b615 in android-security-16.0.0_r7, identical one-line change applied to a reformatted file (single-line function signature, hunk at line 229).",
      "17": "same fix: commit 554513a00597 in android-17.0.0_r1 (base), identical one-line change; the diff shown in the material carries the same blob indices and hunk position as the Android 14 diff."
     },
     "backport_notes": [
      "Single self-contained one-line change with no dependencies; applies to any branch where l2c_fcr_clone_buf declares 'uint16_t buf_size' (only surrounding formatting of the function signature differs between 14/15 and 16)."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers of l2c_fcr_clone_buf can supply large no_of_bytes/new_offset values and whether those values derive from remote L2CAP traffic (the bulletin classifies it as EoP, High).",
      "Whether the sum could actually reach the truncation range in practice given upstream L2CAP MTU/MPS limits; the material does not show the callers or limits.",
      "The per-major facts report '4/5 lines of the patched region present' at the latest tags while marking each branch resolved; the material does not explain the discrepancy (likely a comparison artifact around the changed line, but not confirmed).",
      "The Android 17 diff in the material shows the same blob indices (665ed103c51..e110189a023) as the Android 14 diff, which is not explained by the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0098": {
     "cve": "CVE-2026-0098",
     "summary": "DocumentsUI Shared.getCallingPackageName now only honours the Intent.EXTRA_PACKAGE_NAME calling-package override when the real caller's UID shares the platform signature (checkSignatures against Process.SYSTEM_UID), instead of accepting any app with FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP.",
     "component": "platform/packages/apps/DocumentsUI, base/Shared.java (calling package identification)",
     "weakness": "Insufficient authorization check: trust for an identity-override extra was based on installation status (system image flags) rather than on the caller's signing identity",
     "root_cause": "In src/com/android/documentsui/base/Shared.java, getCallingPackageName(Activity) takes activity.getCallingPackage() and then lets the caller replace it with the string in Intent.EXTRA_PACKAGE_NAME if the caller's ApplicationInfo has FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP (helpers isSystemApp/isUpdatedSystemApp). Those flags reflect installation status, not the signing certificate, so a preinstalled app that is not platform-signed could make DocumentsUI treat an arbitrary package name as the calling package. The code also dereferenced activity.getIntent() without a null check.",
     "fix_analysis": [
      "Shared.java:getCallingPackageName - the condition `isSystemApp(info) || isUpdatedSystemApp(info)` is replaced with `activity.getIntent() != null && isTrustedSystemSignature(activity.getPackageManager(), info.uid)`; only then is Intent.EXTRA_PACKAGE_NAME read and, if non-empty, used as callingPackage.",
      "Shared.java:isTrustedSystemSignature (new private static) - returns `pm.checkSignatures(callingUid, android.os.Process.SYSTEM_UID) == PackageManager.SIGNATURE_MATCH`, i.e. the override is allowed only when the caller's UID is signed with the same certificate as UID 1000 (the platform key).",
      "Shared.java - the helpers isSystemApp(ApplicationInfo) and isUpdatedSystemApp(ApplicationInfo) are deleted; FLAG_SYSTEM/FLAG_UPDATED_SYSTEM_APP no longer play any role in the decision.",
      "The NameNotFoundException handling around getApplicationInfo is unchanged: on lookup failure the override is skipped and the raw getCallingPackage() value is returned."
     ],
     "completeness": [
      "Covers the single place in the diff where EXTRA_PACKAGE_NAME overrides the calling package (Shared.getCallingPackageName); all consumers of that helper inherit the stricter check.",
      "Trust is now a strict signature match with SYSTEM_UID. The commit message speaks of 'core system certificates' but the code compares only against the platform signature; apps signed with other keys are not accepted.",
      "The unprivileged path is unchanged: when the signature check fails, callingPackage remains whatever Activity.getCallingPackage() returned. getCallingPackage() can return null when the activity was not started for a result (general platform knowledge).",
      "No change to how downstream code uses the returned package name; the material does not show those call sites."
     ],
     "behavior_changes": [
      "Preinstalled (FLAG_SYSTEM / updated system) apps that are not platform-signed can no longer override the calling package name via Intent.EXTRA_PACKAGE_NAME; the extra is silently ignored for them.",
      "Platform-signed apps without the system flags (if any) can now use the override, where previously they could not.",
      "A null activity.getIntent() no longer causes a NullPointerException in this path; the override is simply skipped.",
      "Each call now performs PackageManager.checkSignatures instead of a local flag test. checkSignatures is an IPC to the package manager service (general platform knowledge)."
     ],
     "preconditions": [
      "A DocumentsUI activity that calls Shared.getCallingPackageName must be started by another app. Activity.getCallingPackage() only identifies the caller when the activity was started for a result (general platform knowledge).",
      "Pre-fix: the starting app had to have FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP and supply Intent.EXTRA_PACKAGE_NAME.",
      "Post-fix: the starting app's UID must share the platform signature (checkSignatures with Process.SYSTEM_UID returns SIGNATURE_MATCH)."
     ],
     "per_major": {
      "14": "same fix: platform/packages/apps/DocumentsUI a712c9468e47 in android-security-14.0.0_r28, identical hunks to the Android 15 commit (same blob ids 9f328be69..f691229ab).",
      "15": "same fix: platform/packages/apps/DocumentsUI b05353f638a7 in android-security-15.0.0_r16, cherry-picked from internal commit 08582e104a16 (Change-Id I0c337720e42f6492573d261ae66fa88c511cd99e).",
      "16": "same fix: platform/packages/apps/DocumentsUI 0c8b7870ece9 in android-security-16.0.0_r7; identical logic, hunks offset by 11 lines (blob bb8a39393..0a5648f01).",
      "17": "same fix: platform/packages/apps/DocumentsUI a08d1c7ea084 present in the android-17.0.0_r1 base. Only 3 of the 8 lines of the patched region match at android-security-17.0.0_r1, but the resolver marks it resolved."
     },
     "backport_notes": [
      "Single self-contained commit touching only Shared.java with no new dependencies. Google applied it with the same pre-image blob (9f328be69) on 14/15/17 and with an 11-line offset on 16."
     ],
     "confidence": "high",
     "unknowns": [
      "Which DocumentsUI call sites consume getCallingPackageName and what privilege the spoofed package name confers (the material shows only the helper).",
      "Why the Android 17 security tag matches only 3/8 lines of the patched region (possible later refactor); the diff of that state is not provided.",
      "Whether the fix also ships through a DocumentsUI module update separate from the platform tags.",
      "Tests: the commit is marked Test: manual; no automated test accompanies it."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0099": {
     "cve": "CVE-2026-0099",
     "summary": "Adds onNullBinding overrides (and, for the per-transaction connection, an onBindingDied override) to the two ServiceConnection objects in HostEmulationManager. The NFC service now unbinds from a HostApduService whose onBind returned null or whose binding died, instead of keeping the binding.",
     "component": "platform/packages/apps/Nfc (packages/modules/Nfc on 16+), card emulation / HostEmulationManager",
     "weakness": "Improper service-binding lifecycle handling: because ServiceConnection.onNullBinding/onBindingDied were not handled, the privileged NFC process kept a binding to an app service. The Android 14 fix commit is titled \"Several BAL bypasses in HostApduService\".",
     "root_cause": "HostEmulationManager binds to app-provided HostApduService implementations through two ServiceConnection objects. mPaymentConnection is used for the preferred payment service (bound via bindPaymentServiceLocked). mConnection is used for the service selected during a transaction. Neither connection overrode onNullBinding, and mConnection also did not override onBindingDied. When the target service returned null from onBind, or the binding died, the NFC process did not call unbindService, so the binding from the NFC app to the third-party app stayed in place. The Android 14 commit for the same bug describes the consequence as background activity launch (BAL) bypasses in HostApduService. The exact mechanism is not shown in the diff.",
     "fix_analysis": [
      "src/com/android/nfc/cardemulation/HostEmulationManager.java: the new mPaymentConnection.onNullBinding logs and, under mLock, calls mContext.unbindService(this). The existing mPaymentConnection.onBindingDied, which rebinds via bindPaymentServiceLocked, is unchanged.",
      "src/com/android/nfc/cardemulation/HostEmulationManager.java: the new mConnection.onBindingDied logs and calls unbindServiceIfNeededLocked(). In this commit the call is not wrapped in a synchronized(mLock) block.",
      "src/com/android/nfc/cardemulation/HostEmulationManager.java: the new mConnection.onNullBinding logs and, under mLock, calls mContext.unbindService(this).",
      "On Android 16/17 the payment connection gets onNullBinding plus an NfcEventLog entry, using a new BindState.SERVICE_NULL_BINDING value in NfcNci/proto/event.proto. A second connection object, located after HostEmulationServiceConnection and before mConnection, gets the same onBindingDied/onNullBinding overrides."
     ],
     "completeness": [
      "Covers the two ServiceConnection objects the fix touches (mPaymentConnection and mConnection on 15) for the null-binding case, and mConnection for the binding-died case. Whether other ServiceConnection objects exist in the file or the Nfc app is not shown.",
      "The Android 15 onNullBinding implementations call mContext.unbindService(this) directly. They do not reset the connection bookkeeping fields (mService/mServiceName/mServiceBound for mConnection, mPaymentService/mPaymentServiceName for mPaymentConnection). The Android 14 variant instead goes through unbindServiceIfNeededLocked()/unbindPaymentServiceLocked().",
      "In this commit, mConnection.onBindingDied calls unbindServiceIfNeededLocked() without taking mLock. The Android 14 variant wraps the call in synchronized(mLock). Whether unbindServiceIfNeededLocked takes the lock itself is not shown."
     ],
     "behavior_changes": [
      "If a bound HostApduService returns null from onBind, the NFC service now unbinds right away and logs Log.i \"onNullBinding: <component>\".",
      "If the per-transaction binding (mConnection) dies, the NFC service now unbinds via unbindServiceIfNeededLocked and logs Log.i \"onBindingDied: <component>\".",
      "Android 16/17 only: a new NfcEventProto.BindState.SERVICE_NULL_BINDING value, and an NfcEventLog entry when the payment service null-binds."
     ],
     "preconditions": [
      "An app exposes a HostApduService that the NFC service binds to, either as the preferred payment service (bindPaymentServiceLocked path, mPaymentConnection) or as the service selected during a card-emulation transaction (mConnection).",
      "The bound service returns null from onBind or its binding dies."
     ],
     "per_major": {
      "14": "adapted fix: separate commit 34a7821a1bfe in platform/packages/apps/Nfc (android-security-14.0.0_r28), titled \"Several BAL bypasses in HostApduService\". It adds onBindingDied and onNullBinding to both connections, implemented via unbindPaymentServiceLocked()/unbindServiceIfNeededLocked() under mLock. 4/4 lines of the patched region are present at r29.",
      "15": "this fix: 7fb6795b51c3 in platform/packages/apps/Nfc (android-security-15.0.0_r16). It adds onNullBinding to mPaymentConnection and onBindingDied/onNullBinding to mConnection, using mContext.unbindService(this) for null binding. 4/4 lines are present at r17.",
      "16": "same fix, adapted: 127162a9e764 in platform/packages/modules/Nfc (android-security-16.0.0_r7), under NfcNci/src/... It adds onNullBinding with event logging to the payment connection, onBindingDied/onNullBinding to a second connection object, and SERVICE_NULL_BINDING to event.proto. 6/6 lines are present at r8.",
      "17": "same fix as 16: cfbd02ac1630 in platform/packages/modules/Nfc, present in base android-17.0.0_r1. 6/6 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The material shows one commit per branch, but the 14 and 15 variants differ. 14 routes both callbacks through the existing unbind*Locked helpers under mLock. 15 calls mContext.unbindService(this) directly for null binding and calls unbindServiceIfNeededLocked() without the lock for binding death. On 16+ the code lives in packages/modules/Nfc under NfcNci/, and the fix also touches event.proto (Nfc is a mainline module there (general platform knowledge)).",
      "No fork comparison section was provided, so no per-file fork verdict can be reported."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact privilege-escalation mechanism is not shown. Only the Android 14 commit title (\"Several BAL bypasses in HostApduService\") names it.",
      "The bodies of unbindServiceIfNeededLocked, unbindPaymentServiceLocked and bindPaymentServiceLocked are not shown, so it is unknown whether state fields are reset after the fix or whether rebind loops can occur.",
      "Whether calling unbindServiceIfNeededLocked() without synchronized(mLock) in the Android 15 mConnection.onBindingDied was intentional is not shown.",
      "On 16/17, which named connection object receives the second hunk is not fully clear from the diff context.",
      "Whether HostEmulationManager or the Nfc app has other ServiceConnection objects that the fix does not touch."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0100": {
     "cve": "CVE-2026-0100",
     "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp) so that a RES_TABLE_PACKAGE_TYPE chunk whose 32-bit package id is above 255 is rejected instead of being stored as the package id.",
     "component": "platform/frameworks/base, libs/androidfw (resource table / ARSC loading, LoadedArsc)",
     "weakness": "Missing bounds/range check on an untrusted 32-bit field (package id) that is later used as an 8-bit identifier",
     "root_cause": "In LoadedPackage::Load, the package id from the ResTable_package header (header->id, a 32-bit field read with dtohl) was assigned directly to loaded_package->package_id_ without validation. Resource IDs only reserve 8 bits for the package id, so a malformed ARSC file with an id above 255 produced a LoadedPackage carrying an out-of-range package id. Per the commit message, this could lead to unexpected behavior or overflow during parsing. The neighbouring typeIdOffset field already had an equivalent uint8_t range check, but the id field did not.",
     "fix_analysis": [
      "libs/androidfw/LoadedArsc.cpp: LoadedPackage::Load reads header->id into a local const uint32_t package_id. If package_id > std::numeric_limits<uint8_t>::max(), it logs 'RES_TABLE_PACKAGE_TYPE package id is out of valid range' and returns an empty unique_ptr, so the package fails to load (and, as the new test shows, so does LoadedArsc::Load).",
      "The validated value is then stored into loaded_package->package_id_ with static_cast<int>(package_id). The existing checks for package_id_ == 0 / kAppPackageId and PROPERTY_DYNAMIC follow unchanged.",
      "libs/androidfw/tests/LoadedArsc_test.cpp: the new test LoadedArscTest.LoadArscWithInvalidPackageId builds a minimal ResTable_header + ResTable_package with id = 256 and asserts that LoadedArsc::Load returns null."
     ],
     "completeness": [
      "Covers the assignment of header->id in LoadedPackage::Load, the only place in the shown code where the package id is taken from the chunk. It mirrors the existing typeIdOffset range check directly below it.",
      "The check rejects the package outright (the load fails) rather than truncating the value. The material does not show whether package_id_ is set anywhere else.",
      "The material does not show other ARSC parsers (for example the legacy ResTable path or aapt2 tooling), so whether they perform an equivalent check is not visible here."
     ],
     "behavior_changes": [
      "Stricter validation: an ARSC whose RES_TABLE_PACKAGE_TYPE header id is above 255 now fails to load (LoadedPackage::Load returns null) and logs the ERROR 'RES_TABLE_PACKAGE_TYPE package id is out of valid range'. Previously such a package loaded with an out-of-range id.",
      "Resource tables with package ids in 0..255 are unaffected."
     ],
     "preconditions": [
      "A malformed resource table (ARSC) is parsed by libandroidfw's LoadedArsc::Load / LoadedPackage::Load.",
      "The RES_TABLE_PACKAGE_TYPE chunk has an id field greater than 255."
     ],
     "per_major": {
      "14": "same fix: commit 6a9eb9dd74d0 in android-security-14.0.0_r28, identical diff at a different line offset (@@ -476). 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: commit 86dd9d421620 in android-security-15.0.0_r16 (@@ -481). 7/8 lines of the patched region present at android-security-15.0.0_r17. The code context there shows the full check.",
      "16": "same fix: commit ca855338abcf in android-security-16.0.0_r7, identical diff (@@ -485). 7/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "same fix: commit 50d18b36c708 in android-17.0.0_r1 (base), identical diff (@@ -549, test hunk @@ -421). 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "A single self-contained commit. The code hunk is identical on all four majors and targets the line 'loaded_package->package_id_ = dtohl(header->id);' in LoadedPackage::Load. The adjacent typeIdOffset check already uses std::numeric_limits, and the fix adds no include."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream site where an out-of-range package_id_ causes the overflow or misbehavior (the commit message only says 'unexpected behavior or overflow when being parsed').",
      "The concrete entry point and who controls the malformed ARSC (APK install, overlay, loader, etc.) are not shown in the material.",
      "Why the resolver reports only 7/8 lines of the patched region present on each branch."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28577": {
     "cve": "CVE-2026-28577",
     "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the supplied WindowToken already contains a window, enforcing one toast window per toast token.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowManagerService.java, addWindow)",
     "weakness": "Missing validation of window token state (insufficient check allowing a second toast window to be attached to an already-populated toast WindowToken)",
     "root_cause": "In WindowManagerService.addWindow, the only guard for TYPE_TOAST windows was displayContent.canAddToastWindowForUid(callingUid), which limits toasts per calling UID. Nothing checked whether the resolved WindowToken already held a window. The fix comment states the design rule: a WindowToken of TYPE_TOAST allows only one toast window. The per-UID check does not look at the token, so it did not enforce that rule across UIDs. The new test testAddToastWindow_singleWindowPerToken is built to exercise exactly this: a second toast window added to the same token from a different UID, chosen to get past the canAddToastWindowForUid check.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowManagerService.java:addWindow: inside the `if (type == TYPE_TOAST)` block, right after the canAddToastWindowForUid check and before H.WINDOW_HIDE_TIMEOUT is scheduled, adds `if (addToastWindowRequiresToken && !token.isEmpty()) return WindowManagerGlobal.ADD_BAD_APP_TOKEN;`. When the flag is set, this rejects any toast window whose target token is already non-empty, whatever the calling UID.",
      "services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java: adds testAddToastWindow_singleWindowPerToken. It registers a TYPE_TOAST token via addWindowToken, adds a first toast window from uid 1234 (expects >= ADD_OKAY), then adds a second toast window with the same token from uid 1235 (expects ADD_BAD_APP_TOKEN). Both apps are mocked with targetSdkVersion O.",
      "On Android 15 and 16 the ProtoLog.w(WM_ERROR, \"Adding toast window with non-empty token.\") line is commented out. Android 14 and 17 emit it, and Android 14 also adds the matching entry to data/etc/services.core.protolog.json."
     ],
     "completeness": [
      "The new check only runs when addToastWindowRequiresToken is true. When it is false (the legacy path the surrounding comment describes as older apps adding toasts without a token), token emptiness is not checked and only canAddToastWindowForUid applies.",
      "The check runs only at addWindow time, against token.isEmpty(). It covers TYPE_TOAST only and does not change canAddToastWindowForUid or how any other window type is handled.",
      "On Android 15 and 16 the diagnostic ProtoLog is commented out, so the rejection is not logged on those branches. The functional check is the same on all four branches (8/8 lines present in each latest security tag)."
     ],
     "behavior_changes": [
      "addWindow returns WindowManagerGlobal.ADD_BAD_APP_TOKEN (a new failure code on this path) for a TYPE_TOAST window when addToastWindowRequiresToken is true and the target WindowToken already contains a window.",
      "The rejection happens before the WINDOW_HIDE_TIMEOUT message is scheduled, so no hide timer is created for the rejected window.",
      "Android 14 and 17 log a new WM_ERROR ProtoLog warning, \"Adding toast window with non-empty token.\"; Android 15 and 16 log nothing."
     ],
     "preconditions": [
      "The caller reaches WindowManagerService.addWindow with attrs.type == TYPE_TOAST and passes displayContent.canAddToastWindowForUid(callingUid). The test does this by using a different UID from the first toast owner.",
      "addToastWindowRequiresToken must be true for the calling app. The test gives both apps targetSdkVersion O, and the surrounding comment suggests the flag depends on app age, but how it is derived is not in the material.",
      "The supplied token must resolve to an existing WindowToken that already contains a window. The test creates it via addWindowToken(token, TYPE_TOAST, ...) and then adds a first toast window."
     ],
     "per_major": {
      "14": "Adapted fix: commit 2f08e7d895e3 in android-security-14.0.0_r28. Same 6-line check with the ProtoLog active, plus: a services.core.protolog.json entry, a new @VisibleForTesting Session(service, callback, callingPid, callingUid) constructor in Session.java, and a partial-backport createTestSession helper in WindowTestsBase.java to support the test. 8/8 lines present at r29.",
      "15": "Same fix: commit 9af61140db79 in android-security-15.0.0_r16. Identical check with the ProtoLog line commented out; test added. 8/8 lines present at r17.",
      "16": "Same fix: commit d9ad94e69f4e in android-security-16.0.0_r7. Identical check with the ProtoLog line commented out; test added. 8/8 lines present at r8.",
      "17": "Same fix: commit a00072f03623 in android-security-17.0.0_r1. Identical check with the ProtoLog active; test adapted to the WindowRelayoutResult addWindow signature. 8/8 lines present at r1."
     },
     "backport_notes": [
      "The functional change is a self-contained 6-line insertion in addWindow after the canAddToastWindowForUid check. It needs only WindowToken.isEmpty() and the existing addToastWindowRequiresToken local. If you keep the ProtoLog line active on a branch that uses the pre-generated services.core.protolog.json, add the matching entry as the Android 14 commit does, or comment the line out as the 15/16 commits do.",
      "The test depends on a WindowTestsBase.createTestSession(atms, pid, uid) helper. Android 14 lacked it, so its commit adds a minimal version plus a pid/uid Session constructor. Branches without that helper need the same test-only additions."
     ],
     "confidence": "high",
     "unknowns": [
      "How addToastWindowRequiresToken is computed: its definition is not in the provided context, so the exact set of apps the new check covers is not confirmed.",
      "The concrete security impact of attaching a second toast window to a populated toast token; the bulletin gives only High / EoP.",
      "Whether the second add actually succeeded before the fix; the material has no pre-fix test result.",
      "Why the ProtoLog statement was commented out on the Android 15 and 16 branches."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28578": {
     "cve": "CVE-2026-28578",
     "summary": "Adds a per-package-name maximum length check on PackagePolicy objects passed to three DevicePolicyManagerService setters (two managed-profile contacts/caller-id access policy setters and the credential manager policy setter), so overlong package names are rejected before caller authorization and before the policy is applied.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy)",
     "weakness": "missing input length validation (caller-supplied PackagePolicy package names not length-bounded)",
     "root_cause": "The PackagePolicy setters in DevicePolicyManagerService.java accepted the caller-supplied PackagePolicy and its package name set without validating the length of each package name. The bulletin classifies the result as a DoS; the material does not show the downstream failure mode.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java: a new private helper enforcePackagePolicyPackageNamesLength(@NonNull PackagePolicy policy) iterates policy.getPackageNames(), skips null entries, and calls PolicySizeVerifier.enforceMaxPackageNameLength(pkg) for each name (by its name, expected to reject names over the maximum).",
      "DevicePolicyManagerService.java: in the two profile-owner-gated setters (hunks at ~14416 and ~14466, authorization isProfileOwner(caller) && isManagedProfile(caller.getUserId()); per commit message and tests these are the managed profile caller-id and contacts access policy setters), `if (policy != null) enforcePackagePolicyPackageNamesLength(policy);` is added after the mHasFeature early return and before getCallerIdentity/checkCallAuthorization.",
      "DevicePolicyManagerService.java: the same guarded call is added at the same position in the setter authorized by canWriteCredentialManagerPolicy(caller) (hunk at ~16445; the credential manager policy setter), again before authorization. The helper is placed after canWriteCredentialManagerPolicy and just before getCredentialManagerPolicy.",
      "Single commit touching only DevicePolicyManagerService.java; no AIDL, PackagePolicy class, or PolicySizeVerifier changes are included."
     ],
     "completeness": [
      "Covers exactly three setters that take a PackagePolicy; any other code path that accepts or constructs a PackagePolicy (e.g. restore/XML load, DevicePolicyEngine paths) is not touched by this diff and is not shown in the material.",
      "Only the length of each individual package name is bounded; the number of entries in the package name set is not limited by this change.",
      "Null entries in getPackageNames() are skipped rather than rejected.",
      "A null policy bypasses the helper (policy != null guard).",
      "The PackagePolicy policy type field is not validated by this change."
     ],
     "behavior_changes": [
      "Setting a managed profile caller-id/contacts access policy or a credential manager policy with any package name over the limit now throws instead of applying the policy. On 14 this is Preconditions.checkArgument against MAX_PACKAGE_NAME_LENGTH with message 'Package name too long' (IllegalArgumentException); on 15+ the exception comes from PolicySizeVerifier.enforceMaxPackageNameLength (type not shown).",
      "The length check runs before caller authorization: an unauthorized caller supplying an overlong name now gets the length-check exception rather than the exception from checkCallAuthorization.",
      "No behavior change when mHasFeature is false, since the early return precedes the check."
     ],
     "preconditions": [
      "mHasFeature is true (device admin feature present).",
      "For the two contacts/caller-id setters, the caller must be a profile owner of a managed profile to pass authorization; for the credential manager setter, canWriteCredentialManagerPolicy(caller) must hold (context shows it can be satisfied by hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS); the other conditions are not shown).",
      "Caller supplies a non-null PackagePolicy containing at least one overlong package name."
     ],
     "per_major": {
      "14": "adapted fix: commit 1d7703a6b66d in android-security-14.0.0_r28; same three insertion points and helper, but the helper uses Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, \"Package name too long\") instead of PolicySizeVerifier.enforceMaxPackageNameLength. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit e8ff8af05575 in android-security-15.0.0_r16 (Cherrypick-From 4914d6e98bd2 and 36421c7d97b0), uses PolicySizeVerifier.enforceMaxPackageNameLength. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 48c17df8bdc4 in android-security-16.0.0_r7, identical logic at shifted line numbers. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit 4914d6e98bd2 in android-17.0.0_r1; context differs in that authorization uses mDeviceAdmins.isProfileOwner(caller). 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "On branches without PolicySizeVerifier.enforceMaxPackageNameLength, use the Android 14 form (Preconditions.checkArgument against MAX_PACKAGE_NAME_LENGTH); confirm MAX_PACKAGE_NAME_LENGTH is already available in DevicePolicyManagerService on the target branch, as the 14 diff adds no import or definition.",
      "Insert the check after the mHasFeature early return and before getCallerIdentity in each of the three setters so behavior matches upstream ordering."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream failure caused by overlong package names (how the DoS manifests) is not shown in the material.",
      "The exact method names of the two profile-owner-gated setters, and which hunk corresponds to caller-id vs contacts access, are not in the diff context.",
      "The numeric limit and exception type of PolicySizeVerifier.enforceMaxPackageNameLength are not shown.",
      "Whether other PackagePolicy entry points exist on these branches that still lack the check.",
      "Whether these policies are persisted, and how a null policy is handled downstream."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "16": {
   "counts": {
    "announced": 57,
    "certain": 57,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-64505",
     "osv_id": "ASB-A-463980379",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In libpng's pngrtran.c png_set_quantize, the quantize_index array is now always allocated and identity-initialized to PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so png_do_quantize can no longer index past it with out-of-range palette indices; a companion commit frees any previous quantize_index before reallocating and turns quantize_sort into a local variable to stop leaks on repeated calls.",
      "confidence": "high",
      "backport_notes": [
       "Apply the leak/refactor commit (3f148ea688b5) before the allocation-size commit (93147074acc1). The second diff starts from the first's output (blob 3749ebfa6), and its context includes the png_free line and the 'Be careful to avoid leaking memory' comment. Keep the pngstruct.h quantize_sort member in security branches to avoid an ABI change, as Google did.",
       "On trees whose allocation line reads (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))) (as in Android 14 and 17), the hunk needs a small context adaptation; the replacement is the same PNG_MAX_PALETTE_LENGTH constant and loop bound."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "3f148ea688b5dc631c51699947ce5dd3654a5eb0",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "93147074acc1bebdda8316d9a891a5d4d122ca80",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-64720",
     "osv_id": "ASB-A-463995203",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In libpng's png_init_read_transformations, palette entries with partial tRNS alpha are now only premultiplied, not composited against the background, when PNG_FLAG_OPTIMIZE_ALPHA is set. This restores the premultiplied invariant component <= alpha that png_image_read_composite relies on for its png_sRGB_base[]/png_sRGB_delta[] lookups (per the commit message).",
      "confidence": "high",
      "backport_notes": [
       "Single hunk in pngrtran.c with no other files touched. It is identical across 14 through 17; the only difference is a 4-line offset between the 14/17 and 15/16 file versions. The resolver reports 6/8 lines of the patched region present in every latest tag, and the material does not explain why, so check the region by hand when porting."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "2113a245b832fe86bb2c61a99778d8e70149bd3b",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-65018",
     "osv_id": "ASB-A-463998243",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "libpng simplified read API: decoding a 16-bit PNG to an 8-bit output format could overflow the caller's PNG_IMAGE_SIZE buffer because png_combine_row writes at IHDR bit depth (interlaced images only, per the second commit). Google first rejected all 16/8 bit-depth mismatches in png_image_finish_read, then replaced that check with an intermediate local_row buffer path (do_local_scale / png_image_read_direct_scaled) that is used only for interlaced 16-to-8 conversion.",
      "confidence": "medium",
      "backport_notes": [
       "Apply in order: I07c7e84188f067ec23c48e823b46164e20705cfb (rejection), then I87bfbf70c34c466cd7c38adc702f8c4dc6305290 (rearchitect). The second commit's png_image_finish_read hunk only applies if the first commit is present; otherwise drop that hunk. The second commit relies on existing png_image_read_control fields (local_row, first_row, row_bytes) and on the png_safe_execute / do_local_compose pattern in png_image_read_direct.",
       "A branch that has only the first commit (as the material suggests for Android 15) is protected but rejects all 16/8 depth conversions. Check whether the rearchitect commit is wanted there."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "2cafeca8cbf2fa636b658b7bef0aebe900447f40",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/libpng",
       "sha": "960064fdb5f41cc085ab81cd97443dd51736c4aa",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2026-0039",
     "osv_id": "ASB-A-470966846",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow detections throw a C++ std::runtime_error through a custom in-library runtime, instead of going through the ubsan-minimal runtime and its hard abort.",
      "confidence": "high",
      "backport_notes": [
       "The change is build configuration plus one new source file. All four branches patch the same Android.bp base (index 1978b39), replacing cflags: [\"-DqDNGValidate=0\"] in the libdng_sdk cc_library. The change relies on rtti: true and -fexceptions coming from libdng_sdk-defaults.",
       "Check on the target branch that libdng_sdk consumers wrap decoding in a catch that handles std::exception, or in catch(...). The new runtime throws std::runtime_error."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0040",
     "osv_id": "ASB-A-467994860",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that UBSan integer-overflow checks call a library-local runtime that throws std::runtime_error instead of the ubsan_minimal runtime that aborts the process.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. The Android.bp hunk is identical on all four branches (base blob 1978b39), so it should apply as-is to any tree whose libdng_sdk block still reads cflags: [\"-DqDNGValidate=0\"]. It relies on libdng_sdk-defaults already setting rtti: true and cppflags -fexceptions (present in the context). It also relies on Soong honoring sanitize.never together with manual -fsanitize cflags, as the comment in Android.bp notes.",
       "The OWNERS hunk is metadata and can be dropped if it conflicts."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0041",
     "osv_id": "ASB-A-467994310",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a runtime inside the library that turns integer-overflow sanitizer hits into thrown C++ exceptions (std::runtime_error) instead of a hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "The Android.bp hunk is identical across 14, 15, 16 and 17 (same blob ids 1978b39..3d95a7b), so it should apply cleanly on branches with the same libdng_sdk module. The OWNERS hunk differs per branch and has no functional effect.",
       "The fix relies on libdng_sdk-defaults already enabling exceptions (`cppflags: [\"-fexceptions\"]`, present in the shown context). Per the Android.bp comment, `sanitize: { never: true }` is combined with raw `-fsanitize=` cflags so that the replacement handlers are used instead of ubsan_minimal."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0042",
     "osv_id": "ASB-A-456471487",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime) instead of aborting the process through the Soong-linked ubsan minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "The change is build configuration plus one new file, applied as an identical patch on all four branches (Android.bp blob 1978b39 on every branch). It needs libdng_sdk-defaults to already carry rtti: true and cppflags: [\"-fexceptions\"] (present in the shown context), and a Soong/clang toolchain that honors sanitize.never and -fno-sanitize-link-runtime (general platform knowledge). OWNERS conflicts can be resolved freely because that file has no functional effect.",
       "No 'Fix in fork' section was provided, so no per-file fork verdict is available."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0043",
     "osv_id": "ASB-A-453649377",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits (signed and unsigned add/sub/mul/negate/divrem) go to a custom in-library runtime that throws std::runtime_error, replacing the ubsan-minimal runtime and its hard abort.",
      "confidence": "medium",
      "backport_notes": [
       "Single build-only commit. It relies on libdng_sdk-defaults already providing `rtti: true` and `cppflags: [\"-fexceptions\"]` (present in context). On trees with a customized libdng_sdk sanitize configuration, check that the module no longer links the ubsan runtime. Callers should handle C++ exceptions around dng_sdk entry points, otherwise an uncaught exception terminates the process.",
       "For all majors the resolver reports a partial match (3/4 or 3/5 lines), while the android-security-16.0.0_r8 code context shows the full patched region. Check the region directly instead of relying on that metric."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0044",
     "osv_id": "ASB-A-449728942",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix rebuilds libdng_sdk (platform/external/dng_sdk) so that integer-overflow sanitizer hits no longer go to the aborting ubsan-minimal runtime. They now go to a replacement runtime local to the library that throws std::runtime_error, so a hard abort on overflowing DNG data becomes a C++ exception.",
      "confidence": "high",
      "backport_notes": [
       "The Android.bp hunk is identical on all four branches (same pre-image index, 1978b39 -> 3d95a7b), so it should apply cleanly wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"]. The fix relies on libdng_sdk-defaults enabling C++ exceptions (-fexceptions). On a fork with a different sanitizer setup, check that the resulting handler names are the non-minimal __ubsan_handle_*_overflow symbols (without the _minimal suffix) (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0051",
     "osv_id": "ASB-A-461790658",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections raise a C++ std::runtime_error (via a custom in-library ubsan runtime) instead of a hard abort through the ubsan_minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "Apply the three Android.bp pieces together with the new source file: sanitize.never: true, the explicit -fsanitize=signed-integer-overflow,unsigned-integer-overflow plus -fno-sanitize-link-runtime cflags, and the srcs entry for ubsan-replacement/ubsan_throwing_runtime.cpp. The replacement relies on rtti: true and cppflags -fexceptions in libdng_sdk-defaults; the Android.bp pre-image blob (1978b39) is the same in all four branches, so the diff applies as-is there."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0052",
     "osv_id": "ASB-A-467352655",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of aborting the process through the ubsan-minimal runtime.",
      "confidence": "high",
      "backport_notes": [
       "The change is build-only plus one new source file. When porting, both halves are needed on the same module: sanitize.never: true with the manual -fsanitize/-fno-sanitize-link-runtime cflags, and the runtime source in srcs. Keeping Soong's misc_undefined active on the module would bring back the ubsan-minimal runtime and its abort behaviour (general platform knowledge). The module must keep -fexceptions from libdng_sdk-defaults.",
       "No 'Fix in fork' section was provided, so no per-file fork verdict can be reported."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0080",
     "osv_id": "ASB-A-462431872",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a tiny in-tree runtime whose __ubsan_handle_*_overflow handlers throw std::runtime_error, so integer-overflow checks detected while parsing DNG data raise a catchable C++ exception instead of killing the process.",
      "confidence": "high",
      "backport_notes": [
       "The Android.bp hunk is identical across 14, 15 and 16 (same blob ids). On trees with a different libdng_sdk layout (as in 17), add ubsan_throwing_runtime.cpp to the library's srcs list. Keep sanitize.never together with the explicit -fsanitize and -fno-sanitize-link-runtime cflags.",
       "The change relies on libdng_sdk-defaults already having rtti: true and cppflags -fexceptions, and on the consumer catching std::runtime_error. Verify both in the target tree, otherwise the throw still ends the process."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0097",
     "osv_id": "ASB-A-446114623",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes the LE SMP state machine, for the Secure Connections Passkey Display and Key Notification association models, wait for both the remote device's passkey confirmation (STK generated in smp_process_stk) and the local user's approval (SMP_SecurityGrant) before emitting SMP_KEY_READY_EVT, cancels SMP pairing on BTM authentication timeout, and initialises LE pairing flags in btif_dm.",
      "confidence": "medium",
      "backport_notes": [
       "Land all five files together. The passkey_display_state field in smp_int.h is needed by both smp_keys.cc and smp_api.cc, and the smp_process_stk gate returns without posting SMP_KEY_READY_EVT, so it needs the SMP_SecurityGrant counterpart. On older trees, adapt the btif handler name (btif_dm_ble_key_notif_evt vs btif_dm_ble_passkey_notif_evt), btm_cb vs btm_sec_cb for pairing_flags, and the logging macros, as Google did for 14 and 15.",
       "The diff has no flag check despite the Flag: line in the commit message. Check whether your branch expects com.android.bluetooth.flags.passkey_entry_pairing_approval gating."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "ce76f82dac2a8e0e247ca9c838bda57fb9a27b04",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-21352",
     "osv_id": "ASB-A-483693973",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to build 2471. The update adds overflow-checked arithmetic and size limits to the JXL decode/encode buffer geometry and allocators, fixes an off-by-one table read in dng_opcode_MapTable::ReplicateLastEntry, and guards several divisions by zero when blackLevel is 65535.",
      "confidence": "medium",
      "backport_notes": [
       "The commit is a whole-SDK bump from 1.7.1 build 2410. It uses SafeInt32Add/SafeInt32Mult and the two-arg SafeInt64Mult without adding them, so they must already exist; it adds only the 3-arg SafeInt64Mult. The functional hunks can be backported on their own; the version and tcWebP lines are not needed for the fix.",
       "Removing the __ANDROID__ override of __USE_BUILTIN_SMULL_OVERFLOW changes SafeInt64Mult codegen on Android. The header still falls back to SafeInt64MultSlow when the builtin is unavailable."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "b0d9d2a8d1003176df7d0e8ab565ae40d64b1ec1",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-21353",
     "osv_id": "ASB-A-483697751",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to build 2471. The update bundles overflow-checked size/stride arithmetic and dimension limits in the JXL decode/encode paths, an overflow check in two raw allocators, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and divide-by-zero guards for blackLevel == 65535.",
      "confidence": "medium",
      "backport_notes": [
       "Single commit, applied verbatim on 14/15/16/17. The dng_jxl.cpp hunks depend on the new 3-arg SafeInt64Mult added in dng_safe_arithmetic.cpp/.h in the same commit, and on the existing SafeInt32Add/SafeInt32Mult/SafeUint32Mult and DNG_REQUIRE, so take the whole commit rather than individual files.",
       "The latest security tags on every major report only 5/8 lines of the patched region present, and the material does not explain why. The tag is already at build 2502, so code may have changed since; verify each hunk directly rather than relying on the line count."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "b0d9d2a8d1003176df7d0e8ab565ae40d64b1ec1",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-32348",
     "osv_id": "ASB-A-406880479",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops the Background Activity Launch (BAL) \"visible window\" and \"activity in foreground task\" exemptions from being satisfied by an activity in pinned (PIP) windowing mode. It does this by adding a not-pinned visibility query and a pinned-window flag to the BAL decision path.",
      "confidence": "high",
      "backport_notes": [
       "All production changes are in b96d7a515e9e. c879fb5561f2 and 4d488a6a05ab are test-only and cancel each other out.",
       "When porting, match the target's BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed signature (BalCheckConfiguration record on 16 vs. boolean isCheckingForFgsStart on 14/15) and keep inPinnedWindow right after hasActivityInVisibleTask. Where the visibility check still uses mService.hasActiveVisibleWindow(uid), replace it with hasVisibleNotPinnedActivity(uid) || mActiveUids.hasNonAppVisibleWindow(uid), as the 14 and 15 backports do."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r1",
      "date": "2025-11-20",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "b96d7a515e9e9c8e2e8351232c2b7cc502074a37",
       "tag": "android-security-16.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "c879fb5561f221534bf9d13d58b497b8b20516ab",
       "tag": "android-security-16.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "4d488a6a05abacba0859fff475e3b00c46d79e01",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "7dd276c1cf343d034480ac5c8cc646f6da2a89d6",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "2c84985e4257eec96ea4e2d2d5798df6b47aae62",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 9,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 193,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48581",
     "osv_id": "ASB-A-428945391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds sub_dir: \"sysconfig\" to the apexd.mainline_patch_level_2 prebuilt_etc module in platform/system/apex so the com.google.android.mainline.patchlevel.2.xml feature declaration is installed under the sysconfig directory, where it becomes accessible to the device.",
      "confidence": "high",
      "backport_notes": [
       "A single one-line Soong change with no dependencies. It only applies to trees that contain patchlevel/Android.bp with the apexd.mainline_patch_level_2 module. Forks that install the XML some other way (e.g. PRODUCT_COPY_FILES) should check that the file ends up in etc/sysconfig and not in etc."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/system/apex",
       "sha": "117f34be6322c6a608a1c3e06f1d03ec32da60b3",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 117f34be6322",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/build",
      "platform/system/apex"
     ]
    },
    {
     "cve": "CVE-2025-48595",
     "osv_id": "ASB-A-430889718",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fix is a bundled SQLite version bump. On Android 16 the 3.44.5 amalgamation was installed under external/sqlite and selected through the RELEASE_PACKAGE_LIBSQLITE3 release flag (3440300 to 3440500), but the flag flip was reverted one security tag later because of a performance regression. Android 14 instead upgrades its single-version external/sqlite tree from 3.39.2 to 3.39.6.",
      "confidence": "medium",
      "backport_notes": [
       "Branches that use the release-flag scheme need both parts, in order. First land the new dist/sqlite-autoconf-<ver>/ tree in external/sqlite, with Android.patch reapplied to the new upstream amalgamation. Then set RELEASE_PACKAGE_LIBSQLITE3 for the target release config in build/release flag_values. Branches without the flag (the Android 14 layout) upgrade dist/ in place.",
       "The 3.44.5 selection was reverted on Android 16 for a performance regression. A backport should check performance and confirm which sqlite version the target branch actually selects, since the Android 16 security tags end up back on 3440300."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/build/release",
       "sha": "b11607ea3ec7ab3183b558392850c842c582d2cc",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "cbebed38b5798baa61d3924de8e09db9f458292c",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/build/release",
       "sha": "9484064212935962adbd8b6f4e028f2d5179433e",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "1612b56a5b4092f8256dfaa2096e9680d3f47f98",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/external/sqlite",
       "sha": "aaebce3d8fd545d05f16cc9a137f22da8cad52c8",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 9,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/build/release",
      "platform/external/sqlite"
     ]
    },
    {
     "cve": "CVE-2025-48612",
     "osv_id": "ASB-A-429417453",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' NFC default payment app picker stops parsing a ComponentName and user id out of the preference key string. It now looks the key up in a map of the payment apps it enumerated itself, so only a known candidate can be set as default.",
      "confidence": "high",
      "backport_notes": [
       "Self-contained change touching only DefaultPaymentSettings.java and PaymentBackend.java. The same changed lines appear on 14, 15, 16 and 17. On 16 the reland 46ddb9d440ca applies to a different DefaultPaymentSettings.java blob (d75844bb0ff -> b5057ed64f2) than the original commit. Make sure the final state is the Revert^2 (46ddb9d440ca), not the intermediate revert f2babe193c76. The material does not describe the regression, so retest the payment picker after applying."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "aa744e8988f0e7b77a71087edd4d2546b58d2f24",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "46ddb9d440cad942163700985242d2206f29cc40",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "f2babe193c762ee4ee1b03a77e55d4a4acb1f3b0",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48615",
     "osv_id": "ASB-A-433250316",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a 1024-character cap on the combined package-plus-class name of a media button receiver in MediaButtonReceiverHolder. The first commit covers receivers resolved from a PendingIntent; the second covers receivers set directly as a ComponentName.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the commits in order. bb15e7f14f63 depends on MAX_COMPONENT_NAME_LENGTH and rewrites check lines added by a5795fc0cf1f. With only the first commit, create(int, ComponentName) stays unchecked, so verify the second commit on the 14 and 15 branches, where the material shows only the first.",
       "Both changes are confined to MediaButtonReceiverHolder.java. The first commit's pre-image is index e7e70fc1f7b2; the second's is ffa2bcbd6fd2, the first commit's post-image."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a5795fc0cf1f21da88cf05ad06610d3653d1be0e",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "bb15e7f14f638ead9bfc57374e90f9f108f3a5e6",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "public_before_announcement_days": 91,
     "first_commit_before_announcement_days": 182,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48616",
     "osv_id": "ASB-A-438973280",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "KeyguardViewMediator now queries KeyguardUpdateMonitor.isUserInLockdown (the strong auth tracker) instead of LockPatternUtils.isUserInLockdown at three places that decide whether lockdown forces the keyguard to show, so lockdown is honored even when the keyguard is externally disabled (e.g. app pinning).",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. Across branches the only adaptation is the user-id accessor: KeyguardUpdateMonitor.getCurrentUser() in the 14 and 17 diffs, mSelectedUserInteractor.getSelectedUserId() in 15 and 16. The fix relies on KeyguardUpdateMonitor exposing isUserInLockdown(int) and being available as mUpdateMonitor in KeyguardViewMediator.",
       "When checking a fork, verify all three KeyguardViewMediator call sites directly. The resolver's 6/8 partial match does not identify which lines are missing."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r3",
      "date": "2025-12-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "aa9fbb1b74eb11ce0bd48f0c4cab43fc32a23b4e",
       "tag": "android-security-16.0.0_r3",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 182,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48648",
     "osv_id": "ASB-A-396667508",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "NotificationManagerService stops using PackageManagerInternal.isSameApp(pkg, uid, userId) for package-ownership checks. It now compares the caller uid against the package's real uid with UserHandle.isSameApp. It also rejects SDK sandbox uids in areNotificationsEnabledForPackage, resolves USER_ALL to the caller's user, and moves the system/shell check ahead of canNotifyAsPackage in getConversationNotificationChannel.",
      "confidence": "medium",
      "backport_notes": [
       "Only the NotificationManagerService.java changes affect production code. The large test rewrite depends on UiServiceTestCase fields (mPmi, mUid, mUserId, user-relative UID_*), on setUpMockZenTest and on SystemUtil from compatibility-device-util. On 16 it needed two follow-up rename fixes (fd6f923dc9c2, 3bbdaf62767b) to compile, so apply those after the main commit."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-opus-5-5",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3bbdaf62767b5317d9f982a983cd25b2f1fa0de7",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "b88956f3198d591897fd81191b71b001d12e2c19",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "fd6f923dc9c2e485211cdcb0dcf5da75d774775d",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48649",
     "osv_id": "ASB-A-339109116",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Threads a restorePregrantedPermissions flag from ActivityManager.clearApplicationUserData() through AMS, PMS and both permission backends. An app clearing its own data no longer gets default- or role-granted runtime permissions re-granted. Privileged callers (CLEAR_APP_USER_DATA) keep the restoring behaviour through a separate path.",
      "confidence": "high",
      "backport_notes": [
       "IPackageManager.clearApplicationUserData and PermissionManagerServiceInterface/Internal.resetRuntimePermissions change signature. Every implementor and caller (decorators, TestingShim, LegacyPermissionManagerService, CrossUserPackageVisibilityTests, AppIdPermissionPolicyPermissionResetTest) must be updated in the same change or the build breaks. Pass true everywhere except the AMS self-clear route.",
       "On trees where PermissionService.kt resetRuntimePermissions is still a TODO stub (as in the 14 diff), only the signature needs updating and the AppIdPermissionPolicy.kt hunk does not apply. On trees where it is implemented (15/16), both the Java and Kotlin backends need the flag."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7b9a906980a34008cb7e3b93c5ba1d84a2cfaefc",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48652",
     "osv_id": "ASB-A-452042097",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstaller (both the legacy InstallStart activity and the v2 InstallRepository) now applies the unknown-sources user restrictions to ACTION_VIEW / ACTION_INSTALL_PACKAGE intent installs even when the caller holds INSTALL_PACKAGES. The only exemptions left are PackageInstaller-API (non-intent) installs by INSTALL_PACKAGES holders and privileged callers that set EXTRA_NOT_UNKNOWN_SOURCE.",
      "confidence": "high",
      "backport_notes": [
       "This is a reapply: account for both the original patch and its revert (29962260bd04) so the tree ends with the reland. The v1 InstallStart.java hunk relies on the locals `isInstallPkgPermissionGranted` and `isPrivilegedAndKnown` already existing in onCreate.",
       "For the v2 InstallRepository.kt hunk, use the uid variable that matches the branch: Android 16 uses callingUid, Android 17 uses originatingUid. In both cases the helper isInstallRequestFromTrustedSource is removed and its logic inlined."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4dc009cd724cc1cac5202737a36fd99a36817fef",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 4dc009cd724c",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0009",
     "osv_id": "ASB-A-329631990",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Removes the Photopicker MainActivity collector that auto-confirmed and returned the result as soon as one item was selected in single-select mode, and instead always enables the SelectionBar so the user must explicitly confirm the selection, with SelectionImpl.toggle replacing the existing item when selectionLimit == 1.",
      "confidence": "high",
      "backport_notes": [
       "This is a Revert^2: an earlier landing was reverted and relanded after failing CTS tests were fixed; the CTS changes are not in the material, so a backport should check the corresponding CTS state in addition to carrying the photopicker unit test updates (SelectionImplTest, SelectionBarFeatureTest).",
       "Branch-specific pieces differ: Android 15 needs the two-argument isEnabled(config, deferredPrefetchResultsMap) signature and the PhotopickerEventLogger lines in onCreate (verify PhotopickerEventLogger exists in the target), while Android 17 additionally strips EMBEDDED gating in MediaGrid.kt and SelectionBar.kt."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "61808742c17acb107221fa5f0ce998532dbc0bed",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/packages/providers/MediaProvider"
     ]
    },
    {
     "cve": "CVE-2026-0016",
     "osv_id": "ASB-A-460933604",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes Credential Manager package-update handling user-correct: CredentialManagerService.updateProvidersWhenServiceRemoved now reads and writes the Secure settings of the passed userId instead of UserHandle.myUserId(), and AbstractMasterSystemService.removeInvalidCachedServicesLocked only acts on cached services belonging to the user whose package changed.",
      "confidence": "high",
      "backport_notes": [
       "Apply 10b748dbc348, then ffdfa5c70fca. On branches without the android.credentials.flags aconfig library, use the Android 16 unflagged form (direct userId substitution) and drop the SetFlagsRule/Flags test references.",
       "Both hunks are small and self-contained. The AbstractMasterSystemService hunk requires the per-user service class to expose getUserId() (called as s.getUserId() in the lambda)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "10b748dbc3488c8deb232f47b923c93d8ab01371",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "ffdfa5c70fcaeabb63ef0ec41c33acd2466316dd",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0018",
     "osv_id": "ASB-A-449392803",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AccessibilityManagerService now validates, via PackageManagerInternal and outside its lock, that every tileService name declared by an accessibility service or shortcut target resolves to an existing, exported, enabled TileService protected by BIND_QUICK_SETTINGS_TILE before AccessibilityUserState records it in its feature-to-tile maps.",
      "confidence": "medium",
      "backport_notes": [
       "The 16 commit lists two Cherrypick-From changes (637f5b4aa4fb and 9d9034b7f4fb). When backporting, carry AccessibilityTileUtils.java, the AccessibilityUserState signature change, and all AccessibilityManagerService call sites together. The old zero-argument update*Locked methods are removed, so callers must compile against the new signatures.",
       "Branches without Flags.clearShortcutsWhenActivityUpdatesToService (as on 15) or with mInstalledServicesMap (as on 17) need the readInstalledAccessibilityServiceLocked / readInstalledAccessibilityShortcutLocked hunks re-contextualized. The logic is the same on every branch: always rebuild the map, then return the changed flag."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r4",
      "date": "2026-03-02",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7783a5265907eb5a4f14287d81ac4ab1249053a0",
       "tag": "android-security-16.0.0_r4",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "public_before_announcement_days": 91,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0036",
     "osv_id": "ASB-A-405392600",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "StageCoordinator.startAnimation now checks a non-pending transition that arrives while split is inactive. If its opening tasks belong to only one split stage, it starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea, then still returns false so another handler plays the original transition.",
      "confidence": "high",
      "backport_notes": [
       "On branches where StageCoordinator does not keep a Transitions reference: add the mTransitions field and set it in every constructor, make StageTaskListener public, and provide isOpeningMode locally if com.android.wm.shell.shared.TransitionUtil is absent. The ProtoLog import also differs: com.android.internal.protolog.common.ProtoLog on 14/15/17 versus com.android.internal.protolog.ProtoLog on 16.",
       "Use the branch's own split-active check (isSplitActive() on 16, mMainStage.isActive() on 14/15/17). The test file has the same blob hash on all branches."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "04814cb8a86e78a6c9ae9c061c491f36e5c552f9",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0045",
     "osv_id": "ASB-A-380091558",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Stops outgoing secure RFCOMM socket connections from registering their security mask on the shared RFC_MUX security service record (which made later incoming RFCOMM MUX connections enforce bonding and start pairing), and makes the temporary-bonding security upgrade check in btm_sec_is_upgrade_possible direction-aware and unconditional (flag upgrade_temp_bonding_on_auth_req removed).",
      "confidence": "medium",
      "backport_notes": [
       "The BTM_SetSecurityLevel RFC_MUX call in bta_jv_rfcomm_connect appears in three forms across branches: guarded by rfcomm_always_use_mitm() (15), guarded by !upgrade_temp_bonding_on_auth_req() (16 release per index search at android-16.0.0_r4), or unconditional (16 security branch); remove it in whichever form is present and drop the flag definition if it exists.",
       "Field access differs: older branches use p_dev_rec->security_required and p_dev_rec->is_bond_type_temporary(), newer ones use p_dev_rec->sec_rec.*; adapt the btm_sec_is_upgrade_possible hunk accordingly and leave it unflagged."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "2ad8e1e0e0e5adae3abcbab34b9e36eda4a2e36f",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0046",
     "osv_id": "ASB-A-443272513",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In services/core/java/com/android/server/wm/Letterbox.java, the InputWindowHandle of the Letterbox InputInterceptor now reports the letterboxed app's session pid/uid (win.mSession.mPid/mUid) as its owner instead of WindowManagerService.MY_PID/MY_UID, so the letterbox input window is no longer treated as a trusted system window by the obscured-touch (tapjacking) checks.",
      "confidence": "high",
      "backport_notes": [
       "Single hunk inside the Letterbox.InputInterceptor constructor. On branches without Flags.scrollingFromLetterbox(), apply it against the TYPE_INPUT_CONSUMER / NOT_FOCUSABLE | SLIPPERY context, as the 14 and 15 commits do. It uses win.mSession.mPid/mUid from the WindowState the constructor already receives."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "678475b0c23cd3edc527b0b21e42fbafc028ee1a",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0048",
     "osv_id": "ASB-A-463364410",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowState.hide() now cancels any running animation on a window that is being policy-hidden (force-hidden non-system overlay, suspended, app-op hidden or permanently hidden). If that window is already playing an exit animation, it is also hidden immediately without animation, instead of the hide waiting for the exit animation to finish.",
      "confidence": "high",
      "backport_notes": [
       "The hunk is identical across 14 through 17. It only uses existing WindowState fields (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, mAppOpVisibility, mPermanentlyHidden, mAnimatingExit) and existing isAnimating()/cancelAnimation() methods. Place it right after the '!mToken.okToAnimate()' check inside the 'if (doAnimation)' block of hide()."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "36a774d7239923d0ef16ae5f51b87fb132e2bbb9",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0050",
     "osv_id": "ASB-A-290364858",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "AdapterService.handleBondStateChanged now runs its existing permission revocation block whenever fromState is BOND_BONDED, not only when toState is BOND_NONE. As a result, a rebond attempt by a device reusing a bonded address does not keep the previous device's granted access permissions.",
      "confidence": "high",
      "backport_notes": [
       "The Android 15/16 versions use the unconditional form `toState == BOND_NONE || fromState == BOND_BONDED`, without the Flags.rebokePermissionOnUnbond() gate used on 17. Use that form on branches that lack the aconfig flag, and add the BOND_BONDED static import. The hunk sits in handleBondStateChanged right after mDatabaseManager.handleBondStateChanged(device, fromState, toState)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "d0f7d128d436220e10c876c131208f580b4bb829",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0055",
     "osv_id": "ASB-A-460779368",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstallerService.createSessionInternal now validates params.volumeUuid with StorageManager.convert() when a session is created with INSTALL_FORCE_VOLUME_UUID, rejecting malformed values (such as \"../../../data\") with an IllegalArgumentException instead of carrying them further into the install stack.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk inside the INSTALL_FORCE_VOLUME_UUID else-if of createSessionInternal; the hunk context is identical on 14/15/16/17. The diff adds no import, which implies android.os.StorageManager was already imported in PackageInstallerService.java on those branches; verify this on forks."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "62ec466efd801a253a1134011bf9c0e83f1bfb1d",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0056",
     "osv_id": "ASB-A-462431486",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds validation in ResStringPool::setTo so that a ResStringPool_header.styleCount that would overflow when multiplied by sizeof(uint32_t), or whose style offset array would extend past stringsStart, is rejected with BAD_TYPE instead of being accepted.",
      "confidence": "high",
      "backport_notes": [
       "Single commit, applied verbatim on 14 through 17 with only line-offset differences. The ResourceTypes.cpp hunk goes after the existing stylesStart-vs-header.size check and right before mStylePoolSize is assigned. mEntryStyles must already be set at that point.",
       "The new test uses androidfw/StringPool.h, androidfw/Util.h (android::util::Copy) and BigBuffer. The test file is identical on all four branches, which suggests these helpers are available there. No 'Fix in fork' section was provided, so no fork verdict can be reported."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d5a52f737cabce3d9a58e8c23f6201ea658287ac",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0059",
     "osv_id": "ASB-A-447536200",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "RCE"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a remaining-bytes argument and bounds check to sdpu_build_attrib_seq so that the attribute-ID sequence written into the fixed-size SDP request buffer (SDP_DATA_BUF_SIZE, osi_malloc'd) cannot exceed the buffer, and adds the missing buffer accounting (bytes_left / base_bytes) to process_service_attr_rsp.",
      "confidence": "high",
      "backport_notes": [
       "Single commit. It relies on the pre-existing bytes_left accounting in sdp_snd_service_search_req / process_service_search_attr_rsp and on the bytes_left-aware sdpu_build_uuid_seq. If a tree lacks those, port that change first or add bytes_left locally. The loop-index cleanups in sdp_utils.cc are optional and can be dropped to reduce conflicts.",
       "Google used [RESTRICT AUTOMERGE] per-branch versions for 14 and 17. When adapting, match the SDP_INVALID_CONT_STATE spelling (enum-scoped or bare) to the target branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "94566a29d020042ec4b423119f4d019d958b1aaf",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0060",
     "osv_id": "ASB-A-459639258",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The Settings developer option \"Experimental: Enable ANGLE\" sets ANGLE as the system OpenGL ES driver (persist.graphics.egl), which the commit message says can leave a commercial device unusable until a factory reset. The Android 16 fix only adds warning text (back up your data; a factory reset may be needed) to the switch summary and the reboot dialog. The Android 14 fix also gates the switch behind a debug system property.",
      "confidence": "medium",
      "backport_notes": [
       "Order for branches like 16: dc00e06e9bb1 (default strings), then 9666d567fd5a (translations; marked 'DO NOT MERGE ANYWHERE', so branch-specific), then 32c6cb147d3e (restyle).",
       "The text-only commits add no functional safeguard. Consider also taking the Android 14 controller gate (2097b24534af) on branches that lack it. The material does not show whether 15/16/17 already have it."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "32c6cb147d3e9b1c6d28629974995b256eb090d4",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "9666d567fd5aac38b73ccf12b7a8cfd51bb6eabd",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "dc00e06e9bb1d59e65b0620a3a4cb19fa23160b1",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 3,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0061",
     "osv_id": "ASB-A-452010556",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowManager now applies legacy policy visibility (hide/show) to the server-owned WindowState surface (mSurfaceControl) in addition to the existing mWinAnimator hide, so a client cannot undo a policy-driven hide by re-showing its own ViewRootImpl surface.",
      "confidence": "high",
      "backport_notes": [
       "A single commit made of three small additive blocks in WindowState.checkPolicyVisibilityChange(), show() and hide(). The same hunks appear in the 14, 15, 16 and 17 commits, even though the nearby mWinAnimator.hide call uses getGlobalTransaction() on 14/17 and getPendingTransaction() on 15/16."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4518f78f8b34510a1329c60dc5e0b018f015a6e3",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0067",
     "osv_id": "ASB-A-470967228",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error from library-supplied handlers instead of going to the ubsan_minimal runtime, which hard-aborts the process (per commit message).",
      "confidence": "medium",
      "backport_notes": [
       "Apply the sanitize/cflags/srcs changes to the libdng_sdk cc_library, not to libdng_sdk-defaults, and check that the defaults still set rtti: true and cppflags -fexceptions. On trees where the module lists its sources inline (as on 17), append the new file to that srcs list.",
       "Do not add -fno-sanitize-recover for these checks. With it, the compiler references *_abort handler symbols that the replacement runtime does not define."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 9,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0070",
     "osv_id": "ASB-A-438186009",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a new framework resource list application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings) and makes DevicePolicyManagerService.setApplicationHidden refuse to hide any package on that list, because hiding them in a work profile could prevent the device from booting.",
      "confidence": "high",
      "backport_notes": [
       "Single commit, but all three files must be ported together: the string-array in core/res/res/values/policy_exempt_apps.xml, the java-symbol in core/res/res/values/symbols.xml (without it R.array.application_hidden_policy_exempt_apps does not resolve), and the DevicePolicyManagerService changes. The resource lives in framework-res, so framework-res must be rebuilt (general platform knowledge). OEMs whose Settings or NFC packages use different names need to overlay the array.",
       "The patched line is identical in all branches. Only the preceding context line differs (the enforcePermission line in 15/16, a closing brace in 14/17), so the Java hunk may need manual adjustment. Validation test: CtsDevicePolicyManagerTestCases ManagedProfileTest#testHideAllApps."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "dc3785a739bc197b6d71fa57ff798423fa51cb6b",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0074",
     "osv_id": "ASB-A-468061774",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds hidden per-process/per-thread default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and registers a Launcher-process default listener (LauncherProcessImageListener) that downscales images whose estimated decoded size exceeds 300 MB and rejects mime types outside an allowlist, to limit memory use when the Launcher decodes large images.",
      "confidence": "medium",
      "backport_notes": [
       "Apply the frameworks/base ImageDecoder change first: the Launcher3 commit calls the @hide ImageDecoder.setDefaultProcessListener, which will not compile or will fail at runtime without it. The material lists the base commit only for Android 16, so confirm ImageDecoder has setDefaultProcessListener on the target branch before taking the Launcher3 change. Launcher3 quickstep builds against the framework internal API, so hidden methods are reachable (general platform knowledge).",
       "The Launcher3 patch is self-contained new files plus two small hunks; only the QuickstepProcessInitializer.init insertion point and config.xml trailing context differ per branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4abc0b8da1e97accf0213fd228f663f0fc6176a4",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "8c33391368b8d4c08e4f32d8503dc97d9eff4b90",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "projects": [
      "platform/frameworks/base",
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2026-0075",
     "osv_id": "ASB-A-465133716",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "ContactsProvider2 now replaces SQLiteException messages containing \"json\" with a generic \"Stripped exception message\" for callers that fail checkCallingPermission(READ_PERMISSION) (query) or checkCallingPermission(WRITE_PERMISSION) (insert/update/delete). It also consumes the query cursor inside the try block, so SQLite errors raised while the cursor is filled reach the same stripping path.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. It relies on code already in ContactsProvider2: the LogFields.Builder/LogUtils logging in the catch/finally blocks of query/insert/update/delete, the READ_PERMISSION/WRITE_PERMISSION constants, PERMISSION_GRANTED and Locale. All four Google branches carry identical hunks. Trees without this logging code need the helper call placed in whichever catch block rethrows to the caller.",
       "The material includes no fork comparison, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "bb9f36b3ff6d42136afb61d527e1e8f67242fe32",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0076",
     "osv_id": "ASB-A-470115162",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a check in ResXMLTree::validateNode (libs/androidfw/ResourceTypes.cpp) that rejects a START_ELEMENT node whose attrExt->attributeSize is smaller than sizeof(ResXMLTree_attribute), returning BAD_TYPE instead of accepting the node.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained 5-line check inside ResXMLTree::validateNode with no new code dependencies. Google applied the same hunk on 14 through 17, differing only in line offsets. The test file already includes androidfw/Util.h, and its existing tests use util::HostToDevice32. The new test also uses util::HostToDevice16."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a91d16a02b51b7a5652031e485508eee655dff47",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0078",
     "osv_id": "ASB-A-445418705",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService now rejects a global proxy spec or exclusion list string longer than 65535 modified UTF-8 bytes via PolicySizeVerifier.enforceMaxStringLength before storing it in the ActiveAdmin.",
      "confidence": "high",
      "backport_notes": [
       "The only dependency is android.app.admin.PolicySizeVerifier.enforceMaxStringLength(String, String). If the target branch lacks that class, take the 14/17 variant of the commit, which ships a self-contained PolicySizeVerifier.java. Its com.android.modules.utils.ModifiedUtf8, Bundle, Parcelable, PersistableBundle, ComponentName, ArrayDeque and Queue imports are unused by the shipped code and can be dropped if they do not resolve.",
       "The test change requires adding <set-global-proxy /> to device_admin_sample.xml so that the test admin holds the policy checked by getActiveAdminForCallerLocked before the new check is reached."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "50cbf4422ac8e930c522642f5dce0724581a6aa9",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0079",
     "osv_id": "ASB-A-470966318",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow checks call a library-local runtime that throws std::runtime_error, instead of the ubsan minimal runtime that hard-aborts the hosting process.",
      "confidence": "high",
      "backport_notes": [
       "Apply the Android.bp pieces together: sanitize.never: true, the explicit -fsanitize cflags, -fno-sanitize-link-runtime and the new source file. The inline comment says 'never' is meant to be combined with the -fsanitize= cflags. The module relies on cppflags -fexceptions and rtti: true, which are present in libdng_sdk-defaults in the shown context.",
       "Branch layout differs: on 14/15/16 the module only has a cflags line to replace, and srcs is added as a new block. On the 17 Android.bp the file is appended to the module's existing source list."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "change_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "74a5a91d169a341db58d57e0bf018d0a8b784cce",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "change_id",
       "confidence": "certain",
       "evidence": "ref 74a5a91d169a",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0085",
     "osv_id": "ASB-A-414389102",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider look up the ContentValues key case-insensitively, using a new findKey helper. A simple field supplied under a case-variant column name such as \"DaTa1\" is now truncated to the configured maximum size instead of slipping past the limit.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The source change is identical on all four branches and touches only applySimpleFieldMaxSize plus the new private findKey. The only difference between branches is in the tests: @Test annotations on 16, none on 14, 15 or 17."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "0d925d762547a49821187702948de39e4b05bf40",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0087",
     "osv_id": "ASB-A-483142784",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" or hosts ending in \".xyz.com\", instead of any host whose string merely ends in \"xyz.com\" (e.g. \"abcxyz.com\").",
      "confidence": "high",
      "backport_notes": [
       "Self-contained single-condition change in DomainVerificationService.java with no dependency shown; the only cross-version difference is in the test (service.addPackage(pkg) on 14/17 diffs vs service.addPackage(pkg, null) on 15/16), so adapt the test to the local addPackage signature."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 1,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2e3d71d027b090e6466b96995678e38b22ba3fe1",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0088",
     "osv_id": "ASB-A-471127462",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "CertInstaller.getCallingAppLabel() now returns appInfo.loadSafeLabel(pm) instead of appInfo.loadLabel(pm), so the calling app's label shown in the CA-certificate redirect dialog is sanitized before display.",
      "confidence": "high",
      "backport_notes": [
       "Single-commit, single-line change with identical blob hashes (883c897..1eff53f) on all four branches, so it applies cleanly wherever getCallingAppLabel() has the same shape. Forks that moved the label lookup elsewhere should apply loadSafeLabel at every point where the calling app's label is loaded for display.",
       "loadSafeLabel(PackageManager) is a hidden API on PackageItemInfo. The build needs access to framework hidden APIs, which platform CertInstaller has (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "c935d8d079131d200b11389bf01ab0ff8034ad00",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2026-0091",
     "osv_id": "ASB-A-438742644",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Changes IActivityTaskManager.setRunningRemoteTransitionDelegate to take a transition token instead of a caller-supplied IApplicationThread. The animation delegate is now recorded server-side on the Transition, either from the RemoteTransition at request time or through a new WindowContainerTransaction SET_ANIMATION_DELEGATE hierarchy op. The delegate's IApplicationThread is no longer sent out of WM core in TransitionRequestInfo.",
      "confidence": "medium",
      "backport_notes": [
       "The change spans the AIDL, the framework (services/core/wm) and WM Shell in one commit; WM Shell is built into SystemUI, so all parts must land together (general platform knowledge). Carry the delegate binder in a HierarchyOp field that exists on the target branch (mInsetsFrameOwner on 15/16, mReparent via setReparentContainer/getNewParent on 14), and pick an unused HIERARCHY_OP_TYPE value.",
       "Branches that still have the legacy RemoteAnimationAdapter path in StageCoordinator (14/15) had the adapter.getCallingApplication() boost calls removed in Google's fix."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "40c30bb5613ee94403723b37d0d6c58197d3d1b4",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0093",
     "osv_id": "ASB-A-473812391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Replaces ApplicationInfo.loadLabel() with loadSafeLabel(PackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where the Settings credential-management request UI shows the requesting app's label.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. It needs only ApplicationInfo/PackageItemInfo.loadSafeLabel(PackageManager, float, int), PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and the TextUtils.SAFE_STRING_FLAG_* constants, which the platform Settings app can use (general platform knowledge). Apply it to all three loadLabel sites. Google applied the same hunks on 14 through 17; only an unrelated neighboring import differs.",
       "The material has no fork comparison section, so no per-file fork verdict can be given."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "88ab2340c2559d8b74cdf4e2acc14fb5327f2481",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0095",
     "osv_id": "ASB-A-484861632",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Widens the local buf_size in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the allocation size passed to osi_malloc is no longer truncated before the memcpy of no_of_bytes into the new buffer.",
      "confidence": "high",
      "backport_notes": [
       "Single-line change with no dependencies. Across branches, only the hunk line numbers and the function signature formatting differ (wrapped on 14/15/17, single line on 16)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "abc8b696b615415de6608afb726b1da4214fb6af",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0096",
     "osv_id": "ASB-A-473005624",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Settings' Bluetooth forget-device dialog fragment now loads app labels via ApplicationInfo.loadSafeLabel (MAX_SAFE_LABEL_LENGTH, TRIM | FIRST_LINE flags) instead of the raw PackageManager.getApplicationLabel, so app-supplied labels are sanitized before use in the dialog.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit touching getAppLabel and its robotest. The robotest must mock loadSafeLabel on the ApplicationInfo mock instead of PackageManager.getApplicationLabel."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 10,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "24b35b078f392dc4d649338ec123dd3f8928440b",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0098",
     "osv_id": "ASB-A-397217317",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DocumentsUI's Shared.getCallingPackageName now only honors the Intent.EXTRA_PACKAGE_NAME calling-package override when the caller's UID has the platform signature (checkSignatures against SYSTEM_UID). Before the fix, any app with FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP was accepted.",
      "confidence": "high",
      "backport_notes": [
       "Single change to Shared.java; the material shows no dependency on other commits. Google applied the same hunks on 14, 15, 16 and 17. Only the hunk line offsets differ: line 227 in the 16 commit, line 238 in the 14/15/17 commits."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "0c8b7870ece9dcdd9a6cb56a98db20fcfacfcc56",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    },
    {
     "cve": "CVE-2026-0099",
     "osv_id": "ASB-A-385917501",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds onNullBinding overrides to both ServiceConnection implementations in HostEmulationManager, and an onBindingDied override to the per-service connection, so that the NFC process unbinds from a HostApduService whose onBind returned null or whose binding died instead of keeping the binding; also adds a SERVICE_NULL_BINDING event-log enum value.",
      "confidence": "high",
      "backport_notes": [
       "The source location differs by release: src/com/android/nfc/cardemulation/HostEmulationManager.java in platform/packages/apps/Nfc on 14/15, versus NfcNci/src/... in platform/packages/modules/Nfc on 16/17. NFC is a mainline module on newer releases (general platform knowledge). On trees where NfcEventProto does not exist, drop the proto enum and the NfcEventLog call. The 14 fix also adds onBindingDied to the payment connection.",
       "On 14/15 the per-service connection is an anonymous mConnection, while on 16 it is HostEmulationServiceConnection with mComponentNameToConnectionsMap. The unbind helper you use (unbindServiceIfNeededLocked versus unbindService(this)) must match the state tracking in your tree."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "127162a9e7645b4ac8bfc5ece80e0dabb9ddf61f",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Nfc"
     ]
    },
    {
     "cve": "CVE-2026-0100",
     "osv_id": "ASB-A-484973621",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp). If a RES_TABLE_PACKAGE_TYPE chunk has a 32-bit package id above 255, the package load now fails instead of storing that value as the package id.",
      "confidence": "high",
      "backport_notes": [
       "A single self-contained commit. The production hunk replaces 'loaded_package->package_id_ = dtohl(header->id);' in LoadedPackage::Load with 7 lines, and Google applied it identically on 14 through 17. In the Android 16 context, std::numeric_limits<uint8_t> is already used by the adjacent typeIdOffset check."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "ca855338abcfd12831122437d9d01ede37539bd5",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28574",
     "osv_id": "ASB-A-496735702",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "In HostEmulationManager.HostEmulationServiceConnection.onServiceConnected, when the HCE state is already STATE_IDLE and the connected service is not the preferred service, the fix now unbinds the service connection and removes its entry from mComponentNameToConnectionsMap. Before the fix, the code returned and left the binding alive.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained hunk inside onServiceConnected. It uses mComponentNameToConnectionsMap and ComponentNameAndUser, which already exist in the Android 16 code around the fix."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "477d0eb9f0f6ff7c0d40b7c19ce71aa4a294dc13",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Nfc"
     ]
    },
    {
     "cve": "CVE-2026-28577",
     "osv_id": "ASB-A-389950114",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the supplied WindowToken already contains a window, enforcing one toast window per toast token.",
      "confidence": "high",
      "backport_notes": [
       "The production change is a self-contained 6-line insertion after the canAddToastWindowForUid check in addWindow. The only branch-specific choice is the ProtoLog.w line: keep it and add the services.core.protolog.json entry as the Android 14 commit does, or comment it out as 15 and 16 did.",
       "If the target branch lacks WindowTestsBase.createTestSession(atms, pid, uid), take the Session constructor and helper backport from the Android 14 commit (2f08e7d895e3)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d9ad94e69f4e810afbe983a31e4efae1b34a53c6",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28578",
     "osv_id": "ASB-A-475228205",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "DevicePolicyManagerService now rejects PackagePolicy objects containing over-long package names in the managed profile contacts/caller-id access setters and the credential manager policy setter. It loops over policy.getPackageNames() and enforces the maximum package name length before the policy is accepted.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained commit. The only dependency is PolicySizeVerifier.enforceMaxPackageNameLength. On trees that lack that helper, substitute Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, ...), as Google did for Android 14.",
       "Hunk line numbers differ between branches: about 14.4k/16.4k on 15, 14.4k/16.5k on 16, 14.5k/16.7k on 14 and 13.5k/15.9k on 17. Anchor on the three setters by their `if (!mHasFeature) return;` prologue followed by the isProfileOwner/isManagedProfile or canWriteCredentialManagerPolicy authorization."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 7,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-16.0.0_r7",
      "date": "2026-06-01",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "48c17df8bdc45e4175a33978791957d460388b72",
       "tag": "android-security-16.0.0_r7",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    }
   ],
   "tags": [
    "android-security-16.0.0_r7"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "477935679"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "9bef8bf1eee45a434e10c40e61acaeb2f90e10ad",
       "subject": "DO NOT MERGE: Enforce suspend restrictions in startOp and noteOp paths",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": [
        "cve_fix",
        "restrict_automerge"
       ]
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "481967442"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "80828212c3187adf56fc2a25d2d24001c5209937",
       "subject": "Skip serialization of huge vibration effects in NotificationChannel",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "463674813"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "1d2269c1014c2a0c1816ca0574b68f6e9ea158cb",
       "subject": "On face auth success, verify the face auth'd user is the current user",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "401188957"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "02fc7472ebb27fd394caafa8b87d4daead295d70",
       "subject": "Checking userId in com.android.server.pm.ComputerEngine#resolveContentProvider",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "393608784",
      "441090735"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "614c201252f9b2866a7c0457dff0d57889bc39e8",
       "subject": "Catch uncaught IllegalStateException",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-03-24",
       "markers": [
        "cve_fix"
       ]
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "471173239"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/KeyChain",
       "sha": "b8d3fe7c5e4b3b637c37c77fa31a026b3bf03818",
       "subject": "Sanitize app label in KeyChainActivity to prevent UI injection.",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "bulletin_history": [
      {
       "bulletin": "2026-06",
       "cve": "CVE-2026-0094",
       "severity": "High",
       "versions": [
        "14",
        "15",
        "16",
        "16-qpr2"
       ],
       "status": "removed",
       "removed_seen": "2026-10-13",
       "removed_in_version": "1.4"
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "368376056"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "ead2c0a04ff3869f7013f5111002bfd236eaa4b8",
       "subject": "Allow multi-user to control secure nfc",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "368376056",
      "489303684"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "cb36463814194fc58fdf3ddfaeaf9ed3d17a5ae7",
       "subject": "Load the Secure NFC setting when the current user is changed upon query",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "368376056",
      "475315815",
      "475447365"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "26d0a996cdf89863197855bbe5f17742411c98db",
       "subject": "NFC Security Vulnerability in Multi-User Mode",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "8e1d8fa6b90a42cc29ad182fb6b9b80b070f7d3d",
       "subject": "nfc: Modify Secure NFC restoration to use the corresponding userId",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "315320090",
      "418085016"
     ],
     "commits": [
      {
       "project": "platform/packages/modules/Permission",
       "sha": "cf13611fd964c56482906d67e1138e43f0e32701",
       "subject": "Revert^2 \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/modules/Permission",
       "sha": "cef3f2606bd7f678a3b9fe865ff414d1f5c596f5",
       "subject": "Revert \"Display the system gallery storage permission as system fixed\"",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      },
      {
       "project": "platform/packages/modules/Permission",
       "sha": "3badcd33b034606cd0a755803ad01edf560a673b",
       "subject": "Revert \"Ensure Gallery always shows as system fixed\"",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    },
    {
     "bug_ids": [
      "462214379"
     ],
     "commits": [
      {
       "project": "platform/packages/services/Telephony",
       "sha": "a34053ac6fcafb47925fa663326fc69729e379eb",
       "subject": "Disallow shell to change CarrierRestrictionRules",
       "tag": "android-security-16.0.0_r7",
       "merged": "2026-04-09",
       "markers": []
      }
     ],
     "tags": [
      "android-security-16.0.0_r7"
     ],
     "tag": "android-security-16.0.0_r7"
    }
   ],
   "coverage": {
    "n_ingested_tags": 9,
    "first_ingested_tag": "android-security-16.0.0_r1",
    "latest_ingested_tag": "android-security-16.0.0_r9",
    "window_base_tag": "android-16.0.0_r2",
    "window_start": "2025-06-10",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2025-64505": {
     "cve": "CVE-2025-64505",
     "summary": "In libpng's pngrtran.c png_set_quantize, the quantize_index array is now always allocated and identity-initialized to PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so png_do_quantize can no longer index past it with out-of-range palette indices; a companion commit frees any previous quantize_index before reallocating and turns quantize_sort into a local variable to stop leaks on repeated calls.",
     "component": "platform/external/libpng, read transformations (pngrtran.c, palette quantization)",
     "weakness": "Undersized heap allocation leading to out-of-bounds access in png_do_quantize (quantize_index sized by num_palette, indexed by pixel values); plus memory leak on repeated png_set_quantize calls",
     "root_cause": "png_set_quantize (pngrtran.c) allocated png_ptr->quantize_index with exactly num_palette bytes and only initialized indices 0..num_palette-1. Per the commit message, png_do_quantize indexes quantize_index with palette indices taken from image data, so a malformed PNG whose indices are >= num_palette accesses memory beyond the allocation (the commit message calls this a buffer overflow / undefined behavior). Separately, png_set_quantize assumed it would be called once per png_struct: a second call overwrote png_ptr->quantize_index (and png_ptr->quantize_sort in the histogram path) without freeing the earlier allocation, leaking memory.",
     "fix_analysis": [
      "Commit 3f148ea688b5 (first, 'Fix a memory leak in function png_set_quantize; refactor'), pngrtran.c:png_set_quantize: adds png_free(png_ptr, png_ptr->quantize_index) before the png_malloc in the full_quantize == 0 branch, so repeated calls release the old buffer. It moves quantize_sort from a png_struct member to a local png_bytep inside the histogram branch (allocated with png_malloc, used for the bubble sort and palette swap, released with png_free at the end of the branch), and hoists j to the branch scope. The pngstruct.h member removal was reverted in this backport to keep the ABI, so the struct field remains but this function no longer uses it.",
      "Commit 93147074acc1 (second, 'Fix a buffer overflow in png_do_quantize'), pngrtran.c:png_set_quantize: changes the quantize_index allocation from (png_alloc_size_t)num_palette to PNG_MAX_PALETTE_LENGTH, and the identity-initialization loop bound from num_palette to PNG_MAX_PALETTE_LENGTH. Indices >= num_palette now read an identity mapping (index N maps to palette entry N) instead of reading past the allocation. png_do_quantize itself is not modified; the commit explicitly chooses oversizing over a runtime bounds check in the pixel loop.",
      "The resulting code at android-security-16.0.0_r8 (lines 451-455) shows both changes: png_free followed by png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH) and a 256-iteration init loop."
     ],
     "completeness": [
      "The out-of-bounds fix covers only the quantize_index table built in the full_quantize == 0 branch. The png_do_quantize loop is left as-is and relies on the 256-byte allocation rather than a bounds check.",
      "quantize_sort (histogram branch) is still allocated with num_palette bytes. In the shown code it is indexed only by i and j, which are bounded by num_palette. Its values index the caller-supplied histogram and are compared against maximum_colors, so image-data indices do not affect it.",
      "The full_quantize != 0 path is not shown in the material, and these commits do not change it; whether it has similar indexing is unknown.",
      "The leak fix frees quantize_index on re-entry and makes quantize_sort local. The png_struct quantize_sort member kept for ABI reasons is unused by this function."
     ],
     "behavior_changes": [
      "quantize_index is always 256 bytes regardless of num_palette; out-of-range palette indices in image data now map to themselves instead of reading beyond the buffer.",
      "Calling png_set_quantize more than once on the same png_struct no longer leaks the prior quantize_index allocation; quantize_sort is a function-local temporary released before return.",
      "The diffs add no new error or warning path."
     ],
     "preconditions": [
      "The decoding application must enable palette quantization via png_set_quantize with full_quantize == 0 (this sets PNG_QUANTIZE and builds quantize_index).",
      "The PNG's pixel data must contain palette indices >= num_palette (malformed image) for the out-of-bounds access in png_do_quantize to occur.",
      "For the leak: png_set_quantize is called more than once on the same png_struct."
     ],
     "per_major": {
      "14": "adapted fix: commit 411d9be12a3e in android-security-14.0.0_r28 makes the same PNG_MAX_PALETTE_LENGTH change. The pre-fix allocation line differs ((png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))), base blob 831d98d08), and the diff context already contains the png_free line and the leak comment. 7/8 lines of the patched region are present at android-security-14.0.0_r29; the material does not explain the missing line.",
      "15": "same fix: commit 299ab85302d7 in android-security-15.0.0_r16, the same diff (3749ebfa6 -> 5fd5b7330) as Android 16; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commits 3f148ea688b5 (leak/refactor) then 93147074acc1 (256-byte allocation) in android-security-16.0.0_r7; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "adapted fix: commit dc721fee982c in android-17.0.0_r1 (base) makes the same change on the same pre-fix allocation line and base blob (831d98d08) as Android 14. 7/8 lines of the patched region are present at android-security-17.0.0_r1; the material does not explain the missing line."
     },
     "backport_notes": [
      "Apply the leak/refactor commit (3f148ea688b5) before the allocation-size commit (93147074acc1). The second diff starts from the first's output (blob 3749ebfa6), and its context includes the png_free line and the 'Be careful to avoid leaking memory' comment. Keep the pngstruct.h quantize_sort member in security branches to avoid an ABI change, as Google did.",
      "On trees whose allocation line reads (png_alloc_size_t)((png_uint_32)num_palette * (sizeof (png_byte))) (as in Android 14 and 17), the hunk needs a small context adaptation; the replacement is the same PNG_MAX_PALETTE_LENGTH constant and loop bound."
     ],
     "confidence": "high",
     "unknowns": [
      "The png_do_quantize code itself is not in the material; the exact indexing expression is known only from the commit message.",
      "Which Android system components call png_set_quantize with full_quantize == 0 is not shown.",
      "Whether the full_quantize != 0 path has similar sizing assumptions is not visible in the material.",
      "Why only 7/8 lines of the patched region are present on Android 14 and 17 is not shown.",
      "The bulletin lists the type as DoS with Critical severity; the material does not explain how the out-of-bounds access maps to that rating."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-64720": {
     "cve": "CVE-2025-64720",
     "summary": "In libpng's png_init_read_transformations, palette entries with partial tRNS alpha are now only premultiplied, not composited against the background, when PNG_FLAG_OPTIMIZE_ALPHA is set. This restores the premultiplied invariant component <= alpha that png_image_read_composite relies on for its png_sRGB_base[]/png_sRGB_delta[] lookups (per the commit message).",
     "component": "platform/external/libpng, read transformations (pngrtran.c) feeding png_image_read_composite",
     "weakness": "Incorrect calculation producing out-of-range values that are used as array indices (out-of-bounds access of png_sRGB_base[]/png_sRGB_delta[] in png_image_read_composite, per the commit message)",
     "root_cause": "In pngrtran.c:png_init_read_transformations, the palette/tRNS compositing loop (for entries with 0 < trans_alpha[i] < 0xff) always applied png_composite(w, v, trans_alpha[i], back_1.*) to the gamma-linearized component and stored gamma_from_1[w] into the palette, whatever the state of PNG_FLAG_OPTIMIZE_ALPHA. Per the commit message, when PNG_ALPHA_OPTIMIZED is active, png_image_read_composite expects palette entries to hold pure premultiplied RGB (component <= alpha) and does the real background compositing itself. Background-blended values break that invariant and exceed the valid range of the PNG_sRGB_FROM_LINEAR lookup tables png_sRGB_base[] and png_sRGB_delta[], causing out-of-bounds array access.",
     "fix_analysis": [
      "pngrtran.c:png_init_read_transformations: inside the `else /* if (png_ptr->trans_alpha[i] != 0xff) */` branch of the palette loop, the fix adds `if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0)`.",
      "When the flag is set: for red, green and blue, it takes component = gamma_to_1[palette[i].c], computes component = (component * trans_alpha[i] + 128) / 255 in a png_uint_32, and stores palette[i].c = gamma_from_1[component]. This is premultiplication only and back_1 is not used. The linear-domain intermediate is bounded by alpha. Note the commit message says +127 but the code uses +128.",
      "When the flag is not set: the original code path is kept unchanged (png_byte v, w; v = gamma_to_1[...]; png_composite(w, v, trans_alpha[i], back_1.c); palette[i].c = gamma_from_1[w]), so non-optimized background compositing behaves as before.",
      "The diff does not change png_image_read_composite or the png_sRGB_base[]/png_sRGB_delta[] indexing. The fix works on the producer side by restoring the invariant."
     ],
     "completeness": [
      "The fix covers only the partially transparent (0 < alpha < 0xff) palette entries in the gamma-corrected palette compositing path of png_init_read_transformations (the path that uses gamma_to_1/gamma_from_1 and back_1).",
      "The trans_alpha[i] == 0 case directly above (palette[i] = back) is untouched and still assigns the background colour when PNG_FLAG_OPTIMIZE_ALPHA is set. The material does not show how png_image_read_composite treats zero-alpha entries, so whether this matters cannot be determined.",
      "The hunk ends at a sibling `else` branch, presumably the non-gamma palette path. Its contents are not shown, so it is unknown whether it needed an equivalent flag-dependent change.",
      "The diff adds no clamp at the consumer (png_image_read_composite). Whether the consumer has any other protection is not shown."
     ],
     "behavior_changes": [
      "For palette images with partial tRNS alpha decoded with PNG_ALPHA_OPTIMIZED, palette entries now hold premultiplied RGB without background blending. The background is composited later in png_image_read_composite, so output for these images changes.",
      "No behaviour change when PNG_FLAG_OPTIMIZE_ALPHA is clear. The diff adds no new error codes and no API changes."
     ],
     "preconditions": [
      "The input PNG has palette entries with a tRNS alpha value strictly between 0 and 0xff (per the loop conditions in the context).",
      "The reader has PNG_FLAG_OPTIMIZE_ALPHA set (PNG_ALPHA_OPTIMIZED) and is on the gamma palette background compositing path in png_init_read_transformations.",
      "Per the commit message, the out-of-bounds access happens in png_image_read_composite. Which Android consumers reach it is not shown. Per index search, low-level libpng users exist (frameworks/base/libs/androidfw/Png.cpp readPng, bootable/recovery/minui/resources.cpp PngHandler), but their relevance to this path is not established."
     ],
     "per_major": {
      "14": "same fix: platform/external/libpng ba5981bebfba in android-security-14.0.0_r28, identical hunk (file index 03ffef132..38d1306f1, hunk at line 1701). At android-security-14.0.0_r29, 6/8 lines of the patched region are present.",
      "15": "same fix: platform/external/libpng 2a900c24d056 in android-security-15.0.0_r16, identical hunk (file index 5fd5b7330..6a5a401a0, hunk at line 1697). At android-security-15.0.0_r17, 6/8 lines of the patched region are present.",
      "16": "same fix: platform/external/libpng 2113a245b832 in android-security-16.0.0_r7, cherry-pick of 7aad9e82d08b (Change-Id I793102c9fa2cdd908241a22914167b740148992f). The code context at android-security-16.0.0_r8 shows the new flag branch. The resolver reports 6/8 lines of the patched region present.",
      "17": "same fix: platform/external/libpng 20c02c533b25 in android-17.0.0_r1 (base), hunk identical to Android 14 (03ffef132..38d1306f1). At android-security-17.0.0_r1, 6/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single hunk in pngrtran.c with no other files touched. It is identical across 14 through 17; the only difference is a 4-line offset between the 14/17 and 15/16 file versions. The resolver reports 6/8 lines of the patched region present in every latest tag, and the material does not explain why, so check the region by hand when porting."
     ],
     "confidence": "high",
     "unknowns": [
      "The code of png_image_read_composite and its exact indexing into png_sRGB_base[]/png_sRGB_delta[] are not in the material. The overflow mechanism is taken from the commit message.",
      "Whether the non-gamma sibling `else` path of the palette loop (not shown) has or needs an equivalent change.",
      "How zero-alpha palette entries (palette[i] = back, unchanged) are handled downstream under PNG_ALPHA_OPTIMIZED.",
      "Which Android components reach png_image_read_composite with PNG_ALPHA_OPTIMIZED, i.e. real-world reachability on Android.",
      "Why the resolver reports 6/8 lines of the patched region present in every latest tag.",
      "The upstream libpng commit for this fix is not identified in the material."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-65018": {
     "cve": "CVE-2025-65018",
     "summary": "libpng simplified read API: decoding a 16-bit PNG to an 8-bit output format could overflow the caller's PNG_IMAGE_SIZE buffer because png_combine_row writes at IHDR bit depth (interlaced images only, per the second commit). Google first rejected all 16/8 bit-depth mismatches in png_image_finish_read, then replaced that check with an intermediate local_row buffer path (do_local_scale / png_image_read_direct_scaled) that is used only for interlaced 16-to-8 conversion.",
     "component": "platform/external/libpng, pngread.c simplified read API (png_image_finish_read / png_image_read_direct)",
     "weakness": "Out-of-bounds write: output written at the source (IHDR) bit depth into a caller buffer sized for the transformed output format",
     "root_cause": "In png_image_read_direct (pngread.c), when the IHDR bit depth is 16 and the caller requests a non-linear (8-bit) output format, png_set_scale_16 is applied and rows are read directly into the caller's buffer, which is sized via PNG_IMAGE_SIZE(image) for 8-bit components. Per the commit messages, png_combine_row writes into the destination using the IHDR bit depth before the 16-to-8 transformation completes, so the writes go past the buffer. Commit 1's example: a 32x32 16-bit RGB image is 6144 bytes of data, while an 8-bit RGBA output buffer is 4096 bytes, an overflow of 2048 bytes. Commit 2 states that only interlaced PNGs are affected.",
     "fix_analysis": [
      "Commit 1 (960064fdb5f4, Change-Id I07c7e84188f067ec23c48e823b46164e20705cfb), pngread.c:png_image_finish_read: before building the png_image_read_control, reads image->opaque->png_ptr->bit_depth and the PNG_FORMAT_FLAG_LINEAR bit of image->format. It returns png_image_error(\"16-bit PNG must use 16-bit output format\") when bit_depth == 16 and linear output is not requested, and png_image_error(\"8-bit PNG must not use 16-bit output format\") when bit_depth < 16 and linear output is requested. This blocks the vulnerable combination, but it also blocks all 16-to-8 and 8-to-16 conversions, interlaced or not.",
      "Commit 2 (2cafeca8cbf2, Change-Id I87bfbf70c34c466cd7c38adc702f8c4dc6305290), pngread.c:png_image_finish_read: removes the two rejection checks added by commit 1.",
      "Commit 2, pngread.c:png_image_read_direct: adds int do_local_scale. In the 8-bit output branch, after png_set_scale_16(png_ptr), it sets do_local_scale = 1 when png_ptr->interlaced != 0. Non-interlaced 16-to-8 reads keep the existing direct fast path.",
      "Commit 2, pngread.c:png_image_read_direct: adds a new branch `else if (do_local_scale != 0)` before the final fast-path else. It allocates row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)) (per the code comment, sized for the transformed 8-bit output), stores it in display->local_row, runs png_image_read_direct_scaled via png_safe_execute, then clears display->local_row and frees the row with png_free.",
      "Commit 2, pngread.c: new static function png_image_read_direct_scaled. It sets passes from png_ptr->interlaced (1 for NONE, PNG_INTERLACE_ADAM7_PASSES for ADAM7, png_error otherwise). For each pass and each of image->height rows, it calls png_read_row(png_ptr, local_row, NULL), then memcpy(output_row, local_row, (size_t)row_bytes) with row_bytes = display->row_bytes, and advances output_row by row_bytes. libpng now writes into the libpng-owned local_row, and only display->row_bytes bytes per row are copied to the user buffer, so the row write no longer goes directly into the user buffer."
     ],
     "completeness": [
      "The final state (commit 1 + commit 2) adds special handling only for the png_image_read_direct path with a 16-bit IHDR, 8-bit non-linear output (png_set_scale_16) and interlaced input. That is the only case the commit messages identify as vulnerable.",
      "The non-interlaced 16-to-8 fast path at the end of png_image_read_direct is deliberately left unchanged. The commit says it is safe, but the material does not show the code that makes it so.",
      "The reverse mismatch (IHDR < 16 with PNG_FORMAT_FLAG_LINEAR output, png_set_expand_16) was rejected by commit 1 and is no longer rejected after commit 2. Commit 2 adds no special handling for it, on the basis that only interlaced 16-to-8 reads overflow.",
      "The colormap path (png_image_read_colormap / png_image_read_colormapped) and the existing do_local_compose / do_local_background paths are untouched. The material does not show whether they are relevant.",
      "The new local_row is sized from png_get_rowbytes(png_ptr, info_ptr). The material does not show png_combine_row or png_read_update_info, so it cannot confirm that this size covers the IHDR-depth write described in the commit message."
     ],
     "behavior_changes": [
      "With only commit 1 applied: png_image_finish_read fails with a png_image_error for any 16-bit PNG read with a non-linear output format, and for any PNG below 16 bits read with a linear (16-bit) output format, regardless of interlacing.",
      "With commit 2 applied: those errors are gone. Interlaced 16-bit PNGs read as 8-bit now take a slower path, with one extra row allocation per image and a memcpy per row. Non-interlaced reads are unchanged.",
      "Simplified-API output for interlaced 16-to-8 reads is now produced by png_image_read_direct_scaled instead of the direct path."
     ],
     "preconditions": [
      "A caller uses the libpng simplified read API (png_image_finish_read) with a non-colormap, non-linear (8-bit component) output format.",
      "The input PNG has IHDR bit depth 16 and, per commit 2, is interlaced.",
      "The caller's buffer is sized by PNG_IMAGE_SIZE(image) for the requested 8-bit format.",
      "Per index search, the frameworks/base/libs/androidfw/Png.cpp snippet shows a custom read callback (png_get_io_ptr / png_error), not simplified png_image API calls. The index surfaced no Android caller of the simplified API, but that does not prove there are none."
     ],
     "per_major": {
      "14": "Same fix: the rearchitect commit (5e46c0292d5c) landed in android-security-14.0.0_r28. Its diff removes the commit 1 rejection hunk, so commit 1 must have been present before it. Hunk offsets differ slightly (3260/3678 vs 3266/3684), but the content is identical. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "Partial per the material: only commit 1 (6a5cceb169b3, the bit-depth mismatch rejection in png_image_finish_read) is shown, in android-security-15.0.0_r16. 4/4 lines of that region are present at android-security-15.0.0_r17. The material does not show the rearchitect commit on Android 15, so 15 may carry the stricter rejection behaviour instead of the local_row path.",
      "16": "Same fix, both commits: 960064fdb5f4 (rejection) and 2cafeca8cbf2 (rearchitect) in android-security-16.0.0_r7. Code at android-security-16.0.0_r8 shows png_image_read_direct_scaled present and the rejection removed from png_image_finish_read.",
      "17": "Same fix: rearchitect commit 0e9750be87cb in android-17.0.0_r1 (base), identical in content to Android 14's. 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply in order: I07c7e84188f067ec23c48e823b46164e20705cfb (rejection), then I87bfbf70c34c466cd7c38adc702f8c4dc6305290 (rearchitect). The second commit's png_image_finish_read hunk only applies if the first commit is present; otherwise drop that hunk. The second commit relies on existing png_image_read_control fields (local_row, first_row, row_bytes) and on the png_safe_execute / do_local_compose pattern in png_image_read_direct.",
      "A branch that has only the first commit (as the material suggests for Android 15) is protected but rejects all 16/8 depth conversions. Check whether the rearchitect commit is wanted there."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether the rearchitect commit landed on Android 15 (the material only shows commit 1 there).",
      "png_combine_row and png_read_update_info internals are not shown. The material therefore cannot confirm how the row ends up written at IHDR depth, why non-interlaced images are unaffected, or whether png_get_rowbytes(png_ptr, info_ptr) sizes local_row adequately.",
      "Whether interlace handling is enabled in the new do_local_scale path, and so whether png_image_read_direct_scaled (which overwrites each full output row from a reused local_row on every pass) produces correct image content for interlaced images. This is a correctness question the material does not answer.",
      "Which Android components, if any, call the libpng simplified API with 8-bit output on untrusted PNGs."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0039": {
     "cve": "CVE-2026-0039",
     "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow detections throw a C++ std::runtime_error through a custom in-library runtime, instead of going through the ubsan-minimal runtime and its hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image decoding library used by Android image decoding)",
     "weakness": "Integer overflow in DNG processing that the ubsan-minimal runtime turned into a process abort (crash-based denial of service)",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined for unsigned-integer-overflow and signed-integer-overflow. Through Soong this used the ubsan-minimal runtime, which ends in a hard abort when it detects an overflow (commit message: 'a much, much better solution for ubsan errors than a hard abort'). This happened even though the library is built with rtti: true and -fexceptions. The commit lists seven bug IDs. This commit does not change the overflowing arithmetic. What it changes is the fatal failure mode.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: sets sanitize.never: true. Per the diff comment, this disables the Soong-managed ubsan_minimal runtime so the library can supply its own.",
      "Android.bp: cflags add -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which re-enables the same two checks directly through the compiler, and -fno-sanitize-link-runtime, so no sanitizer runtime is linked and the handlers come from the library itself.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp is new and added to srcs. It defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message. libdng_sdk-defaults has rtti: true and cppflags -fexceptions (context lines 141-142), so the exception can propagate to the caller instead of aborting.",
      "OWNERS: adds Bug component 1075130 and includes the frameworks/base graphics OWNERS. Not security relevant.",
      "Test referenced: atest ImageDecoderTest#testInterestingDngs."
     ],
     "completeness": [
      "Defines handlers for five integer-overflow kinds: add, sub, mul, negate, divrem. No handlers for other UBSan checks are defined, and the new cflags enable only signed-integer-overflow and unsigned-integer-overflow.",
      "The handlers take no parameters, so the overflow diagnostic data is not used. The only information left is the exception message.",
      "The overflowing arithmetic in dng_sdk is not corrected. Each detected overflow becomes an exception. The material does not show whether every libdng_sdk caller catches std::runtime_error / std::exception or uses catch(...). If a caller does not, an uncaught exception would still terminate the process.",
      "sanitize.never: true applies to the whole libdng_sdk module. Whether it also turns off other sanitizers that would otherwise apply to this module is not shown in the material.",
      "The host_linux target still links libcompiler_rt-extras (Android.bp lines 194-196). The throwing runtime is in the top-level srcs, so it is compiled into the host build as well."
     ],
     "behavior_changes": [
      "A detected signed or unsigned integer overflow inside libdng_sdk now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting the process.",
      "If the caller catches the exception, decoding an affected file fails with an error instead of a crash. If it does not, the process still terminates.",
      "libdng_sdk is built with sanitize.never: true. The two overflow checks are re-enabled only through raw cflags."
     ],
     "preconditions": [
      "A DNG image is decoded by libdng_sdk in the target process. The referenced test (ImageDecoderTest#testInterestingDngs) points to the android.graphics.ImageDecoder path.",
      "The file content must make one of the overflow-checked operations in dng_sdk overflow. The material does not show the specific sites."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk (base index 1978b39) and ubsan_throwing_runtime.cpp are identical. The OWNERS hunk differs (it removes adaubert/zalman, and the trailing newline differs), which is not security relevant.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the runtime file are identical. The OWNERS hunk differs trivially.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, cherry-picked from three internal commits. The code context at android-security-16.0.0_r8 shows the sanitize/cflags/srcs block and the runtime file in place. The per-major check reports 3/5 lines of the patched region present.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (resolved via base), tagged RESTRICT AUTOMERGE. Android.bp and the runtime file content are identical."
     },
     "backport_notes": [
      "The change is build configuration plus one new source file. All four branches patch the same Android.bp base (index 1978b39), replacing cflags: [\"-DqDNGValidate=0\"] in the libdng_sdk cc_library. The change relies on rtti: true and -fexceptions coming from libdng_sdk-defaults.",
      "Check on the target branch that libdng_sdk consumers wrap decoding in a catch that handles std::exception, or in catch(...). The new runtime throws std::runtime_error."
     ],
     "confidence": "high",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow for the seven referenced bugs, and what DNG structures trigger them.",
      "Whether every in-tree caller of libdng_sdk catches std::runtime_error, so the exception does not itself end the process.",
      "Whether the five handlers cover every handler symbol the compiler emits for these flags, for example recover vs. _abort variants.",
      "Whether sanitize.never: true also disables other sanitizers that would otherwise apply to libdng_sdk.",
      "Why the per-major check reports only 3/5 (or 3/4) lines of the patched region present, when the 16.0.0_r8 context shows the full block.",
      "Which processes host libdng_sdk decoding, and why the bulletin rates this DoS Critical."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0040": {
     "cve": "CVE-2026-0040",
     "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that UBSan integer-overflow checks call a library-local runtime that throws std::runtime_error instead of the ubsan_minimal runtime that aborts the process.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and UBSan runtime replacement",
     "weakness": "Integer overflow (signed and unsigned) in dng_sdk code, which the sanitizer runtime turned into a hard process abort (DoS). The material does not show the individual overflow sites.",
     "root_cause": "libdng_sdk inherits sanitize.misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] from libdng_sdk-defaults in Android.bp. With that configuration Soong links the ubsan_minimal runtime, whose handlers abort the process when a check fires (general platform knowledge). The commit message says the hard abort is the problem being fixed and lists seven bug IDs. This indicates that integer overflows in dng_sdk code, reached while processing DNG input, were turning into process crashes. The library is already built with rtti: true and cppflags -fexceptions, so an exception-based failure path was available but unused.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true }. This turns off Soong's sanitizer handling for this module, and with it the ubsan_minimal runtime link, overriding the misc_undefined list inherited from libdng_sdk-defaults.",
      "Android.bp, cc_library libdng_sdk: re-enables the same checks by hand via cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow, and adds -fno-sanitize-link-runtime so that clang does not pull in any ubsan runtime. The existing -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short message. An instrumented overflow site in dng_sdk therefore raises a C++ exception that unwinds to the nearest catch instead of aborting.",
      "OWNERS: metadata only (bug component and owners); no functional effect.",
      "Test cited: atest ImageDecoderTest#testInterestingDngs."
     ],
     "completeness": [
      "The fix does not correct any of the underlying integer overflows in dng_sdk. It only changes what happens when one is detected: an exception instead of an abort. Every overflow that used to abort now throws.",
      "Handlers are provided only for five overflow kinds: add, sub, mul, negate and divrem. No other UBSan checks are enabled for libdng_sdk in this configuration. If more -fsanitize checks were added later without matching handlers, the build would fail to link.",
      "The handlers take no parameters and ignore the diagnostic data clang passes. The exception message names only the overflow kind, not where it happened.",
      "The exception only helps if callers of the instrumented code catch std::runtime_error (or std::exception). The material does not show the catch sites. An uncaught exception would still terminate the process.",
      "The change applies to the libdng_sdk cc_library on both device and host (host_supported: true). The target.host_linux block still links libcompiler_rt-extras and is unchanged."
     ],
     "behavior_changes": [
      "An integer overflow detected in libdng_sdk now raises std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the ubsan_minimal runtime. The sanitizer instrumentation is still compiled in via explicit cflags.",
      "Sanitizer diagnostics (location, operand values) are no longer reported for these checks, because the replacement handlers ignore their arguments."
     ],
     "preconditions": [
      "A DNG image processed by libdng_sdk whose contents push an arithmetic operation in dng_sdk into signed or unsigned integer overflow.",
      "The cited test (ImageDecoderTest#testInterestingDngs) suggests the path can be reached through DNG decoding via the platform image decoder. The material does not show the exact entry points.",
      "Per index search, the matches outside dng_sdk are DNG writer code (android_hardware_camera2_DngCreator.cpp, img_utils DngUtils.cpp). The material does not show that these use libdng_sdk, so they do not establish further callers."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk is identical (same blobs 1978b39..3d95a7b) and ubsan_throwing_runtime.cpp is identical. The OWNERS hunk differs: different prior owner list and result blob 1362108 instead of b9315f6. It is metadata only.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the runtime file are identical; OWNERS differs only in the prior owner list (metadata).",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. The code context at android-security-16.0.0_r8 shows the sanitize.never block, the cflags and the srcs entry in place.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1, subject marked RESTRICT AUTOMERGE. Android.bp and the runtime file are identical."
     },
     "backport_notes": [
      "Single commit. The Android.bp hunk is identical on all four branches (base blob 1978b39), so it should apply as-is to any tree whose libdng_sdk block still reads cflags: [\"-DqDNGValidate=0\"]. It relies on libdng_sdk-defaults already setting rtti: true and cppflags -fexceptions (present in the context). It also relies on Soong honoring sanitize.never together with manual -fsanitize cflags, as the comment in Android.bp notes.",
      "The OWNERS hunk is metadata and can be dropped if it conflicts."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific dng_sdk arithmetic sites overflowed (the material does not describe the seven bug IDs).",
      "Which process hosts the decode, and why the bulletin rates the DoS Critical.",
      "Where the thrown std::runtime_error is caught in the DNG decode path; the material does not show the catch sites.",
      "The exact caller chain from platform APIs into libdng_sdk (only the ImageDecoderTest name hints at it)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0041": {
     "cve": "CVE-2026-0041",
     "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a runtime inside the library that turns integer-overflow sanitizer hits into thrown C++ exceptions (std::runtime_error) instead of a hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and new ubsan-replacement/ubsan_throwing_runtime.cpp",
     "weakness": "Integer overflow in DNG parsing arithmetic, surfaced as a sanitizer-induced process abort (denial of service)",
     "root_cause": "libdng_sdk is built with the signed-integer-overflow and unsigned-integer-overflow sanitizers through the misc_undefined list in libdng_sdk-defaults (Android.bp lines 144-149). Per the commit message, this used the ubsan-minimal runtime, which hard-aborts on a sanitizer error. An arithmetic overflow reached while decoding DNG data therefore aborted the process that loaded libdng_sdk. The commit references seven bug IDs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942). It does not change any overflowing computation; it only changes what happens when an overflow is detected.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds `sanitize: { never: true }` to disable the Soong-provided ubsan_minimal runtime. It then re-enables the same two checks through cflags (`-fsanitize=signed-integer-overflow,unsigned-integer-overflow`) and adds `-fno-sanitize-link-runtime` so that no sanitizer runtime is linked.",
      "Android.bp :: cc_library libdng_sdk: adds `ubsan-replacement/ubsan_throwing_runtime.cpp` to srcs, so the sanitizer handler symbols are provided from inside the library.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines five hidden-visibility extern \"C\" handlers (__ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow, __ubsan_handle_divrem_overflow). Each one throws std::runtime_error instead of aborting. libdng_sdk-defaults already sets `rtti: true` and `cppflags: [\"-fexceptions\"]` (lines 141-142), so exceptions are enabled and the exception can propagate.",
      "OWNERS: metadata only (bug component and owner list), no functional effect."
     ],
     "completeness": [
      "The fix mitigates the failure mode but does not correct any individual overflowing computation. No dng_sdk parsing code is modified, so the overflows still occur and are now raised as exceptions.",
      "Only the libdng_sdk cc_library module is changed. libdng_sdk-defaults still lists the two overflow sanitizers in misc_undefined, so any other module using those defaults without the same override would keep the previous runtime. The material does not show whether such modules exist.",
      "The replacement runtime defines five handlers (add, sub, mul, negate, divrem) for the two enabled overflow sanitizers.",
      "The fix only helps if the thrown std::runtime_error is caught somewhere above the overflow site. The material does not show catch sites in dng_sdk or in its callers."
     ],
     "behavior_changes": [
      "On an integer overflow inside libdng_sdk, a std::runtime_error with the message \"ubsan: <kind>-overflow\" is thrown instead of the process hard-aborting.",
      "libdng_sdk no longer links the Soong-provided ubsan minimal runtime. The sanitizer handler symbols have hidden visibility and stay internal to the library."
     ],
     "preconditions": [
      "A process that loads libdng_sdk decodes crafted DNG data. The commit's test (atest ImageDecoderTest#testInterestingDngs) suggests the ImageDecoder DNG path reaches this library.",
      "The input must drive dng_sdk arithmetic into a signed or unsigned integer overflow that the sanitizer instruments."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp hunks are identical to the Android 16 commit; only the OWNERS hunk differs (the old list had zalman@google.com).",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the runtime file are identical; the OWNERS hunk is adapted to a one-line prior file.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (Cherrypick-From lists three internal commits). The code context at android-security-16.0.0_r8 shows the change present.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (resolved in base), titled with RESTRICT AUTOMERGE. Android.bp and the runtime file are identical to the other branches."
     },
     "backport_notes": [
      "The Android.bp hunk is identical across 14, 15, 16 and 17 (same blob ids 1978b39..3d95a7b), so it should apply cleanly on branches with the same libdng_sdk module. The OWNERS hunk differs per branch and has no functional effect.",
      "The fix relies on libdng_sdk-defaults already enabling exceptions (`cppflags: [\"-fexceptions\"]`, present in the shown context). Per the Android.bp comment, `sanitize: { never: true }` is combined with raw `-fsanitize=` cflags so that the replacement handlers are used instead of ubsan_minimal."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which specific dng_sdk functions overflow for the seven referenced bugs, and whether those bugs are distinct sites; the commit does not touch or name them.",
      "Where the thrown std::runtime_error is caught (inside dng_sdk or in the ImageDecoder/JNI caller), whether it reliably prevents process termination, and how the failure is reported to the app.",
      "Whether any other Soong module consumes libdng_sdk-defaults and still links the aborting runtime.",
      "The per-major resolver reports 3/5 (3/4 on 15) patched-region lines present at the latest tags, while the shown Android 16 context contains the complete change; the material does not explain the partial count."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0042": {
     "cve": "CVE-2026-0042",
     "summary": "Rebuilds libdng_sdk so that UBSan signed/unsigned integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime) instead of aborting the process through the Soong-linked ubsan minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG SDK arithmetic surfaced as a sanitizer abort (process crash / DoS); the fix changes the overflow handling strategy, not the individual overflow sites",
     "root_cause": "libdng_sdk-defaults (Android.bp lines 144-149) enables sanitize.misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] and libdng_sdk inherited it with only cflags: [\"-DqDNGValidate=0\"]. With Soong-managed sanitizers the module is linked against the ubsan minimal runtime, whose handlers abort on any detected overflow (general platform knowledge); the commit message describes this as a 'hard abort'. Any arithmetic overflow reached during decoding (the commit references seven bugs: 470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942) therefore terminated the hosting process rather than being reported as an error, even though the library is built with rtti: true and cppflags: [\"-fexceptions\"] and so could propagate an error as an exception.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } so Soong does not add its own sanitizer flags or link the ubsan minimal runtime for this module (the comment in the diff says this is a workaround because Soong has no flag that only drops the runtime).",
      "Android.bp, cc_library libdng_sdk: the cflags now set the sanitizer checks by hand. -fsanitize=signed-integer-overflow,unsigned-integer-overflow turns back on the same two checks the defaults requested, and -fno-sanitize-link-runtime stops the compiler driver from linking a UBSan runtime.",
      "Android.bp, cc_library libdng_sdk: adds srcs ubsan-replacement/ubsan_throwing_runtime.cpp so the module itself provides the handler symbols the instrumented code calls.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a message naming the check (e.g. \"ubsan: add-overflow\"). Hidden visibility means these symbols are not exported from the library (general platform knowledge).",
      "OWNERS: adds Bug component 1075130, includes the frameworks/base graphics OWNERS and marks the previous owners as LAST_RESORT_SUGGESTION; no functional effect.",
      "The -DqDNGValidate=0 cflag is kept unchanged. The libdng_sdk-defaults block (misc_undefined list, rtti, -fexceptions) is untouched."
     ],
     "completeness": [
      "Covers only the five integer-overflow handler entry points (add, sub, mul, negate, divrem). These match the two checks enabled by -fsanitize=signed-integer-overflow,unsigned-integer-overflow, and the shown cflags enable no other UBSan checks for this module.",
      "Only the non-_abort handler names are provided. If the build ever emitted the *_abort variants (e.g. via -fno-sanitize-recover), those symbols would be missing at link time (general platform knowledge). The material does not show such flags for libdng_sdk.",
      "The fix does not correct any of the underlying arithmetic overflows in the DNG SDK sources: the material has no changes to dng_sdk source files. Every overflow site is still there and now throws an exception instead of aborting.",
      "Only the libdng_sdk module gets sanitize.never. libdng_sdk-defaults still carries misc_undefined, so any other module built from those defaults (none shown in the material) would still get the aborting minimal runtime.",
      "The material does not show whether every caller of libdng_sdk catches std::runtime_error (and not only dng_exception). An uncaught exception would still terminate the process."
     ],
     "behavior_changes": [
      "On a detected signed or unsigned integer overflow inside libdng_sdk, a std::runtime_error is thrown instead of the process aborting; callers that catch exceptions see a decode failure.",
      "sanitize.never: true removes all Soong-managed sanitizer configuration from libdng_sdk (not only the ubsan runtime), including anything a global sanitizer build variant would otherwise add to this module (general platform knowledge).",
      "The ubsan handlers now live in libdng_sdk itself (hidden visibility) instead of coming from a linked runtime. Because the cflags and srcs are not target-scoped and the module is host_supported, this applies to both device and host builds."
     ],
     "preconditions": [
      "A component decodes untrusted DNG image data through libdng_sdk. The commit's test (atest ImageDecoderTest#testInterestingDngs) suggests the android.graphics.ImageDecoder DNG path is the exercised entry point.",
      "The decoded data must drive the SDK into a signed or unsigned integer overflow in an instrumented arithmetic operation. The specific overflow sites are only referenced by bug numbers and are not shown."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk (same blob 1978b39 -> 3d95a7b) and the ubsan_throwing_runtime.cpp file are identical; only the OWNERS base differed (adaubert, zalman). The per-major facts report 3/5 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16, with the same Android.bp hunk and runtime file; the OWNERS base differed (adaubert only). The per-major facts report 3/4 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (the analysed commit). The code context at android-security-16.0.0_r8 shows the patched Android.bp block and ubsan_throwing_runtime.cpp, while the per-major facts report 3/5 lines of the patched region present there.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (resolved via base), titled with RESTRICT AUTOMERGE, with the same Android.bp hunk and runtime file. The per-major facts report 3/5 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The change is build configuration plus one new file, applied as an identical patch on all four branches (Android.bp blob 1978b39 on every branch). It needs libdng_sdk-defaults to already carry rtti: true and cppflags: [\"-fexceptions\"] (present in the shown context), and a Soong/clang toolchain that honors sanitize.never and -fno-sanitize-link-runtime (general platform knowledge). OWNERS conflicts can be resolved freely because that file has no functional effect.",
      "No 'Fix in fork' section was provided, so no per-file fork verdict is available."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete DNG SDK source locations where the integer overflows occur (seven bug IDs are referenced but no source diffs are shown).",
      "Whether the ImageDecoder callers of libdng_sdk catch std::runtime_error, so that the exception becomes a decode error instead of terminating the process.",
      "Which process hosts the decode in the reported scenario and therefore what exactly was denied service.",
      "Why the bulletin rates a DoS as Critical; the material gives no further detail.",
      "Why the per-major facts report only 3/5 (3/4 on 15) lines of the patched region present in the latest tags, when the r8 code context shows the full patched block."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0043": {
     "cve": "CVE-2026-0043",
     "summary": "Rebuilds libdng_sdk so that integer-overflow sanitizer hits (signed and unsigned add/sub/mul/negate/divrem) go to a custom in-library runtime that throws std::runtime_error, replacing the ubsan-minimal runtime and its hard abort.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and new ubsan-replacement runtime; DNG image decoding",
     "weakness": "integer overflow in DNG processing arithmetic (CWE-190 class); the diff is a build-level mitigation and does not show the specific overflowing expression",
     "root_cause": "libdng_sdk inherited `sanitize: { misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] }` from libdng_sdk-defaults (Android.bp lines 144-149 in context). The cc_library itself only added `cflags: [\"-DqDNGValidate=0\"]`. Under Soong these checks are backed by the ubsan-minimal runtime, which handles a detected error with a hard abort. The commit message says this abort is what is being replaced, because C++ exceptions are enabled in the library (rtti: true and cppflags -fexceptions are set in the defaults) and are a better response to ubsan errors. The diff does not identify the specific arithmetic site(s). Seven bug IDs are referenced, which suggests several findings were handled by this one mechanism.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds `sanitize: { never: true }`. Per the in-diff comment, this disables the Soong-managed ubsan_minimal runtime for this module. The misc_undefined list in libdng_sdk-defaults is left as is.",
      "Android.bp :: cc_library libdng_sdk: re-enables the same two checks by hand with `cflags: [\"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\", \"-fno-sanitize-link-runtime\"]`. Integer arithmetic is still instrumented, but no ubsan runtime is linked. `-DqDNGValidate=0` is kept.",
      "Android.bp :: cc_library libdng_sdk: adds `srcs: [\"ubsan-replacement/ubsan_throwing_runtime.cpp\"]`, so the module provides its own handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines extern \"C\" hidden-visibility `__ubsan_handle_add_overflow`, `__ubsan_handle_sub_overflow`, `__ubsan_handle_mul_overflow`, `__ubsan_handle_negate_overflow` and `__ubsan_handle_divrem_overflow`. Each one does `throw std::runtime_error(\"ubsan: <kind>\")`.",
      "Net effect: a detected signed or unsigned overflow in instrumented libdng_sdk code now unwinds as a C++ exception instead of aborting. The handler never returns, so execution does not continue with the overflowed value.",
      "OWNERS: metadata change only (bug component 1075130, include of the frameworks/base graphics OWNERS file). No functional effect."
     ],
     "completeness": [
      "Applies to all instrumented integer add/sub/mul/negate/div-rem in code compiled into the libdng_sdk module, because the checks are compiler instrumentation rather than per-site fixes. No DNG source file is changed and no specific overflow is patched.",
      "Only the five overflow handlers are defined, matching the two -fsanitize groups enabled. Other UBSan check classes are not enabled.",
      "The handlers take no parameters, so the ubsan data/operand arguments are ignored and no diagnostic detail is recorded.",
      "The exception type is std::runtime_error, not dng_exception. The material does not show whether callers catch std::exception or use catch(...). An exception that escapes to a frame with no handler causes std::terminate, which aborts the process just as before (general platform knowledge). The commit's Test: line names ImageDecoderTest#testInterestingDngs, but the material does not show its results.",
      "Applies only to the `libdng_sdk` cc_library target. libdng_sdk-defaults still lists misc_undefined. The material shows no other module that uses those defaults.",
      "host_linux still links `libcompiler_rt-extras`. The material does not show how the host variant interacts with -fno-sanitize-link-runtime."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error with the message \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\", instead of a hard abort via ubsan-minimal.",
      "DNG files that hit such an overflow can now fail to decode instead of aborting the process, as long as the caller catches the exception.",
      "libdng_sdk no longer links the Soong-provided ubsan runtime (`sanitize: never`, `-fno-sanitize-link-runtime`)."
     ],
     "preconditions": [
      "A DNG image whose processing in libdng_sdk performs an arithmetic operation that overflows a signed or unsigned integer. The material does not show which field(s).",
      "The image must reach libdng_sdk. The test name ImageDecoderTest#testInterestingDngs points to the android.graphics.ImageDecoder path as one entry point. The index results (DngCreator.cpp, img_utils DngUtils.cpp) concern DNG writing and are not shown to call libdng_sdk (per index search)."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp and ubsan_throwing_runtime.cpp hunks are identical; only the OWNERS pre-image differs (adaubert/zalman).",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and the runtime file are identical; the OWNERS pre-image differs (adaubert only).",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. The commit lists three Cherrypick-From references. The code context at android-security-16.0.0_r8 shows the full patched Android.bp block and runtime file, while the resolver reports 3/5 lines of the patched region present.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1 (resolved via base), subject marked RESTRICT AUTOMERGE. Android.bp and the runtime file are identical; the OWNERS pre-image differs (adaubert/zalman)."
     },
     "backport_notes": [
      "Single build-only commit. It relies on libdng_sdk-defaults already providing `rtti: true` and `cppflags: [\"-fexceptions\"]` (present in context). On trees with a customized libdng_sdk sanitize configuration, check that the module no longer links the ubsan runtime. Callers should handle C++ exceptions around dng_sdk entry points, otherwise an uncaught exception terminates the process.",
      "For all majors the resolver reports a partial match (3/4 or 3/5 lines), while the android-security-16.0.0_r8 code context shows the full patched region. Check the region directly instead of relying on that metric."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic expression(s) in dng_sdk overflow, and what consequence led to the Critical EoP rating. The diff only changes the sanitizer response.",
      "Which callers (ImageDecoder/Skia DNG codec or others) catch std::runtime_error from libdng_sdk. The material shows no caller-side changes.",
      "Whether sanitizer instrumentation was actually active on device for this sdk_version: \"current\" module before the change, which would decide whether the pre-fix behaviour was an abort or silent wrap-around.",
      "What the three Cherrypick-From references correspond to, and why the resolver reports only partial matches of the patched region."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0044": {
     "cve": "CVE-2026-0044",
     "summary": "The fix rebuilds libdng_sdk (platform/external/dng_sdk) so that integer-overflow sanitizer hits no longer go to the aborting ubsan-minimal runtime. They now go to a replacement runtime local to the library that throws std::runtime_error, so a hard abort on overflowing DNG data becomes a C++ exception.",
     "component": "platform/external/dng_sdk: libdng_sdk build configuration (Android.bp) and a new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG SDK arithmetic surfaced as a sanitizer abort (uncontrolled process termination, DoS)",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] (Android.bp lines 144-149 in the code context). Per the commit, this used the ubsan minimal runtime, which hard-aborts the process when an instrumented operation overflows. The commit references seven bug IDs; what they describe is not shown, though they may be several overflow sites hit by malformed input. The commit does not change any of the arithmetic itself. The defect it addresses is that detected overflows were fatal rather than recoverable, even though the library is built with -fexceptions and rtti (defaults, lines 141-142).",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } to disable the Soong-managed ubsan_minimal runtime for this module. It then re-adds the checks by hand through the cflags \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" and \"-fno-sanitize-link-runtime\", so no compiler-rt ubsan runtime is linked. The comment in the diff says this is a workaround because Soong has no flag that only drops the minimal runtime.",
      "Android.bp, cc_library libdng_sdk: adds srcs \"ubsan-replacement/ubsan_throwing_runtime.cpp\" so the handler symbols are provided from inside libdng_sdk.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new file): defines five handlers with extern \"C\" linkage and hidden visibility: __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short \"ubsan: ...\" message instead of aborting. The handlers are declared with no parameters.",
      "OWNERS: ownership and bug-component metadata only, no functional effect.",
      "The -DqDNGValidate=0 cflag is kept. libdng_sdk-defaults (misc_undefined, -fexceptions, rtti) is left unchanged."
     ],
     "completeness": [
      "Covers every instrumented signed and unsigned overflow site in code compiled into the libdng_sdk cc_library, because the change is made at module level rather than per overflow site. No individual arithmetic bug is fixed: the overflows still happen and are now reported by an exception.",
      "Only the cc_library named libdng_sdk is changed. libdng_sdk-defaults still carries sanitize.misc_undefined, so any other module built from those defaults would still use the aborting minimal runtime. The material does not show whether such a module exists.",
      "Recovery through the exception only works if the overflow happens in a frame from which a C++ exception can propagate to a handler. An overflow in a noexcept function or a destructor would still end the process through std::terminate. The material does not show where std::runtime_error is caught in dng_sdk or its callers.",
      "Only overflow handlers are provided. If any other ubsan check were enabled for this module, its handler symbol would not be supplied, because -fno-sanitize-link-runtime is set.",
      "sanitize.never: true disables all Soong-managed sanitizers for libdng_sdk, not just the minimal runtime (general platform knowledge)."
     ],
     "behavior_changes": [
      "On a detected integer overflow in libdng_sdk code, a std::runtime_error is thrown (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of the hard abort from the ubsan minimal runtime.",
      "Where the caller catches the exception, decoding of the offending DNG fails instead of crashing the process. Where it is not caught, the process still terminates.",
      "The same overflow checks (signed and unsigned) stay enabled, so the set of inputs flagged does not change compared with the earlier sanitized build. Only the reaction changes.",
      "libdng_sdk no longer links the compiler-rt ubsan minimal runtime. The replacement handler symbols have hidden visibility inside the library.",
      "Only the build configuration changes and one new source file (the handler runtime) is added. No existing DNG SDK source file is modified."
     ],
     "preconditions": [
      "A process that links libdng_sdk must parse attacker-controlled DNG data that reaches one of the instrumented arithmetic sites with overflowing values. The referenced test (ImageDecoderTest#testInterestingDngs) and the OWNERS include of frameworks/base graphics point to the image decoding path as the consumer being exercised.",
      "The index search returned frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp (DngCreator_setup) and frameworks/av/media/img_utils/src/DngUtils.cpp. These are DNG writing paths, and the material does not show that they link libdng_sdk, so they are not confirmed consumers.",
      "The material does not show which process hosts the decode (an app process or a system component)."
     ],
     "per_major": {
      "14": "Same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. Android.bp and ubsan_throwing_runtime.cpp are identical; only the OWNERS pre-image differs (adaubert/zalman).",
      "15": "Same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. Android.bp and ubsan_throwing_runtime.cpp are identical; the OWNERS pre-image differs (adaubert only).",
      "16": "Same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, a cherry-pick of three internal commits (Change-Id I7b8414e4b5c2f2ca44f572fb72e625d1907f7902). The code context at android-security-16.0.0_r8 shows the Android.bp and runtime-file changes present.",
      "17": "Same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1, with RESTRICT AUTOMERGE in the subject. Android.bp and the runtime file are identical; the OWNERS pre-image is the same as on 14."
     },
     "backport_notes": [
      "The Android.bp hunk is identical on all four branches (same pre-image index, 1978b39 -> 3d95a7b), so it should apply cleanly wherever libdng_sdk still has cflags: [\"-DqDNGValidate=0\"]. The fix relies on libdng_sdk-defaults enabling C++ exceptions (-fexceptions). On a fork with a different sanitizer setup, check that the resulting handler names are the non-minimal __ubsan_handle_*_overflow symbols (without the _minimal suffix) (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow. The seven referenced bugs are not described in the material.",
      "Where std::runtime_error is caught on the decode path (in dng_sdk, in the ImageDecoder consumer, or nowhere on some paths), and therefore whether every input that used to crash now fails gracefully.",
      "Whether the five handlers defined cover every handler the compiler emits for the two enabled check groups.",
      "Whether any other Soong module uses libdng_sdk-defaults and therefore still uses the aborting minimal runtime.",
      "Which processes load libdng_sdk, and so the actual impact behind the Critical/DoS rating.",
      "Why the per-major resolver reports only 3/5 patched lines present at the latest tags (3/4 on 15) while the Android 16 code context shows the change present."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0051": {
     "cve": "CVE-2026-0051",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections raise a C++ std::runtime_error (via a custom in-library ubsan runtime) instead of a hard abort through the ubsan_minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and new ubsan-replacement runtime; DNG image decoding",
     "weakness": "Integer overflow in DNG processing converted into a process abort by the sanitizer runtime (reachable abort / DoS)",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined for signed-integer-overflow and unsigned-integer-overflow. Those checks were serviced by the ubsan_minimal runtime, which per the commit message results in a hard abort on detection. Any arithmetic overflow reached while dng_sdk processed DNG data therefore terminated the process hosting the decoder. The commit message references seven bugs; the specific overflow sites are not shown in the material.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so the ubsan_minimal runtime is not used for this module (the comment notes Soong has no specific flag to disable that runtime, and that 'never' combined with -fsanitize= cflags works for this).",
      "Android.bp, cc_library libdng_sdk: re-enables the checks at the compiler level by adding -fsanitize=signed-integer-overflow,unsigned-integer-overflow to cflags and adds -fno-sanitize-link-runtime so no sanitizer runtime is linked; -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow, each throwing std::runtime_error with a 'ubsan: <kind>-overflow' message. libdng_sdk-defaults already builds with rtti: true and cppflags -fexceptions, and the commit message states C++ exceptions are enabled in this library.",
      "OWNERS: metadata only (bug component and owners), no functional effect."
     ],
     "completeness": [
      "The fix changes the failure mode only: detected integer overflows now throw instead of aborting. This commit does not change the overflowing arithmetic in dng_sdk itself.",
      "Only five handlers are provided (add, sub, mul, negate, divrem overflow), corresponding to the integer-overflow checks enabled by the module's -fsanitize list.",
      "Only the libdng_sdk cc_library gets the replacement runtime. libdng_sdk-defaults still declares sanitize.misc_undefined; any other module built from those defaults (none shown in the material) would not get the throwing handlers.",
      "The thrown type is std::runtime_error, not dng_sdk's own exception type; the material does not show whether every consumer of libdng_sdk catches std exceptions at the boundary. The commit's listed test is ImageDecoderTest#testInterestingDngs."
     ],
     "behavior_changes": [
      "Integer overflow detected inside libdng_sdk now throws std::runtime_error (message 'ubsan: add-overflow', 'sub-overflow', 'mul-overflow', 'negate-overflow' or 'divrem-overflow') instead of aborting the process.",
      "libdng_sdk no longer uses the ubsan_minimal runtime; sanitizer instrumentation stays compiled in via explicit -fsanitize cflags.",
      "Callers that do not catch C++ exceptions at the libdng_sdk boundary would see an uncaught exception instead of a sanitizer abort (the material does not show this either way)."
     ],
     "preconditions": [
      "A DNG/raw image whose contents drive dng_sdk into an arithmetic overflow must be processed by code linking libdng_sdk.",
      "The commit's test (ImageDecoderTest#testInterestingDngs) indicates the Java ImageDecoder DNG path is a consumer; the material does not identify other consumers.",
      "Index search returned frameworks/base android_hardware_camera2_DngCreator.cpp and frameworks/av img_utils DngUtils.cpp as semantic matches, but these are DNG writers (TiffWriter/OpcodeListBuilder) and the material does not show them calling libdng_sdk (per index search, relevance unconfirmed)."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28, identical Android.bp and ubsan_throwing_runtime.cpp hunks (same blob ids 1978b39..3d95a7b and aeaad0c); only the OWNERS hunk differs (removes zalman@google.com).",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16, identical functional hunks; OWNERS hunk differs trivially.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (the analysed commit); present in the code at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 7937f2f835ab in android-17.0.0_r1, tagged RESTRICT AUTOMERGE, identical functional hunks."
     },
     "backport_notes": [
      "Apply the three Android.bp pieces together with the new source file: sanitize.never: true, the explicit -fsanitize=signed-integer-overflow,unsigned-integer-overflow plus -fno-sanitize-link-runtime cflags, and the srcs entry for ubsan-replacement/ubsan_throwing_runtime.cpp. The replacement relies on rtti: true and cppflags -fexceptions in libdng_sdk-defaults; the Android.bp pre-image blob (1978b39) is the same in all four branches, so the diff applies as-is there."
     ],
     "confidence": "high",
     "unknowns": [
      "Which dng_sdk functions contain the overflowing arithmetic behind the seven referenced bugs; the material shows only the build and runtime change.",
      "Whether all consumers of libdng_sdk (beyond the ImageDecoder path exercised by ImageDecoderTest#testInterestingDngs) catch std::runtime_error at the library boundary.",
      "What the three Cherrypick-From references contain; they share this commit's Change-Id, so they appear to be the same change on other branches.",
      "Build or link behaviour if only part of the Android.bp change is applied (for example, without sanitize.never or -fno-sanitize-link-runtime)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0052": {
     "cve": "CVE-2026-0052",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error (via a bundled replacement runtime, ubsan-replacement/ubsan_throwing_runtime.cpp) instead of aborting the process through the ubsan-minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and UBSan runtime handling",
     "weakness": "Integer overflow in DNG processing arithmetic surfaced as a sanitizer abort (process crash / DoS); the exact overflowing expressions are not shown in the diff",
     "root_cause": "libdng_sdk-defaults in Android.bp enables sanitize.misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"]. Per the commit message, these detections were handled by the ubsan-minimal runtime as a hard abort. The library is compiled with -fexceptions and rtti (C++ exceptions are enabled, per the commit message), but a sanitizer-detected overflow while processing DNG data bypassed exception handling and aborted the process decoding the image. The commit message lists seven bugs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942) that this single change addresses, which fits several distinct overflow sites all ending in the same abort.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } so Soong does not apply its sanitizer configuration to this module and so does not link the ubsan-minimal runtime. The comment says Soong has no dedicated flag for this, which is why 'never' is combined with manual -fsanitize cflags.",
      "Android.bp, cc_library libdng_sdk: cflags now contain -fsanitize=signed-integer-overflow,unsigned-integer-overflow, so instrumentation is still emitted, plus -fno-sanitize-link-runtime so no compiler-rt UBSan runtime is linked. -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a message of the form \"ubsan: <kind>-overflow\". Because the module is built with -fexceptions (libdng_sdk-defaults, Android.bp lines 141-142), the throw can unwind to a C++ handler instead of the process aborting.",
      "OWNERS (14/15/16 only): adds bug component 1075130, includes the frameworks/base graphics OWNERS file and marks the existing owners as LAST_RESORT_SUGGESTION. This has no functional effect."
     ],
     "completeness": [
      "The fix changes only how detected overflows are reported. It does not correct any overflowing arithmetic in the dng_sdk sources, so an overflow that used to abort now throws.",
      "Only the cc_library named libdng_sdk is switched to the throwing runtime. libdng_sdk-defaults still declares misc_undefined for both overflow checks, so any other module built from those defaults (the material shows none) would keep the previous behaviour.",
      "Handlers are provided for five overflow kinds (add, sub, mul, negate, divrem), matching the two -fsanitize groups enabled in cflags. The material does not confirm that these are every handler symbol the instrumentation can emit.",
      "The handlers take no parameters and the exception message carries only the overflow kind, so no source location is reported.",
      "Whether the std::runtime_error is caught depends on the callers, and the material shows no catch sites. The cited test ImageDecoderTest#testInterestingDngs suggests the ImageDecoder path was exercised."
     ],
     "behavior_changes": [
      "When a signed or unsigned integer overflow is detected inside libdng_sdk, a std::runtime_error (\"ubsan: add-overflow\" etc.) is thrown instead of the ubsan-minimal runtime aborting the process.",
      "libdng_sdk no longer links a compiler-rt UBSan runtime (sanitize.never plus -fno-sanitize-link-runtime). The handler symbols have hidden visibility and stay internal to the library.",
      "Also applies to host_linux builds of libdng_sdk, since the sanitize and cflags changes are not target-scoped."
     ],
     "preconditions": [
      "A process must decode or otherwise process DNG data through libdng_sdk, and that data must push an arithmetic expression into signed or unsigned overflow. The material does not show the specific overflowing sites.",
      "The cited test is ImageDecoderTest#testInterestingDngs, which points at the platform image decoding path as the consumer that was exercised. Per index search, frameworks/base DngCreator.cpp and frameworks/av img_utils DngUtils.cpp are DNG writers built on TiffWriter/img_utils, not libdng_sdk callers, so they are not evidence of reachability."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk is identical to 16 (same blob ids 1978b39..3d95a7b) and ubsan_throwing_runtime.cpp is the same. OWNERS differs only in the pre-existing names it replaces (adaubert, zalman). The resolver reports 3/5 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. The Android.bp hunk and runtime file are identical to 16, and the OWNERS hunk is adapted to the branch's single-line file. The resolver reports 3/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. Its Cherrypick-From lines list 81ded662eee0 and two other internal commits. The resolver reports 3/5 lines of the patched region present at android-security-16.0.0_r8, and the r8 code context shows the full Android.bp change and runtime file.",
      "17": "adapted fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1. The Android.bp layout differs (hunk at line 105, srcs is an explicit per-file list), so ubsan_throwing_runtime.cpp is appended to the existing srcs list instead of a new srcs block. This commit has no OWNERS change. It was resolved via a related bug rather than the Change-Id, and the resolver reports 5/6 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The change is build-only plus one new source file. When porting, both halves are needed on the same module: sanitize.never: true with the manual -fsanitize/-fno-sanitize-link-runtime cflags, and the runtime source in srcs. Keeping Soong's misc_undefined active on the module would bring back the ubsan-minimal runtime and its abort behaviour (general platform knowledge). The module must keep -fexceptions from libdng_sdk-defaults.",
      "No 'Fix in fork' section was provided, so no per-file fork verdict can be reported."
     ],
     "confidence": "high",
     "unknowns": [
      "Which dng_sdk expressions overflow for each of the seven referenced bugs; the diff touches no parsing code.",
      "Which callers catch the new std::runtime_error (the material shows no catch sites), and so whether every path that used to abort now recovers cleanly.",
      "How the other two internal commits listed in Cherrypick-From (475c8cce973f, 1337d2e14a41) relate to this change; they may be the same change on other branches."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0080": {
     "cve": "CVE-2026-0080",
     "summary": "Replaces the aborting ubsan-minimal runtime in libdng_sdk with a tiny in-tree runtime whose __ubsan_handle_*_overflow handlers throw std::runtime_error, so integer-overflow checks detected while parsing DNG data raise a catchable C++ exception instead of killing the process.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and ubsan runtime replacement",
     "weakness": "Integer overflow in DNG parsing arithmetic, surfaced as a process abort by the UBSan integer-overflow runtime (denial of service)",
     "root_cause": "libdng_sdk-defaults in Android.bp builds the library with sanitize.misc_undefined = [\"unsigned-integer-overflow\", \"signed-integer-overflow\"]. Per the commit message this used the ubsan-minimal runtime, which handles a detected overflow with a hard abort. Malformed DNG input that pushed arithmetic in the dng_sdk sources into overflow therefore terminated the process decoding the image. This happened even though the library is built with -fexceptions and rtti and could have reported an error instead. The commit message lists seven bugs, which fits several distinct overflow sites that all end in the same abort. The individual arithmetic sites are not changed or shown in the material.",
     "fix_analysis": [
      "Android.bp :: cc_library libdng_sdk: adds sanitize: { never: true } so that Soong no longer links the ubsan minimal runtime for this module. The comment says Soong has no dedicated flag for dropping only the runtime.",
      "Android.bp :: cc_library libdng_sdk: turns the same two checks back on by hand with cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow. It also adds -fno-sanitize-link-runtime, so the compiler emits the __ubsan_handle_* calls but no runtime implementation is linked.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines hidden-visibility extern \"C\" __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a short message, so a detected overflow now unwinds as a C++ exception. The throw is viable because libdng_sdk-defaults already sets rtti: true and cppflags -fexceptions.",
      "Android.bp :: cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs so the handlers are compiled into libdng_sdk itself.",
      "OWNERS: metadata only (bug component and owner list), no functional effect."
     ],
     "completeness": [
      "The fix changes the failure mode, not the arithmetic. No dng_sdk source file is modified, so the overflowing computations still happen; they now raise an exception rather than an abort.",
      "Handlers are provided for the five overflow kinds behind -fsanitize=signed-integer-overflow,unsigned-integer-overflow (add, sub, mul, negate, divrem). If sanitize.never also suppresses the misc_undefined list from the defaults, no other handler symbols should be referenced. The material does not show that interaction directly.",
      "The outcome depends on a caller catching the std::runtime_error, and the material does not show the catch sites. An exception that is not handled anywhere would still terminate the process via std::terminate.",
      "Exceptions can now be thrown from arbitrary arithmetic points inside dng_sdk. This change does not audit the throw paths for exception safety.",
      "sanitize.never: true applies to the whole libdng_sdk module, so any other sanitizer Soong would apply to it is disabled too, not only the ubsan runtime linkage (general platform knowledge). The host_linux target keeps static_libs libcompiler_rt-extras unchanged."
     ],
     "behavior_changes": [
      "Integer overflow detected by UBSan inside libdng_sdk now throws std::runtime_error(\"ubsan: <kind>-overflow\") instead of aborting the process. A DNG that previously crashed the decoding process should now just fail to decode, provided the caller catches the exception.",
      "libdng_sdk is built with sanitize.never, so Soong-driven sanitizers no longer apply to this module. The two integer-overflow checks are kept through explicit cflags."
     ],
     "preconditions": [
      "A DNG image is parsed by libdng_sdk and its contents push signed or unsigned integer arithmetic into overflow at an instrumented site.",
      "The fix was tested with atest ImageDecoderTest#testInterestingDngs, which suggests the ImageDecoder DNG decoding path uses libdng_sdk. The exact decode entry points are not shown in the material.",
      "Per index search: the index returned frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp (DngCreator_setup) and frameworks/av/media/img_utils/src/DngUtils.cpp (OpcodeListBuilder). These are DNG writers built on TiffWriter. The material does not show them using libdng_sdk, so they look like noisy matches."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28. The Android.bp hunk (1978b39..3d95a7b) and ubsan_throwing_runtime.cpp are identical; the OWNERS diff differs only in the lines removed.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16. The Android.bp hunk and runtime file are identical.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7. The code context at android-security-16.0.0_r8 shows the sanitize.never block, the explicit -fsanitize cflags, -fno-sanitize-link-runtime, the srcs entry and the full runtime file.",
      "17": "adapted fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1, which is the first change listed in Cherrypick-From (resolved by related bug). The Android.bp layout differs, so the runtime file is appended to the main library srcs list after source/dng_xy_coord.cpp. There is no OWNERS change, and the runtime file is identical."
     },
     "backport_notes": [
      "The Android.bp hunk is identical across 14, 15 and 16 (same blob ids). On trees with a different libdng_sdk layout (as in 17), add ubsan_throwing_runtime.cpp to the library's srcs list. Keep sanitize.never together with the explicit -fsanitize and -fno-sanitize-link-runtime cflags.",
      "The change relies on libdng_sdk-defaults already having rtti: true and cppflags -fexceptions, and on the consumer catching std::runtime_error. Verify both in the target tree, otherwise the throw still ends the process."
     ],
     "confidence": "high",
     "unknowns": [
      "Which specific arithmetic sites in the dng_sdk sources overflow. Seven bug ids are referenced, but no source change or reproducer details are included.",
      "Where the std::runtime_error is caught in the consuming code (ImageDecoder / codec path), and whether every entry into libdng_sdk is wrapped.",
      "How Soong handles sanitize.never: true on the module combined with sanitize.misc_undefined in the shared defaults.",
      "Why the per-major facts report only 3/5 (14, 16) or 3/4 (15) lines of the patched region present, when the code context for 16 shows all added lines."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0097": {
     "cve": "CVE-2026-0097",
     "summary": "Makes the LE SMP state machine, for the Secure Connections Passkey Display and Key Notification association models, wait for both the remote device's passkey confirmation (STK generated in smp_process_stk) and the local user's approval (SMP_SecurityGrant) before emitting SMP_KEY_READY_EVT, cancels SMP pairing on BTM authentication timeout, and initialises LE pairing flags in btif_dm.",
     "component": "platform/packages/modules/Bluetooth, system/stack/smp (Security Manager Protocol) with btm_sec and btif_dm",
     "weakness": "Missing user-consent/authorization gate in the LE pairing state machine (key generation proceeded without waiting for the local user's approval)",
     "root_cause": "In system/stack/smp/smp_keys.cc, smp_process_stk unconditionally masked the generated STK and posted SMP_KEY_READY_EVT to the SMP state machine. For the SMP_MODEL_SEC_CONN_PASSKEY_DISP and SMP_MODEL_KEY_NOTIF association models, this meant the pairing moved on to key material as soon as the STK was produced, with no dependency on the local user's answer delivered through SMP_SecurityGrant. The shown part of the old SMP_SecurityGrant had no model-specific branch for these models before its consent-dialog check. In addition, btm_sec_pairing_timeout did not cancel an active LE SMP pairing in BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, so SMP could continue after the BTM pairing timer expired. Also, btif_dm_ble_passkey_notif_evt did not set pairing_cb.is_le_only or is_le_nc for this LE passkey notification.",
     "fix_analysis": [
      "system/stack/smp/smp_int.h (tSMP_CB): adds a passkey_display_state struct with two bools: approved (the user approved the pairing) and confirmed (the remote device confirmed the passkey).",
      "system/stack/smp/smp_keys.cc:smp_process_stk: after smp_mask_enc_key, if selected_association_model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF, it sets passkey_display_state.confirmed = true and saves the masked key into p_cb->tk. If passkey_display_state.approved is still false, it logs 'Waiting for user to approve pairing' and returns without posting SMP_KEY_READY_EVT. Otherwise (other models, or already approved) it posts SMP_KEY_READY_EVT with key_type SMP_KEY_TYPE_STK as before.",
      "system/stack/smp/smp_api.cc:SMP_SecurityGrant: adds an early block that runs when smp_cb.pairing_bda == bd_addr and the model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF. On res == SMP_SUCCESS it sets passkey_display_state.approved = true. If confirmed is already true, it posts SMP_KEY_READY_EVT with key {SMP_KEY_TYPE_TK, smp_cb.tk.data()}; otherwise it logs that it is waiting for the remote to enter the passkey. On any other res it posts SMP_AUTH_CMPL_EVT with status SMP_PAIR_AUTH_FAIL. The block returns, so the existing handling later in the function is skipped for these models.",
      "system/stack/btm/btm_sec.cc:btm_sec_pairing_timeout: in case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, if btm_sec_cb.pairing_flags has BTM_PAIR_FLAGS_LE_ACTIVE, it calls SMP_PairCancel(p_cb->pairing_bda). That function posts SMP_AUTH_CMPL_EVT with SMP_PAIR_FAIL_UNKNOWN when SMP is not idle for that address. It then falls through (FALLTHROUGH_INTENDED) into the BTM_PAIR_STATE_GET_REM_NAME handling, which notifies the UI of the timeout. The state/flags log is raised from verbose to warn.",
      "system/btif/src/btif_dm.cc:btif_dm_ble_passkey_notif_evt: sets pairing_cb.is_le_only = true and pairing_cb.is_le_nc = false before logging the SSP request and invoking the SSP request callback with BT_SSP_VARIANT_PASSKEY_NOTIFICATION (per index search). The commit message describes this as initialising default LE pairing flags."
     ],
     "completeness": [
      "The gate applies only to SMP_MODEL_SEC_CONN_PASSKEY_DISP and SMP_MODEL_KEY_NOTIF. All other association models go through smp_process_stk and SMP_SecurityGrant unchanged.",
      "The wait is enforced in smp_process_stk and released from SMP_SecurityGrant. No other key-ready emission site is modified in the diff.",
      "The diff does not reset passkey_display_state anywhere. Whether the control block is cleared between pairings (so a stale approved flag cannot carry over) depends on existing tSMP_CB reset code that the material does not show.",
      "The commit message names the flag com.android.bluetooth.flags.passkey_entry_pairing_approval, but none of the shown hunks test a flag. The new behaviour is unconditional in the diffed code.",
      "The key posted from SMP_SecurityGrant uses key_type SMP_KEY_TYPE_TK, while the non-deferred path in smp_process_stk uses SMP_KEY_TYPE_STK. The material does not show how the state machine handles that difference.",
      "The SMP_SecurityGrant block requires smp_cb.pairing_bda to equal the granted address. Grants for a different address fall through to the old handling.",
      "The timeout cancellation covers only BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with BTM_PAIR_FLAGS_LE_ACTIVE set. Other timeout states are unchanged."
     ],
     "behavior_changes": [
      "For LE SC Passkey Display / Key Notification pairings, key generation now stalls until the local user grants through SMP_SecurityGrant. The remote's confirmation alone no longer advances the state machine.",
      "A non-success SMP_SecurityGrant for these models now fails the pairing with SMP_PAIR_AUTH_FAIL through SMP_AUTH_CMPL_EVT, instead of taking the previous path.",
      "A BTM pairing timeout in WAIT_AUTH_COMPLETE now cancels an active LE SMP pairing (SMP_PAIR_FAIL_UNKNOWN), then notifies the UI of the timeout through the GET_REM_NAME fallthrough.",
      "btm_sec_pairing_timeout logs state/flags at warn level instead of verbose. SMP_SecurityGrant logs more fields.",
      "pairing_cb.is_le_only and is_le_nc are now set to true and false on LE passkey notification."
     ],
     "preconditions": [
      "An LE pairing with a peer where the selected association model is SMP_MODEL_SEC_CONN_PASSKEY_DISP or SMP_MODEL_KEY_NOTIF.",
      "smp_process_stk is reached when the STK is generated. The user's approval arrives through SMP_SecurityGrant; the routing from btif/BTA is not shown in the material.",
      "For the timeout hunk: BTM pairing state BTM_PAIR_STATE_WAIT_AUTH_COMPLETE with BTM_PAIR_FLAGS_LE_ACTIVE set when the pairing timer fires.",
      "btif_dm_ble_passkey_notif_evt is the btif handler that delivers BT_SSP_VARIANT_PASSKEY_NOTIFICATION to the UI (per index search)."
     ],
     "per_major": {
      "14": "adapted fix: commit 725df035dfe2 in android-security-14.0.0_r28. Same logic, adapted to the 14 codebase: btif_dm_ble_key_notif_evt instead of btif_dm_ble_passkey_notif_evt, btm_cb.pairing_flags instead of btm_sec_cb.pairing_flags, BTM_TRACE_*/SMP_TRACE_* macros, typedef struct tSMP_CB, and the gd shim LOG_ASSERT kept. 7/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "adapted fix: commit c18ac65121e1 in android-security-15.0.0_r16. Same logic as 16; the btif hunk is in btif_dm_ble_key_notif_evt, with log::verbose/warn formatting. 7/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit ce76f82dac2a in android-security-16.0.0_r7 (combined backport of main CLs ag/35861798, ag/35914708, ag/35861802). 7/8 lines of the patched region present at android-security-16.0.0_r8; the code context shows the new hunks in place.",
      "17": "resolved on base: commit 57541dad9e1f in android-17.0.0_r1. The diff supplied for 17 is identical to the Android 14 diff (BTM_TRACE_WARNING, gd shim LOG_ASSERT), and only 3/8 lines of the patched region are present at android-security-17.0.0_r1. The material therefore does not reliably show what the change looks like on 17."
     },
     "backport_notes": [
      "Land all five files together. The passkey_display_state field in smp_int.h is needed by both smp_keys.cc and smp_api.cc, and the smp_process_stk gate returns without posting SMP_KEY_READY_EVT, so it needs the SMP_SecurityGrant counterpart. On older trees, adapt the btif handler name (btif_dm_ble_key_notif_evt vs btif_dm_ble_passkey_notif_evt), btm_cb vs btm_sec_cb for pairing_flags, and the logging macros, as Google did for 14 and 15.",
      "The diff has no flag check despite the Flag: line in the commit message. Check whether your branch expects com.android.bluetooth.flags.passkey_entry_pairing_approval gating."
     ],
     "confidence": "medium",
     "unknowns": [
      "The exact security consequence (how the missing approval wait leads to EoP). The material gives only the commit message and the Critical EoP rating.",
      "Whether tSMP_CB (and so passkey_display_state) is reset between pairings; the diff contains no reset.",
      "How the SMP state machine treats SMP_KEY_TYPE_TK posted from SMP_SecurityGrant compared with SMP_KEY_TYPE_STK from smp_process_stk in the same state.",
      "Where com.android.bluetooth.flags.passkey_entry_pairing_approval is actually checked, if anywhere.",
      "The real content of the Android 17 change: the supplied 17 diff duplicates the 14 diff, and only 3/8 lines match at android-security-17.0.0_r1.",
      "How btif_dm routes the user's SSP reply to SMP_SecurityGrant, and how is_le_only/is_le_nc affect that routing.",
      "The rest of the pre-fix SMP_SecurityGrant body after the consent-dialog check."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21352": {
     "cve": "CVE-2026-21352",
     "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to build 2471. The update adds overflow-checked arithmetic and size limits to the JXL decode/encode buffer geometry and allocators, fixes an off-by-one table read in dng_opcode_MapTable::ReplicateLastEntry, and guards several divisions by zero when blackLevel is 65535.",
     "component": "platform/external/dng_sdk (Adobe DNG SDK): JXL codec glue (source/dng_jxl.cpp), opcode processing (dng_misc_opcodes.cpp, dng_gain_map.cpp, dng_reference.cpp), memory allocation (dng_ref_counted_block.cpp), safe arithmetic helpers",
     "weakness": "Integer overflow / truncation in buffer-size and stride computation leading to undersized allocations and out-of-bounds access; off-by-one out-of-bounds read; missing allocation null check; floating-point division by zero",
     "root_cause": "Several computations on image-derived geometry were done with unchecked arithmetic. (1) source/dng_jxl.cpp: jxl_image_chunk_reader and jxl_buffer_chunk_reader built a dng_pixel_buffer from size_t xpos/ypos/xsize/ysize by casting to int32 and adding without overflow checks (fArea.t/l/b/r). Their fRowStep was computed with SafeUint32Mult and no bound on fPlanes or width. (2) EncodeJXL_Common computed fRowStep and previewBytesNeeded with plain multiplication. (3) dng_jxl_decoder::Decode accepted basicInfo.xsize/ysize of any size, computed fRowStep and bytesNeeded with plain multiplication, and cast the libjxl-reported output buffer size to uint32 before host.Allocate. (4) jxl_memory_block added 160 to a uint64 bytesNeeded with no wrap check and did not check the Malloc result for null. (5) source/dng_misc_opcodes.cpp: dng_opcode_MapTable::ReplicateLastEntry read table[fCount] instead of table[fCount - 1]. The table holds 0x10000 uint16 entries and fCount may be up to 0x10000, so the read is one past the end of the allocated block in that case and reads an entry the constructor did not fill otherwise. (6) source/dng_ref_counted_block.cpp: dng_ref_counted_block::Allocate computed size + sizeof(header) with no wrap check. (7) dng_gain_map.cpp, dng_reference.cpp and dng_misc_opcodes.cpp: a blackLevel of 65535 produced a zero divisor. The material does not show which of these hunks the bulletin entry refers to; the commit cites two bugs (483697751, 483693973).",
     "fix_analysis": [
      "source/dng_jxl.cpp:jxl_memory_block::jxl_memory_block: computes mallocSize = (size_t)(160u + bytesNeeded), calls ThrowOverflow() if mallocSize <= bytesNeeded, and calls ThrowMemoryFull() if allocator.Malloc returns null. This applies only on the fUseMalloc path (bytesNeeded > 0x80000000).",
      "source/dng_jxl.cpp:jxl_image_chunk_reader (get_color_channel_data_at callback): rejects xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\"); computes fArea.b and fArea.r with SafeInt32Add; adds DNG_REQUIRE(fPlanes <= 0x7fffffff) and DNG_REQUIRE(fArea.W() <= 0x7fffffff); computes fRowStep with SafeInt32Mult instead of SafeUint32Mult.",
      "source/dng_jxl.cpp:jxl_buffer_chunk_reader (same callback): same coordinate limit; fArea.t/l/b/r are now computed with SafeInt32Add, including the fBuffer.fArea offset; same DNG_REQUIRE checks; fRowStep via SafeInt32Mult. bytesNeeded was already computed with SafeUint32Mult.",
      "source/dng_jxl.cpp:EncodeJXL_Common: adds DNG_REQUIRE(previewPixelBuffer.fArea.W() <= 0x7fffffff); fRowStep via SafeInt32Mult; previewBytesNeeded via 3-arg SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp:dng_jxl_decoder::Decode: adds DNG_REQUIRE(basicInfo.xsize <= 0x7fffffff && basicInfo.ysize <= 0x7fffffff) before assigning to dng_point size; buffer.fRowStep and wholeBuffer.fRowStep via SafeInt32Mult; bytesNeeded via the new 3-arg SafeInt64Mult; adds DNG_REQUIRE(size <= 0xffffffff) before the (uint32) cast of the JxlDecoderImageOutBufferSize result passed to host.Allocate.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::ReplicateLastEntry: reads table[fCount - 1] instead of table[fCount]. The constructor validates fCount as non-zero and <= 0x10000, so the index now always falls inside the 0x10000-entry block.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::Prepare: srcScale = (blackLevel < 65535) ? 65535.0 / (65535.0 - blackLevel) : 0.0.",
      "source/dng_gain_map.cpp:dng_opcode_GainMap::ProcessArea, source/dng_reference.cpp:RefVignette32 and RefBaselineMapPoly32: blackScale1 = (blackScale2 != 0.0) ? 1.0f / blackScale2 : 0.0.",
      "source/dng_ref_counted_block.cpp:dng_ref_counted_block::Allocate: computes size_t mallocSize = size + sizeof(header), calls ThrowOverflow() if mallocSize <= size, then calls malloc(mallocSize).",
      "source/dng_safe_arithmetic.h/.cpp: adds SafeInt64Mult(int64, int64, int64), which chains the two-arg SafeInt64Mult and throws on overflow. Removes the __ANDROID__ special case, so __USE_BUILTIN_SMULL_OVERFLOW follows __has_builtin(__builtin_smull_overflow) on Android clang builds too.",
      "source/dng_flags.h: build version 2410 -> 2471; source/dng_tag_codes.h: adds the tcWebP enum value (not security relevant)."
     ],
     "completeness": [
      "In the hunks shown, the stride/size computations in the two JXL chunk readers, EncodeJXL_Common and dng_jxl_decoder::Decode are converted to Safe* arithmetic or bounded by explicit 0x7fffffff / 0xffffffff checks. The fix is a vendor SDK update, not a targeted single-site patch.",
      "jxl_memory_block: the overflow and null checks are added only on the fUseMalloc path (bytesNeeded > 0x80000000). The else path, allocator.Allocate(uint32(bytesNeeded)), is unchanged, but it cannot truncate given the 2 GB threshold.",
      "dng_ref_counted_block::Allocate: size is uint32 and sizeof(header) is size_t, so the new wrap check only matters on builds where size_t is 32-bit. On 64-bit builds the addition cannot wrap.",
      "The division guards substitute a scale of 0.0 for blackLevel == 65535 instead of rejecting the input. This is a robustness change, not a bounds check.",
      "The ReplicateLastEntry fix addresses the one-past-end read. The constructor validation around it (fCount in 1..0x10000, dataSize match) is unchanged.",
      "The material does not show the definition of DNG_REQUIRE, so the diff does not confirm how the new DNG_REQUIRE checks fail (for example, which exception type they raise)."
     ],
     "behavior_changes": [
      "JXL decode/encode now throws a dng_exception (ThrowOverflow, ThrowMemoryFull, or a DNG_REQUIRE failure) in these cases: coordinates, dimensions or plane counts above 0x7fffffff; stride/size products that overflow; or a libjxl output buffer size above 0xffffffff. Sites that used plain arithmetic or casts previously continued with wrapped or truncated values.",
      "dng_ref_counted_block::Allocate throws on size wrap instead of allocating an undersized block.",
      "MapTable opcodes now copy table[fCount - 1] into entries fCount..0xffff, instead of the value at table[fCount].",
      "Processing with blackLevel == 65535 now uses a scale of 0.0 instead of dividing by zero.",
      "On Android clang builds where the builtin is available, SafeInt64Mult now uses the __builtin_smull_overflow path instead of the SafeInt64MultSlow fallback.",
      "The SDK build string is now 1.7.1 (2471); new tcWebP enum value."
     ],
     "preconditions": [
      "dng_sdk must process a DNG/TIFF-family image. The dng_jxl.cpp hunks require JXL-compressed image data handled by dng_jxl_decoder::Decode or the chunk readers. The dng_misc_opcodes.cpp hunk requires an OpcodeList containing a MapTable opcode; the one-past-end read requires fCount == 0x10000.",
      "Per index search, frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp (DngCreator_setup, DngCreator_nativeWriteInputStream) came up as a semantic match. The code shown uses TiffWriter, not dng_sdk symbols, so the material does not establish it as a caller.",
      "dng_sdk is used on Android by Skia's RAW/DNG codec for image decoding (general platform knowledge). The material does not show whether the JXL path is compiled into Android builds."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 72d44e39d499 in android-security-14.0.0_r28, identical diff (Update dng_sdk to 1.7.1 2471). At android-security-14.0.0_r29, 5/8 lines of the patched region are present; the material does not show why the other lines differ.",
      "15": "same fix: platform/external/dng_sdk 7a737bb68ca6 in android-security-15.0.0_r16, identical diff. At android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "same fix: platform/external/dng_sdk b0d9d2a8d100 in android-security-16.0.0_r7, a cherry-pick of two internal commits (Change-Id I25ad017a3b1bdf00bf4b773ba508a43aab470a33). At android-security-16.0.0_r8, 5/8 lines of the patched region are present. The code context there shows the jxl_memory_block, ref_counted_block, division-guard and safe-arithmetic changes, with dng_flags.h at build 2502, which points to a later SDK update changing the version lines.",
      "17": "same fix in base: platform/external/dng_sdk 9a9e97362f2d in android-17.0.0_r1. Same content, with only context differences in source/dng_reference.cpp (RefBaselineMapPoly32 hunk at line 3011 instead of 3009). At android-security-17.0.0_r1, 5/8 lines are present."
     },
     "backport_notes": [
      "The commit is a whole-SDK bump from 1.7.1 build 2410. It uses SafeInt32Add/SafeInt32Mult and the two-arg SafeInt64Mult without adding them, so they must already exist; it adds only the 3-arg SafeInt64Mult. The functional hunks can be backported on their own; the version and tcWebP lines are not needed for the fix.",
      "Removing the __ANDROID__ override of __USE_BUILTIN_SMULL_OVERFLOW changes SafeInt64Mult codegen on Android. The header still falls back to SafeInt64MultSlow when the builtin is unavailable."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which hunk(s) of the SDK update correspond to CVE-2026-21352 and which to the second cited bug; the material does not map bug IDs to hunks.",
      "The definition of DNG_REQUIRE and the exception it raises on failure.",
      "The declared type of dng_pixel_buffer::fRowStep.",
      "Whether JXL support in dng_sdk is compiled into Android builds, and which Android process decodes such content. The bulletin rates this Critical EoP, but the material does not show a reachable caller.",
      "The content of the later SDK update (build 2502) seen at android-security-16.0.0_r8, and which 3 of the 8 patched-region lines differ at the latest tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-21353": {
     "cve": "CVE-2026-21353",
     "summary": "Updates external/dng_sdk from 1.7.1 build 2410 to build 2471. The update bundles overflow-checked size/stride arithmetic and dimension limits in the JXL decode/encode paths, an overflow check in two raw allocators, an off-by-one fix in dng_opcode_MapTable::ReplicateLastEntry, and divide-by-zero guards for blackLevel == 65535.",
     "component": "platform/external/dng_sdk (DNG SDK: JXL codec glue, opcode processing, memory allocation, safe arithmetic)",
     "weakness": "Integer overflow / wraparound in buffer size and stride computation (undersized allocation), plus an off-by-one out-of-bounds read and unguarded floating-point division by zero. The commit does not identify which of these is the CVE.",
     "root_cause": "Several arithmetic paths in dng_sdk trusted file-derived values without overflow checks. In source/dng_jxl.cpp, jxl_image_chunk_reader and jxl_buffer_chunk_reader cast size_t xpos/ypos/xsize/ysize straight to int32, added them with plain '+', and computed fRowStep via SafeUint32Mult even though the fix switches to SafeInt32Mult (signed stride). dng_jxl_decoder::Decode assigned basicInfo.xsize/ysize to dng_point without range checks, computed fRowStep with plain '*' and bytesNeeded with unchecked uint64 multiplication, and cast the JxlDecoderImageOutBufferSize result to uint32 without checking that it fit. EncodeJXL_Common computed previewPixelBuffer.fRowStep and previewBytesNeeded with plain multiplication. jxl_memory_block added 160u to a uint64 bytesNeeded and passed the result to allocator.Malloc without an overflow or null check. In source/dng_ref_counted_block.cpp, dng_ref_counted_block::Allocate computed size + sizeof(header) without a wrap check. In source/dng_misc_opcodes.cpp, dng_opcode_MapTable::ReplicateLastEntry read table[fCount] from a 0x10000-entry table while fCount may legally be 0x10000 (the constructor allows 1..0x10000). That read is one element past the end and is also not the actual last entry. In dng_gain_map.cpp, dng_reference.cpp and dng_misc_opcodes.cpp, a blackLevel of 65535 made blackScale2 (or 65535.0 - blackLevel) zero and caused a division by zero.",
     "fix_analysis": [
      "source/dng_jxl.cpp:jxl_memory_block::jxl_memory_block: computes mallocSize = (size_t)(160u + bytesNeeded), calls ThrowOverflow() if mallocSize <= bytesNeeded, and calls ThrowMemoryFull() if allocator.Malloc returns null, before DNG_ALIGN_SIMD is applied.",
      "source/dng_jxl.cpp:jxl_image_chunk_reader and jxl_buffer_chunk_reader (get_color_channel_data_at): rejects xpos/ypos/xsize/ysize > 0x7fffffff with ThrowOverflow(\"coordinate too large\"). Computes fArea.b/fArea.r (and fArea.t/fArea.l in the buffer reader) with SafeInt32Add. Adds DNG_REQUIRE(fPlanes <= 0x7fffffff) and DNG_REQUIRE(fArea.W() <= 0x7fffffff). Computes fRowStep with SafeInt32Mult instead of SafeUint32Mult.",
      "source/dng_jxl.cpp:EncodeJXL_Common: adds DNG_REQUIRE on previewPixelBuffer.fArea.W(), computes fRowStep via SafeInt32Mult, and computes previewBytesNeeded via 3-arg SafeUint32Mult before host.Allocate.",
      "source/dng_jxl.cpp:dng_jxl_decoder::Decode: adds DNG_REQUIRE(basicInfo.xsize/ysize <= 0x7fffffff) before assigning to dng_point. Computes buffer.fRowStep and wholeBuffer.fRowStep via SafeInt32Mult and bytesNeeded via the new 3-arg SafeInt64Mult. Adds DNG_REQUIRE(size <= 0xffffffff) before casting the JxlDecoderImageOutBufferSize result to uint32 for host.Allocate.",
      "source/dng_safe_arithmetic.cpp/.h: adds int64 SafeInt64Mult(int64, int64, int64) (a chained two-arg SafeInt64Mult), used by Decode. Removes the __ANDROID__ special case that forced __USE_BUILTIN_SMULL_OVERFLOW to 0, so Android clang builds use the builtin path when __has_builtin reports it.",
      "source/dng_ref_counted_block.cpp:dng_ref_counted_block::Allocate: computes size_t mallocSize = size + sizeof(header), calls ThrowOverflow() if mallocSize <= size, then calls malloc.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::ReplicateLastEntry: reads table[fCount - 1] instead of table[fCount]. This stays inside the 0x10000-entry buffer when fCount == 0x10000 and uses the real last entry.",
      "source/dng_misc_opcodes.cpp:dng_opcode_MapTable::Prepare, source/dng_gain_map.cpp:dng_opcode_GainMap::ProcessArea, source/dng_reference.cpp:RefVignette32 and RefBaselineMapPoly32: guard the reciprocal (srcScale / blackScale1) with a zero check, yielding 0.0 when blackLevel == 65535.",
      "source/dng_flags.h: build version 2410 -> 2471. source/dng_tag_codes.h: adds the tcWebP enum value (no functional change in this diff)."
     ],
     "completeness": [
      "All JXL chunk-reader and decoder stride/size computations shown in the diff now go through Safe* helpers or DNG_REQUIRE. Both reader classes (jxl_image_chunk_reader and jxl_buffer_chunk_reader) receive the same checks.",
      "In jxl_buffer_chunk_reader the existing bytesNeeded = SafeUint32Mult(fRowStep, H, ...) remains, now fed by an fRowStep validated by SafeInt32Mult.",
      "The dng_ref_counted_block::Allocate check is only effective where size_t is 32-bit. On 64-bit, a uint32 size plus sizeof(header) cannot wrap, so the check is defensive there.",
      "The divide-by-zero guards compare a real32 against 0.0 in dng_gain_map.cpp and dng_reference.cpp. The srcScale guard in dng_misc_opcodes.cpp uses the integer comparison blackLevel < 65535. Both avoid the division, and the resulting scale of 0.0 is a semantic choice.",
      "The commit is an umbrella SDK update. The material does not say which change corresponds to CVE-2026-21353, so coverage of the specific CVE trigger cannot be judged beyond the listed changes."
     ],
     "behavior_changes": [
      "JXL images whose xsize/ysize, chunk coordinates, plane count or width exceed 0x7fffffff, or whose stride/size products overflow, now throw (ThrowOverflow / DNG_REQUIRE / Safe* helpers) instead of continuing with wrapped values.",
      "jxl_memory_block now calls ThrowMemoryFull() when the large-allocation Malloc path returns null, and dng_ref_counted_block::Allocate calls ThrowOverflow() on size wrap.",
      "dng_opcode_MapTable::ReplicateLastEntry now replicates table[fCount-1]. For fCount < 0x10000 the replicated value differs from before (previously the entry just past the parsed data).",
      "A blackLevel of 65535 now yields a scale of 0.0 instead of a division by zero in GainMap, MapTable, Vignette and BaselineMapPoly processing.",
      "On Android clang builds, SafeInt64Mult may now use the __builtin_smull_overflow path instead of SafeInt64MultSlow.",
      "New tcWebP tag code enum value."
     ],
     "preconditions": [
      "Attacker-controlled DNG/JXL content must be parsed by code built from external/dng_sdk. The JXL paths also require the dng_jxl.cpp codec glue to be compiled and reachable in the consuming process, which the material does not show.",
      "The MapTable off-by-one requires an opcode list containing a MapTable opcode with fCount == 0x10000 (the constructor accepts fCount in 1..0x10000).",
      "Per index search, frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp uses dng_sdk-adjacent code, but the shown code uses TiffWriter (write side) and does not evidently call the patched dng_sdk decode paths. Platform reachability is therefore not established by the material."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 72d44e39d499 in android-security-14.0.0_r28, identical diff (same blob hashes) to the Android 16 commit. At android-security-14.0.0_r29, 5/8 lines of the patched region are present.",
      "15": "same fix: platform/external/dng_sdk 7a737bb68ca6 in android-security-15.0.0_r16, identical diff to the Android 16 commit. At android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "same fix: platform/external/dng_sdk b0d9d2a8d100 in android-security-16.0.0_r7 (cherry-pick of two internal commits, Change-Id I25ad017a3b1bdf00bf4b773ba508a43aab470a33). At android-security-16.0.0_r8 the SDK is at build 2502 and 5/8 lines of the patched region are present. The shown context includes the fixed code for jxl_memory_block, ref_counted_block, GainMap, RefVignette32 and safe_arithmetic.",
      "17": "same fix: platform/external/dng_sdk 9a9e97362f2d in android-17.0.0_r1 (base). The only difference is a two-line offset in dng_reference.cpp (RefBaselineMapPoly32 hunk at line 3011, different pre-image blob); otherwise the diff is identical. At android-security-17.0.0_r1, 5/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single commit, applied verbatim on 14/15/16/17. The dng_jxl.cpp hunks depend on the new 3-arg SafeInt64Mult added in dng_safe_arithmetic.cpp/.h in the same commit, and on the existing SafeInt32Add/SafeInt32Mult/SafeUint32Mult and DNG_REQUIRE, so take the whole commit rather than individual files.",
      "The latest security tags on every major report only 5/8 lines of the patched region present, and the material does not explain why. The tag is already at build 2502, so code may have changed since; verify each hunk directly rather than relying on the line count."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which of the bundled changes (JXL size overflow, jxl_memory_block/ref_counted_block wrap, MapTable off-by-one, divide-by-zero) is the actual CVE-2026-21353 issue. The commit references two bugs (483697751, 483693973) without mapping them to hunks.",
      "Whether dng_jxl.cpp (libjxl glue) is compiled into Android's dng_sdk build, and which Android process consumes dng_sdk with untrusted input.",
      "Why only 5/8 lines of the patched region are found at the latest security tags, and whether every functional hunk is still present there.",
      "Contents of the two internal Cherrypick-From commits beyond the squashed diff shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-32348": {
     "cve": "CVE-2025-32348",
     "summary": "Stops the Background Activity Launch (BAL) \"visible window\" and \"activity in foreground task\" exemptions from being satisfied by an activity in pinned (PIP) windowing mode. It does this by adding a not-pinned visibility query and a pinned-window flag to the BAL decision path.",
     "component": "platform/frameworks/base, services/core WindowManager (BackgroundActivityStartController / BackgroundLaunchProcessController / VisibleActivityProcessTracker)",
     "weakness": "Over-permissive authorization check: the BAL visibility exemption did not distinguish pinned (PIP) windows from regular visible windows",
     "root_cause": "The BAL exemptions used VisibleActivityProcessTracker.hasVisibleActivity(uid) (via BalState.mCallingUidHasVisibleActivity / mRealCallingUidHasVisibleActivity) and WindowProcessController.hasActivityInVisibleTask(), neither of which looks at windowing mode. A process whose only visible activity is in pinned windowing mode (picture-in-picture) therefore counted as \"visible\" / \"foreground\". As a result, BackgroundActivityStartController returned BAL_ALLOW_VISIBLE_WINDOW for the caller or real caller, and BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed returned BAL_ALLOW_FOREGROUND, which let such an app start activities from the background. The commit message states that pinned windows (e.g. PIP) should not enable BAL.",
     "fix_analysis": [
      "VisibleActivityProcessTracker.java: adds hasVisibleNotPinnedActivity(int uid), which calls match(uid, wpc -> !wpc.inPinnedWindowingMode()). hasVisibleActivity now passes WindowContainer.alwaysTruePredicate(), and match() drops the null-predicate branch, so a predicate is now required.",
      "BackgroundActivityStartController.java BalState: adds mCallingUidHasVisibleNotPinnedActivity and mRealCallingUidHasVisibleNotPinnedActivity. The constructor fills them from getService().mVisibleActivityProcessTracker.hasVisibleNotPinnedActivity(...) in three cases: no real caller gives false, same uid copies the caller value, different uid queries the tracker. Both are appended to toString(). The old mCallingUidHasVisibleActivity fields are kept.",
      "BackgroundActivityStartController.java, caller check (checkBackgroundActivityStartAllowedByCaller path, around line 1050) and real-caller check (checkBackgroundActivityStartAllowedByRealCaller path, around line 1180): the condition `appSwitchAllowedOrFg && state.mCallingUidHasVisibleActivity` / `mRealCallingUidHasVisibleActivity` becomes `... && state.mCallingUidHasVisibleNotPinnedActivity` / `mRealCallingUidHasVisibleNotPinnedActivity`. The verdict message becomes \"callingUid has visible non-pinned window\" / \"realCallingUid has visible non-pinned window\". Otherwise BalVerdict.BLOCK is returned as before.",
      "BackgroundLaunchProcessController.java areBackgroundActivityStartsAllowed(): adds a parameter `boolean inPinnedWindow` after hasActivityInVisibleTask. The foreground-task exemption becomes `(checkConfiguration.isCheckingForFgsStart || !inPinnedWindow) && checkConfiguration.checkOtherExemptions && hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW`. A pinned process therefore no longer gets BAL_ALLOW_FOREGROUND for activity starts, while FGS-start checks (CHECK_FOR_FGS_START) are intentionally unchanged.",
      "WindowProcessController.java areBackgroundActivityStartsAllowed(int, BalCheckConfiguration): passes inPinnedWindowingMode() as the new argument. Per index search, this method is reached from BackgroundActivityStartController.checkProcessAllowsBal (for the caller app and all other processes of the same uid) and from areBackgroundFgsStartsAllowed.",
      "Tests: BackgroundActivityStartControllerExemptionTests now expects BAL_BLOCK when hasVisibleActivity is true but hasVisibleNotPinnedActivity is false, and adds testCaller_appHasVisiblePinnedWindow / testRealCaller_appHasNotPinnedVisibleWindow. BackgroundLaunchProcessControllerTests adds testForegroundTaskBlockedIfPinned. The new VisibleActivityProcessTrackerTests covers WINDOWING_MODE_PINNED.",
      "Follow-up commit c879fb5561f2 (android-security-16.0.0_r1) only edits BackgroundLaunchProcessControllerTests call sites so they compile against the new signature. In three calls it places mInPinnedWindoMode before mHasActivityInVisibleTask. Commit 4d488a6a05ab (android-security-16.0.0_r3) reverts that test edit. Neither touches production code."
     ],
     "completeness": [
      "Covers the two BAL_ALLOW_VISIBLE_WINDOW decisions in BackgroundActivityStartController (caller and real caller) and the BAL_ALLOW_FOREGROUND decision in BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed for non-FGS checks.",
      "The analysed diff does not touch the 'process bound by foreground uid' exemption in BackgroundLaunchProcessController. The android-security-16.0.0_r8 context shows that this path picks a uidHasActiveVisibleNotPinnedWindowPredicate when !isCheckingForFgsStart. The material does not show which commit introduced that.",
      "Pinned-ness is checked per process (WindowProcessController.inPinnedWindowingMode()), not per activity. In the r8 context, hasVisibleNotPinnedActivity uses `wpc -> wpc.hasVisibleNotPinnedActivity()` instead.",
      "hasVisibleActivity is left unchanged, as is ActivityTaskManagerService.hasActiveVisibleWindow, which wraps it per index search. BalState still records mCallingUidHasVisibleActivity. The material does not show whether any remaining BAL decision depends on those unfiltered values.",
      "FGS starts from a pinned process are deliberately still allowed, because isCheckingForFgsStart short-circuits the pinned check."
     ],
     "behavior_changes": [
      "An app (or PendingIntent real caller) whose only visible activity is in pinned/PIP mode now gets BAL_BLOCK instead of BAL_ALLOW_VISIBLE_WINDOW from the caller/real-caller visibility exemption, unless another exemption applies.",
      "A pinned process no longer receives BAL_ALLOW_FOREGROUND from the 'activity in foreground task' exemption for activity starts. Foreground-service start checks are unaffected.",
      "BalState.toString() gains callingUidHasVisibleNotPinnedActivity and realCallingUidHasVisibleNotPinnedActivity entries. BalVerdict messages change to '... has visible non-pinned window'.",
      "Internal API changes: BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed gains a boolean inPinnedWindow parameter, VisibleActivityProcessTracker.match no longer accepts a null predicate, and VisibleActivityProcessTracker gains hasVisibleNotPinnedActivity."
     ],
     "preconditions": [
      "An activity start is evaluated by BackgroundActivityStartController (a BalState built for a calling uid / real calling uid), or by checkProcessAllowsBal -> WindowProcessController.areBackgroundActivityStartsAllowed with a non-FGS BalCheckConfiguration (per index search).",
      "The calling or real calling uid has a visible activity only in pinned (PIP) windowing mode, and appSwitchAllowedOrFg is true: the app switch state is APP_SWITCH_ALLOW or APP_SWITCH_FG_ONLY, or the caller is the home app. For the BackgroundLaunchProcessController path, hasActivityInVisibleTask is true and appSwitchState != APP_SWITCH_DISALLOW.",
      "No other exemption (permission, token, SAW, bound-by-foreground-uid, grace period, etc.) applies."
     ],
     "per_major": {
      "14": "Adapted fix (RESTRICT AUTOMERGE, 4ec3e4cea49d in android-security-14.0.0_r22). BackgroundActivityStartController computes callingUidHasAnyVisibleWindow / realCallingUidHasAnyVisibleWindow as hasVisibleNotPinnedActivity(uid) || mActiveUids.hasNonAppVisibleWindow(uid) instead of mService.hasActiveVisibleWindow(uid). BackgroundLaunchProcessController (the int-returning variant with boolean isCheckingForFgsStart) gains inPinnedWindow and also changes the app-switch condition to != APP_SWITCH_DISALLOW. VisibleActivityProcessTracker uses a local ALWAYS_TRUE predicate. Adds VisibleActivityProcessTrackerTests. At android-security-14.0.0_r29, 7/8 patched lines are present.",
      "15": "Adapted fix (RESTRICT AUTOMERGE, 483fba0b4777 in android-security-15.0.0_r10). A larger refactor: it replaces BalState.mCallingUidHasAnyVisibleWindow / mRealCallingUidHasAnyVisibleWindow with three fields (HasVisibleActivity / HasVisibleNotPinnedActivity / HasNonAppVisibleWindow) and adds a two-arg BalVerdict constructor. It patches both the balImproveRealCallerVisibilityCheck() branch and the legacy real-caller branch (hasVisibleNotPinnedActivity || hasNonAppVisibleWindow), and uses the boolean isCheckingForFgsStart signature of BackgroundLaunchProcessController. At android-security-15.0.0_r17, 7/8 patched lines are present.",
      "16": "Same fix (b96d7a515e9e in android-security-16.0.0_r1, a cherry-pick of accdba705e78 / 61eafea8fc14). It is followed by the test-only build fix c879fb5561f2 (r1) and that fix's revert 4d488a6a05ab (r3). At android-security-16.0.0_r8, 8/8 patched lines are present. The r8 context also differs from the analysed diff: the bound-by-foreground-uid path has a not-pinned predicate, and the tracker uses wpc.hasVisibleNotPinnedActivity(). The commits behind these differences are not in the material.",
      "17": "Unclear from the material. The resolver links 58822cb0d7e4 in android-17.0.0_r1, which is a 'RESTRICT AUTOMERGE Revert \"RESTRICT AUTOMERGE Ignore pinned Windows\"'. It touches an ActivityStarter.java-based BAL check and a boolean-returning BackgroundLaunchProcessController, a code shape that does not match the Android 16 files. At android-security-17.0.0_r1, 0/8 lines of the patched region are present. The bulletin lists 17-next as affected. The material does not show whether 17 carries an equivalent fix in restructured code."
     },
     "backport_notes": [
      "All production changes are in b96d7a515e9e. c879fb5561f2 and 4d488a6a05ab are test-only and cancel each other out.",
      "When porting, match the target's BackgroundLaunchProcessController.areBackgroundActivityStartsAllowed signature (BalCheckConfiguration record on 16 vs. boolean isCheckingForFgsStart on 14/15) and keep inPinnedWindow right after hasActivityInVisibleTask. Where the visibility check still uses mService.hasActiveVisibleWindow(uid), replace it with hasVisibleNotPinnedActivity(uid) || mActiveUids.hasNonAppVisibleWindow(uid), as the 14 and 15 backports do."
     ],
     "confidence": "high",
     "unknowns": [
      "How a pinned-window caller leads to the bulletin's 'DoS' classification. The material only shows the BAL exemption being tightened.",
      "Whether the bound-by-foreground-uid exemption, which this commit does not touch, was considered part of the same issue. The r8 context shows a not-pinned predicate there, but the commit that added it is not in the material.",
      "The actual state of Android 17. The linked commit is a revert in an unfamiliar code shape, and the patched region is absent at android-security-17.0.0_r1.",
      "Whether WindowProcessController.inPinnedWindowingMode(), as of this commit, correctly classifies a process that hosts both a pinned and a non-pinned visible activity."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48581": {
     "cve": "CVE-2025-48581",
     "summary": "Adds sub_dir: \"sysconfig\" to the apexd.mainline_patch_level_2 prebuilt_etc module in platform/system/apex so the com.google.android.mainline.patchlevel.2.xml feature declaration is installed under the sysconfig directory, where it becomes accessible to the device.",
     "component": "platform/system/apex, patchlevel (build rule for the mainline patch level feature declaration)",
     "weakness": "Build/installation misconfiguration: the feature declaration file was installed to a path the device does not read. The bulletin classifies the result as EoP, but the diff itself does not show the runtime consequence.",
     "root_cause": "patchlevel/Android.bp defined prebuilt_etc \"apexd.mainline_patch_level_2\" with src com.google.android.mainline.patchlevel.2.xml but no sub_dir, so the XML was installed directly under the etc directory instead of etc/sysconfig. Per the commit message, the device therefore could not access the feature it declares. In general, system feature declarations in XML are read from the etc/sysconfig directory (general platform knowledge), so a file placed one level up is not picked up.",
     "fix_analysis": [
      "patchlevel/Android.bp, module apexd.mainline_patch_level_2: adds `sub_dir: \"sysconfig\"` to the prebuilt_etc rule. This moves the install location of com.google.android.mainline.patchlevel.2.xml to the sysconfig subdirectory of the partition's etc directory. No other files change, and src and filename_from_src stay the same.",
      "The 16 commit 117f34be6322 is a cherry-pick (cherry picked from 8b89163d, Cherrypick-From 9c3d1d96). Commit 8b89163d61b8, present in android-17.0.0_r1, has identical content. The commit message says it was tested with GtsOsTestCases."
     ],
     "completeness": [
      "The fix only changes where the single XML file is installed. It does not change the XML content, apexd, or any runtime consumer. The material does not show those consumers, so the code does not tell us whether anything else depended on the old location.",
      "At the fixed tag, apexd.mainline_patch_level_2 is the only module in patchlevel/Android.bp, so no sibling module in this file can have the same missing sub_dir."
     ],
     "behavior_changes": [
      "com.google.android.mainline.patchlevel.2.xml is now installed under etc/sysconfig/ instead of etc/ on the target partition. Per the commit message, the feature it declares becomes accessible to the device."
     ],
     "preconditions": [
      "The device build includes the apexd.mainline_patch_level_2 module (Android 16 or later; patchlevel/Android.bp is absent at the latest Android 14 and 15 security tags).",
      "The material does not show the runtime trigger conditions: unknown."
     ],
     "per_major": {
      "14": "not affected per the bulletin; patchlevel/Android.bp is absent at android-security-14.0.0_r29 (code absent).",
      "15": "not affected per the bulletin; patchlevel/Android.bp is absent at android-security-15.0.0_r17 (code absent).",
      "16": "same fix: commit 117f34be6322 (cherry-picked from 8b89163d) in android-security-16.0.0_r7; at android-security-16.0.0_r8 all 3 lines of the patched region are present.",
      "17": "same fix: commit 8b89163d61b8 (identical diff, linked by related_bug) in android-17.0.0_r1; at android-security-17.0.0_r1 all 3 lines of the patched region are present."
     },
     "backport_notes": [
      "A single one-line Soong change with no dependencies. It only applies to trees that contain patchlevel/Android.bp with the apexd.mainline_patch_level_2 module. Forks that install the XML some other way (e.g. PRODUCT_COPY_FILES) should check that the file ends up in etc/sysconfig and not in etc."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which runtime component reads com.google.android.mainline.patchlevel.2.xml, or the concrete EoP consequence of the feature being missing.",
      "The content of com.google.android.mainline.patchlevel.2.xml and the partition or APEX the module is packaged into are not shown.",
      "Neither the diff nor the commit message explains why bug 436433817 was rated High EoP.",
      "Whether 8b89163d or 9c3d1d96 is the original upstream commit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48595": {
     "cve": "CVE-2025-48595",
     "summary": "The fix is a bundled SQLite version bump. On Android 16 the 3.44.5 amalgamation was installed under external/sqlite and selected through the RELEASE_PACKAGE_LIBSQLITE3 release flag (3440300 to 3440500), but the flag flip was reverted one security tag later because of a performance regression. Android 14 instead upgrades its single-version external/sqlite tree from 3.39.2 to 3.39.6.",
     "component": "platform/external/sqlite (libsqlite3 amalgamation, dist/sqlite-autoconf-*) and platform/build/release (RELEASE_PACKAGE_LIBSQLITE3 flag)",
     "weakness": "unknown",
     "root_cause": "The material does not identify the specific defective function. The fix moves the bundled third-party SQLite library to a newer upstream patch release: 3.44.3 to 3.44.5 on Android 16 and 3.39.2 to 3.39.6 on Android 14, both tied to bugs 430889718 and 281064726. The Android 14 diff shows the upstream changes between 3.39.2 and 3.39.6. These include several robustness changes: a new bounds check in modifyPagePointer that returns SQLITE_CORRUPT_PAGE when pCell+4 exceeds usableSize, a rewrite of sibling-page reordering in balance_nonroot, sqlite3PcacheMove dropping an existing page at the new page number, propagation of OOM errors to outer Parse objects in sqlite3OomFault, and mutex protection of sqlite3_temp_directory. The material does not say which of these corresponds to this CVE.",
     "fix_analysis": [
      "platform/external/sqlite cbebed38b579 (android-security-16.0.0_r3) adds dist/sqlite-autoconf-3440500/. It contains orig/ (pristine upstream sqlite3.c, sqlite3.h, sqlite3ext.h and shell.c from tarball a881857822, version 3.44.5), the Android-patched copies, Android.patch, METADATA and README.version. This commit only installs sources. Its message says it does not change which version is built.",
      "platform/build/release b11607ea3ec7 (android-security-16.0.0_r3) changes string_value in flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto from \"3440300\" to \"3440500\", so the ap3a release config selects the new 3.44.5 tree.",
      "platform/build/release 948406421293 (android-security-16.0.0_r4) reverts the flag to \"3440300\" (stated reason: performance regression), so ap3a again selects 3.44.3.",
      "Android.patch in the 3.44.5 tree carries the Android-specific changes reapplied on top of upstream: fdsan owner tags in the unix VFS open path and in robust_close, unixLogError on fstat failures, SQLITE_CORRUPT_BKPT instead of SQLITE_ERROR on the 'unsupported file format' schema-init path, fts3_tokenizer registration disabled under ANDROID, optional fts1/fts2 aliases, and register_localized_collators/register_android_functions in shell.c."
     ],
     "completeness": [
      "The fix is a whole-library version bump, not a targeted patch. All upstream changes between 3.44.3 and 3.44.5 (or between 3.39.2 and 3.39.6) come in together.",
      "On Android 16 the flag flip was reverted in android-security-16.0.0_r4. At android-security-16.0.0_r8, flag_values/ap3a/RELEASE_PACKAGE_LIBSQLITE3.textproto still reads \"3440300\" and the dist/sqlite-autoconf-3440500 files are reported as not present. From the material, the ap3a build at the latest Android 16 security tag therefore selects the pre-fix 3.44.3 tree, unless 3.44.3 was patched in place (not shown).",
      "The commit message says 25Q4 explicitly selects a newer sqlite, so the ap3a flag does not affect it. These commits do not touch any other release config."
     ],
     "behavior_changes": [
      "The reported sqlite version changes: 3.44.5 on ap3a while the flag was set, and 3.39.6 on Android 14.",
      "On Android 14, the 3.39.6 changes alter some error outcomes. For example, modifyPagePointer returns SQLITE_CORRUPT_PAGE for a cell pointer that extends past usableSize, sqlite3_strglob/sqlite3_strlike accept NULL arguments, and temp-directory access is serialized by a static mutex.",
      "Selecting 3.44.5 caused a performance regression and was reverted on Android 16."
     ],
     "preconditions": [
      "The process must link libsqlite3 built from the affected amalgamation (RELEASE_PACKAGE_LIBSQLITE3 = 3440300 on Android 16 ap3a, or the 3.39.2 tree on Android 14).",
      "The triggering input (SQL, database file or API sequence) is not shown in the material: unknown."
     ],
     "per_major": {
      "14": "Adapted fix: platform/external/sqlite 2abdb383b72c (android-security-14.0.0_r24) upgrades the single-version dist/ tree in place from 3.39.2 to 3.39.6 (tarball 642be27a38), updating METADATA, README.version, Android.patch and the amalgamation. No release flag is involved. At android-security-14.0.0_r29, 3/8 lines of the patched region are present.",
      "15": "Resolved via platform/build/release df01bf9a7498 (android-security-15.0.0_r12), which deletes flag_values/trunk_staging/RELEASE_PACKAGE_LIBSQLITE3.textproto (value 3440300). The material does not show which sqlite version Android 15 selects as a result. At android-security-15.0.0_r17, 2/2 lines of the patched region are present.",
      "16": "Same fix in two parts at android-security-16.0.0_r3: install the 3.44.5 sources in external/sqlite, then flip the ap3a flag to 3440500. The flag flip was reverted at android-security-16.0.0_r4 for a performance regression. At android-security-16.0.0_r8 the flag is 3440300 and, per the material, the 3.44.5 source directory is not present.",
      "17": "Resolved in base at android-17.0.0_r1. The linked commit platform/external/sqlite e13117e12f88 removes the dist/sqlite-autoconf-3500100 (3.50.1) tree. The material does not show which sqlite version is selected."
     },
     "backport_notes": [
      "Branches that use the release-flag scheme need both parts, in order. First land the new dist/sqlite-autoconf-<ver>/ tree in external/sqlite, with Android.patch reapplied to the new upstream amalgamation. Then set RELEASE_PACKAGE_LIBSQLITE3 for the target release config in build/release flag_values. Branches without the flag (the Android 14 layout) upgrade dist/ in place.",
      "The 3.44.5 selection was reverted on Android 16 for a performance regression. A backport should check performance and confirm which sqlite version the target branch actually selects, since the Android 16 security tags end up back on 3440300."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which upstream SQLite change between the old and new versions fixes this CVE, and the vulnerable function or input.",
      "Whether the 3.44.3 tree on Android 16 was patched in place after the revert, or whether Android 16 stays on the pre-fix library at android-security-16.0.0_r8.",
      "Which sqlite version Android 15 selects after the trunk_staging flag file was removed, and which version Android 17 selects.",
      "Why only 3/8 lines of the Android 14 patched region match at android-security-14.0.0_r29."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48612": {
     "cve": "CVE-2025-48612",
     "summary": "Settings' NFC default payment app picker stops parsing a ComponentName and user id out of the preference key string. It now looks the key up in a map of the payment apps it enumerated itself, so only a known candidate can be set as default.",
     "component": "platform/packages/apps/Settings, NFC default payment app selector (com.android.settings.nfc.DefaultPaymentSettings / PaymentBackend)",
     "weakness": "Improper input validation: a string key was parsed directly into a ComponentName and user id and passed to setDefaultPaymentApp without being checked against the legitimate candidate set (the commit calls it string injection).",
     "root_cause": "In src/com/android/settings/nfc/DefaultPaymentSettings.java, getDefaultKey() encoded the candidate as \"componentName.flattenToString() + ' ' + userId\". setDefaultKey(String key) reversed that with key.split(\" \"), ComponentName.unflattenFromString(keys[0]) and Integer.parseInt(keys[1]), then called mPaymentBackend.setDefaultPaymentApp(component, userId) whenever the key had at least two parts. Nothing checked that the decoded component/user pair matched one of the PaymentAppInfo entries returned by PaymentBackend.getPaymentAppInfos(). So a crafted key string reaching setDefaultKey could make Settings set an arbitrary component for an arbitrary user id as the NFC default payment app. The commit title calls this a \"string injection in default payment app selector\", and the bulletin rates it High EoP.",
     "fix_analysis": [
      "src/com/android/settings/nfc/PaymentBackend.java: PaymentAppInfo gains getKey(), which returns Integer.toString(hashCode()). It also gains hashCode()/equals() overrides on (componentName, userHandle), using Objects.hash for the hash. The key is now an opaque token that cannot be parsed back into a component and user.",
      "src/com/android/settings/nfc/DefaultPaymentSettings.java: mAppInfos changes from List<PaymentAppInfo> to Map<String, PaymentAppInfo>. onAttach() builds it with getPaymentAppInfos().stream().collect(Collectors.toMap(PaymentAppInfo::getKey, Function.identity())).",
      "DefaultPaymentSettings.getDefaultKey(): returns defaultAppInfo.getKey() instead of the flattened 'component userId' string, or null if there is no default. It is annotated @SuppressWarnings(\"NullAway\").",
      "DefaultPaymentSettings.setDefaultKey(String key): looks the key up in mAppInfos. If the key is absent it returns true and does nothing. Otherwise it calls mPaymentBackend.setDefaultPaymentApp(appInfo.componentName, appInfo.userHandle.getIdentifier()) with the PaymentAppInfo that Settings enumerated itself. The split/unflatten/parseInt of the incoming string is removed, and the android.content.ComponentName import is dropped.",
      "DefaultPaymentSettings.getCandidates(): iterates mAppInfos.values() instead of the list.",
      "Android 16 history: the fix landed as aa744e8988f0 (android-security-16.0.0_r3). It was reverted by f2babe193c76 ('revert needed to fix regression') and relanded with the same changes by 46ddb9d440ca (Revert^2); both of those are in android-security-16.0.0_r7. android-security-16.0.0_r8 carries the relanded code."
     ],
     "completeness": [
      "The fix removes the only parsing site in DefaultPaymentSettings (setDefaultKey). The setter can now only be called with a component/user pair that PaymentBackend.getPaymentAppInfos() produced.",
      "The lookup key is the decimal form of Objects.hash(componentName, userHandle), not the identity itself. If two distinct candidates had colliding hashes, Collectors.toMap would throw IllegalStateException in onAttach (duplicate key). A supplied key that equals a legitimate candidate's hash selects that legitimate candidate.",
      "The map is filled in onAttach(). The diff does not show whether it is refreshed on onPaymentAppsChanged, so a candidate added after attach may be ignored by setDefaultKey.",
      "The fix only adds validation on the Settings side. The diff does not touch PaymentBackend.setDefaultPaymentApp or the NFC CardEmulation side, and the material does not show whether those do their own checks."
     ],
     "behavior_changes": [
      "Preference keys for payment candidates are now opaque hash strings instead of 'flattenedComponent userId'. Anything that relied on the old key format no longer matches.",
      "setDefaultKey with an unrecognised key now does nothing and returns true. Before, it tried to set whatever the key encoded, and malformed input could throw from Integer.parseInt.",
      "PaymentAppInfo now has value equality on (componentName, userHandle).",
      "onAttach can throw IllegalStateException if getPaymentAppInfos() returns two entries with the same key, that is, the same (componentName, userHandle) or a hash collision.",
      "getCandidates iterates the Map's values, so the original list order is not necessarily kept.",
      "On the Android 16 branch the fix was reverted for an unspecified regression and then relanded with the same changes."
     ],
     "preconditions": [
      "The DefaultPaymentSettings fragment (the NFC default payment app picker, a DefaultAppPickerFragment subclass in Settings) must be reached, and its setDefaultKey must be called with a key string the caller can influence. The material does not show the external entry point."
     ],
     "per_major": {
      "14": "same fix: platform/packages/apps/Settings c90fee1f1ea4 in android-security-14.0.0_r24, identical diff to aa744e8988f0; 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/packages/apps/Settings adac3d79c0f3 in android-security-15.0.0_r12, identical diff; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: aa744e8988f0 in android-security-16.0.0_r3 (backwards compatible port of ag/35084316). It was then reverted (f2babe193c76, regression) and relanded with the same changes (46ddb9d440ca, Revert^2); both are in android-security-16.0.0_r7. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: 1553d28f0f57 present in the base android-17.0.0_r1, identical diff; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Self-contained change touching only DefaultPaymentSettings.java and PaymentBackend.java. The same changed lines appear on 14, 15, 16 and 17. On 16 the reland 46ddb9d440ca applies to a different DefaultPaymentSettings.java blob (d75844bb0ff -> b5057ed64f2) than the original commit. Make sure the final state is the Revert^2 (46ddb9d440ca), not the intermediate revert f2babe193c76. The material does not describe the regression, so retest the payment picker after applying."
     ],
     "confidence": "high",
     "unknowns": [
      "How a crafted key string would reach DefaultPaymentSettings.setDefaultKey (the untrusted entry point is not shown).",
      "What regression caused the Android 16 revert f2babe193c76, and why relanding the same change was acceptable.",
      "Whether PaymentBackend.setDefaultPaymentApp or the NFC service does its own validation of component/user.",
      "Contents of the original internal change ag/35084316.",
      "Device requirements, such as NFC card emulation support, for the payment settings UI to be reachable."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48615": {
     "cve": "CVE-2025-48615",
     "summary": "Adds a 1024-character cap on the combined package-plus-class name of a media button receiver in MediaButtonReceiverHolder. The first commit covers receivers resolved from a PendingIntent; the second covers receivers set directly as a ComponentName.",
     "component": "platform/frameworks/base, system_server media session service (services/core/java/com/android/server/media/MediaButtonReceiverHolder.java)",
     "weakness": "Missing length/bounds check on caller-supplied component name (improper input validation)",
     "root_cause": "MediaButtonReceiverHolder accepted a media button receiver component name of any length. In getComponentName(PendingIntent, componentType), the resolved ComponentInfo packageName and name were turned into a ComponentName with no size check. create(int userId, ComponentName broadcastReceiver) stored whatever ComponentName it was given. Per the class comments, the receiver's component name is written to persistent storage so the receiver can be restored across boot. The first commit message says the case \"can't be tested on main as PackageManager prevents it\". In the code shown, the direct ComponentName path performs no PackageManager resolution before building the holder. The material does not show what goes wrong downstream when a name is too long.",
     "fix_analysis": [
      "Commit a5795fc0cf1f (android-security-16.0.0_r3), MediaButtonReceiverHolder.java: adds private static final int MAX_COMPONENT_NAME_LENGTH = 1024. The new check is in getComponentName(PendingIntent, int componentType). It runs once a ResolveInfo's ComponentInfo matches the PendingIntent creator package and has non-null packageName and name. It computes packageName.length() + name.length() + 1. If the result is greater than 1024, it logs 'detected and ignored component name with overly long package or name' and moves on to the next ResolveInfo instead of returning that ComponentName.",
      "Commit bb15e7f14f63 (android-security-16.0.0_r4), same file: adds private static boolean componentNameTooLong(ComponentName), which applies the same packageName + className + 1 > MAX_COMPONENT_NAME_LENGTH test. create(int userId, ComponentName broadcastReceiver), the setMediaButtonBroadcastReceiver path per the commit message, now throws IllegalArgumentException(\"receiver name too long\") before constructing the holder. getComponentName(PendingIntent, int) is refactored to build the ComponentName first and pass it to componentNameTooLong, keeping the log-and-continue behavior.",
      "Together the two commits gate both create() factory methods that build a holder from a ComponentName. unflattenFromString, which also builds a holder from a parsed ComponentName, is not modified."
     ],
     "completeness": [
      "Covered: both the PendingIntent-resolved path (getComponentName) and the explicit ComponentName path (create(int, ComponentName)) enforce the 1024 limit on package name plus class name plus separator.",
      "The PendingIntent path only skips the oversized candidate. If no other candidate resolves, create(int, PendingIntent, String) falls back to a holder built from sessionPackageName with COMPONENT_TYPE_INVALID. That holder has no length check, but per the code comment the package name has already been validated as belonging to the session owner.",
      "unflattenFromString(Context, String) rebuilds a holder from a persisted string via ComponentName.unflattenFromString(tokens[0]). Neither commit modifies it, and the lines shown (85-94) contain no length check. The rest of the method is not shown.",
      "The limit counts Java String length (UTF-16 code units), not encoded byte size. The material does not show whether this matches the limit of whatever consumes the name.",
      "Both checks use a hardcoded constant and the commits are marked Flag: EXEMPT, so the behavior applies unconditionally once the commits are present."
     ],
     "behavior_changes": [
      "A PendingIntent-based receiver whose resolved component name is longer than 1024 characters is ignored with a Log.w. The session may then get an unresolvable holder (COMPONENT_TYPE_INVALID, package set to the session package) instead of the intended component.",
      "create(int, ComponentName) now throws IllegalArgumentException(\"receiver name too long\") for oversized names. The material does not show how that exception reaches the calling app.",
      "No change for component names of 1024 characters or fewer."
     ],
     "preconditions": [
      "The caller owns a MediaSession and sets a media button receiver, either via a PendingIntent (setMediaButtonReceiver) that resolves to a component in the creator package, or via setMediaButtonBroadcastReceiver(ComponentName). Both APIs are named in the second commit message.",
      "For the PendingIntent path, the ComponentInfo must resolve with packageName equal to the PendingIntent creator package. For the ComponentName path, the code shown does no PackageManager resolution before the check.",
      "The component name (package plus class) must exceed 1024 characters to be rejected. The first commit says this case cannot be tested on main because PackageManager prevents it."
     ],
     "per_major": {
      "14": "Same fix: the first commit, a1bbb371980e (diff identical to a5795fc0cf1f), is in android-security-14.0.0_r24, and 8/8 lines of the patched region are present at android-security-14.0.0_r29. The material does not show whether the second commit (ComponentName path) was applied to 14.",
      "15": "Same fix: the first commit, 8f8c16cee29b (identical diff), is in android-security-15.0.0_r12, and 8/8 lines are present at android-security-15.0.0_r17. The material does not show whether the second commit is on 15.",
      "16": "Same fix, both commits: a5795fc0cf1f in android-security-16.0.0_r3 and bb15e7f14f63 in android-security-16.0.0_r4. The code context at android-security-16.0.0_r8 shows componentNameTooLong in create(int, ComponentName), so both are present.",
      "17": "Same fix: the second commit, 013bb57034ee, landed in android-17.0.0_r1. Its pre-image index ffa2bcbd6fd2 is the first commit's post-image, so the tree already contained the first commit. 6/8 lines of the patched region are present at android-security-17.0.0_r1, which fits the second commit's refactor of the check lines."
     },
     "backport_notes": [
      "Apply the commits in order. bb15e7f14f63 depends on MAX_COMPONENT_NAME_LENGTH and rewrites check lines added by a5795fc0cf1f. With only the first commit, create(int, ComponentName) stays unchecked, so verify the second commit on the 14 and 15 branches, where the material shows only the first.",
      "Both changes are confined to MediaButtonReceiverHolder.java. The first commit's pre-image is index e7e70fc1f7b2; the second's is ffa2bcbd6fd2, the first commit's post-image."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete consequence of an overly long component name (which consumer fails, and how that leads to EoP) is not shown in the material.",
      "Whether the second commit (ComponentName path) was applied to the Android 14 and 15 security branches.",
      "How the new IllegalArgumentException from create(int, ComponentName) reaches the binder caller. The callers are not shown, and the index search found none.",
      "The rest of unflattenFromString beyond line 94, and whether its input can contain names longer than 1024 characters.",
      "Why 1024 was chosen as the limit."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48616": {
     "cve": "CVE-2025-48616",
     "summary": "KeyguardViewMediator now queries KeyguardUpdateMonitor.isUserInLockdown (the strong auth tracker) instead of LockPatternUtils.isUserInLockdown at three places that decide whether lockdown forces the keyguard to show, so lockdown is honored even when the keyguard is externally disabled (e.g. app pinning).",
     "component": "platform/frameworks/base, SystemUI keyguard (KeyguardViewMediator / KeyguardUpdateMonitorCallback)",
     "weakness": "Inconsistent source of truth for security state: lockdown was checked against a LockPatternUtils view that could be out of sync with the StrongAuthTracker that drives the callback, so the keyguard could fail to show under lockdown",
     "root_cause": "KeyguardViewMediator reacted to KeyguardUpdateMonitorCallback.onStrongAuthStateChanged, which KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged dispatches (per index search). It then decided whether the user is in lockdown by calling mLockPatternUtils.isUserInLockdown(). Per the commit message, LockPatternUtils and the StrongAuthTracker can be out of sync, so LockPatternUtils could report \"not in lockdown\" when the callback ran. In that case three things went wrong: onStrongAuthStateChanged did not call doKeyguardLocked; setKeyguardEnabled(false) was not overridden by lockdown; and the doKeyguard path returned early with \"not showing because externally disabled\" when mExternallyEnabled was false. With the keyguard externally disabled (e.g. a pinned app) and lockdown triggered, the keyguard could therefore fail to show. The commit's test is to pin an app, lock down the device, and observe that the keyguard always shows.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java: the onStrongAuthStateChanged override of KeyguardViewMediator's KeyguardUpdateMonitorCallback now checks mUpdateMonitor.isUserInLockdown(mSelectedUserInteractor.getSelectedUserId()) instead of mLockPatternUtils.isUserInLockdown(...) before calling doKeyguardLocked(null).",
      "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java: in setKeyguardEnabled, the 'keyguardEnabled(false) overridden by user lockdown' early return (when !enabled && mShowing) now uses mUpdateMonitor.isUserInLockdown(...).",
      "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java: in the doKeyguard path, the 'if another app is disabling us, don't show' condition (!mExternallyEnabled && !isUserInLockdown) now uses mUpdateMonitor.isUserInLockdown(...). An external disable therefore no longer suppresses the keyguard when KeyguardUpdateMonitor reports lockdown.",
      "packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java: the javadoc of onStrongAuthStateChanged now tells implementers to use KeyguardUpdateMonitor (e.g. KeyguardUpdateMonitor#isUserInLockdown(int)) as the source of truth for strong auth state.",
      "packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java: two lockdown tests, including onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally, now stub mUpdateMonitor.isUserInLockdown instead of mLockPatternUtils.isUserInLockdown."
     ],
     "completeness": [
      "The three isUserInLockdown call sites changed by the diff (onStrongAuthStateChanged, setKeyguardEnabled, the doKeyguard path) are switched to KeyguardUpdateMonitor. The fix only changes which source is consulted; it adds no new state or synchronization.",
      "Only KeyguardViewMediator changes. The javadoc update is guidance for other KeyguardUpdateMonitorCallback implementers. The material does not show whether other SystemUI code still consults LockPatternUtils.isUserInLockdown from a strong auth callback.",
      "The underlying desynchronization between LockPatternUtils and the StrongAuthTracker is not addressed. The fix avoids it by always using KeyguardUpdateMonitor inside KeyguardViewMediator."
     ],
     "behavior_changes": [
      "Under lockdown, onStrongAuthStateChanged calls doKeyguardLocked based on KeyguardUpdateMonitor state, even when the keyguard is externally disabled (e.g. app pinning).",
      "Under lockdown, setKeyguardEnabled(false) while the keyguard is showing is refused based on KeyguardUpdateMonitor state rather than LockPatternUtils state.",
      "KeyguardViewMediator tests must mock KeyguardUpdateMonitor.isUserInLockdown rather than LockPatternUtils.isUserInLockdown."
     ],
     "preconditions": [
      "Keyguard externally disabled (mExternallyEnabled == false), e.g. an app pinned, as in the commit's reproduction steps.",
      "User triggers lockdown, producing a strong auth state change that KeyguardUpdateMonitor.notifyStrongAuthAllowedChanged dispatches to KeyguardViewMediator via onStrongAuthStateChanged (per index search).",
      "LockPatternUtils.isUserInLockdown out of sync with the strong auth tracker when the callback runs. The commit message describes this; the material does not show the exact timing window."
     ],
     "per_major": {
      "14": "adapted fix: commit 991998d5464f in android-security-14.0.0_r24. Same three call sites and the same javadoc change, but it uses KeyguardUpdateMonitor.getCurrentUser() instead of mSelectedUserInteractor.getSelectedUserId(). At android-security-14.0.0_r29 the resolver finds 6/8 lines of the patched region.",
      "15": "same fix: commit 9adff3df0285 in android-security-15.0.0_r12, with content matching the Android 16 change. At android-security-15.0.0_r17 the resolver finds 6/8 lines of the patched region.",
      "16": "same fix: commit aa9fbb1b74eb in android-security-16.0.0_r3 (cherry-pick of 5c25e76eb886). Code at android-security-16.0.0_r8 shows mUpdateMonitor.isUserInLockdown in onStrongAuthStateChanged and the updated test. The resolver finds 6/8 lines of the patched region.",
      "17": "adapted fix: commit 52a6d6dfda83 (RESTRICT AUTOMERGE) in android-17.0.0_r1. Like the Android 14 variant, it uses KeyguardUpdateMonitor.getCurrentUser(). At android-security-17.0.0_r1 the resolver finds 6/8 lines of the patched region."
     },
     "backport_notes": [
      "Single self-contained commit. Across branches the only adaptation is the user-id accessor: KeyguardUpdateMonitor.getCurrentUser() in the 14 and 17 diffs, mSelectedUserInteractor.getSelectedUserId() in 15 and 16. The fix relies on KeyguardUpdateMonitor exposing isUserInLockdown(int) and being available as mUpdateMonitor in KeyguardViewMediator.",
      "When checking a fork, verify all three KeyguardViewMediator call sites directly. The resolver's 6/8 partial match does not identify which lines are missing."
     ],
     "confidence": "high",
     "unknowns": [
      "The implementations of KeyguardUpdateMonitor.isUserInLockdown and LockPatternUtils.isUserInLockdown, and the exact mechanism by which they get out of sync, are not shown.",
      "The material does not explain why the bulletin classifies this as ID (information disclosure). The fix concerns the keyguard failing to show under lockdown.",
      "It is not shown whether other KeyguardUpdateMonitorCallback implementers in SystemUI still consult LockPatternUtils for lockdown.",
      "Which 2 of the 8 patched-region lines the resolver did not match on each branch is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48648": {
     "cve": "CVE-2025-48648",
     "summary": "NotificationManagerService stops using PackageManagerInternal.isSameApp(pkg, uid, userId) for package-ownership checks. It now compares the caller uid against the package's real uid with UserHandle.isSameApp. It also rejects SDK sandbox uids in areNotificationsEnabledForPackage, resolves USER_ALL to the caller's user, and moves the system/shell check ahead of canNotifyAsPackage in getConversationNotificationChannel.",
     "component": "platform/frameworks/base, services/core NotificationManagerService (notification ownership and caller checks)",
     "weakness": "Improper caller/package ownership validation (overly permissive same-app check that includes an SDK sandbox exception)",
     "root_cause": "NotificationManagerService checked whether a package belongs to a calling uid with mPackageManagerInternal.isSameApp(pkg, uid, userId). Per the commit message, that check carried an SDK sandbox exception and was not a strict uid comparison. The final ownership check (the method that throws \"Package X is not owned by uid Y\"), the Wellbeing check for restricted AutomaticZenRule types, and getEffectsSuppressor all relied on it. resolveNotificationUid also mapped USER_ALL to USER_SYSTEM rather than to the caller's user. areNotificationsEnabledForPackage did not treat SDK sandbox uids specially. The commit message does not explain how these flaws lead to the bulletin's DoS rating.",
     "fix_analysis": [
      "NotificationManagerService.java (ownership check that throws SecurityException \"Package ... is not owned by uid ...\"): the check is now !UserHandle.isSameApp(uid, mPackageManagerInternal.getPackageUid(pkg, 0L, userId)). It only compares uids and no longer grants the sandbox exception.",
      "NotificationManagerService.java areNotificationsEnabledForPackage: returns false at the start if Process.isSdkSandboxUid(uid), before enforceSystemOrSystemUIOrSamePackage runs.",
      "NotificationManagerService.java getConversationNotificationChannel: the condition now evaluates isCallerSystemOrSystemUiOrShell() before canNotifyAsPackage(...), so system, SystemUI or shell callers skip the package check.",
      "NotificationManagerService.java addAutomaticZenRule path (Wellbeing check) and getEffectsSuppressor: mPackageManagerInternal.isSameApp(...) is replaced with the service's isCallerSameApp(...) helper.",
      "NotificationManagerService.java resolveNotificationUid: USER_ALL now maps to UserHandle.getUserId(callingUid) rather than USER_SYSTEM.",
      "Tests: UiServiceTestCase makes the UID_* constants user-relative and stubs mPmi.getPackageUid. NotificationManagerServiceTest drops the overly broad isSameApp mock (b/317957802) and its own PackageManagerInternal mock. PreferencesHelperTest is updated. The follow-up commits fd6f923dc9c2 and 3bbdaf62767b only rename mockZenModeHelper/zenHelper to zenModeHelper so the Android 16 test cherry-pick compiles."
     ],
     "completeness": [
      "All isSameApp calls visible in the diff are replaced in NotificationManagerService.java. The material does not show whether other services still call PackageManagerInternal.isSameApp with the sandbox exception.",
      "On Android 14 and 17 the fix only changes areNotificationsEnabledForPackage, getConversationNotificationChannel (plus a rename of isCallerIsSystemOrSysemUiOrShell) and the ownership check. The Wellbeing, getEffectsSuppressor and resolveNotificationUid hunks are not in those diffs. The material does not show whether that code exists on those branches."
     ],
     "behavior_changes": [
      "areNotificationsEnabledForPackage returns false for SDK sandbox uids.",
      "Ownership checks throw SecurityException in cases the old isSameApp accepted, such as an SDK sandbox uid acting for its host package.",
      "Notifications posted for USER_ALL resolve the uid in the caller's user, not in user 0.",
      "System, SystemUI and shell callers of getConversationNotificationChannel no longer run canNotifyAsPackage first."
     ],
     "preconditions": [
      "The caller reaches INotificationManager binder methods. For example, NotificationManager.getNotificationChannel calls getConversationNotificationChannel (per index search).",
      "Settings NotificationBackend.getNotificationsBanned calls the binder areNotificationsEnabledForPackage (per index search). ActivityManager ServiceRecord.run calls a same-named method on NotificationManagerInternal (per index search); the material does not show whether that path goes through the patched binder method.",
      "The material does not show the exact trigger conditions behind the DoS rating."
     ],
     "per_major": {
      "14": "Adapted fix, 699e4677367e in android-security-14.0.0_r25: sandbox early return, reordered getConversationNotificationChannel check (with the isCallerIsSystemOrSysemUiOrShell rename), and the UserHandle.isSameApp ownership check. No Wellbeing, getEffectsSuppressor or resolveNotificationUid hunks. Patched region present at r29.",
      "15": "Same production fix as 16, 3d502bcba495 in android-security-15.0.0_r13: all NotificationManagerService hunks, including Wellbeing, getEffectsSuppressor and resolveNotificationUid. Present at r17.",
      "16": "b88956f3198d in android-security-16.0.0_r4, followed by the test-only build fixes fd6f923dc9c2 and 3bbdaf62767b. Present at r8.",
      "17": "Adapted fix like 14, 44ee96106f25 in android-17.0.0_r1. The test side also adds compatibility-device-util-axt-minus-dexmaker to services/tests/uiservicestests/Android.bp, a UserManagerInternal mock and a setUpMockZenTest helper. Present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Only the NotificationManagerService.java changes affect production code. The large test rewrite depends on UiServiceTestCase fields (mPmi, mUid, mUserId, user-relative UID_*), on setUpMockZenTest and on SystemUtil from compatibility-device-util. On 16 it needed two follow-up rename fixes (fd6f923dc9c2, 3bbdaf62767b) to compile, so apply those after the main commit."
     ],
     "confidence": "medium",
     "unknowns": [
      "How the flaw produces the bulletin's DoS impact.",
      "Which of the hunks is the essential security fix and which are related hardening.",
      "The name of the ownership-check method around line 11859 (the diff context does not show it).",
      "Whether 14 and 17 contain the Wellbeing, getEffectsSuppressor and USER_ALL code paths that 15 and 16 changed.",
      "Whether NotificationManagerInternal.areNotificationsEnabledForPackage (used by ServiceRecord) is affected by the sandbox check."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-opus-5-5",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48649": {
     "cve": "CVE-2025-48649",
     "summary": "Threads a restorePregrantedPermissions flag from ActivityManager.clearApplicationUserData() through AMS, PMS and both permission backends. An app clearing its own data no longer gets default- or role-granted runtime permissions re-granted. Privileged callers (CLEAR_APP_USER_DATA) keep the restoring behaviour through a separate path.",
     "component": "platform/frameworks/base, ActivityManagerService / PackageManagerService / permission service (resetRuntimePermissions)",
     "weakness": "Improper privilege management: an unprivileged self data-clear path reused a privileged runtime-permission reset that re-grants default/role permissions (missing authorization gate on a privilege-restoring side effect)",
     "root_cause": "ActivityManager.clearApplicationUserData() (the public no-arg API an app uses on itself) and the hidden two-arg API both ended in ActivityManagerService.clearApplicationUserData. That method permits the call without any permission when applicationInfo.uid == callingUid. It then calls IPackageManager.clearApplicationUserData, and PackageManagerService.clearApplicationUserDataLIF unconditionally calls mPermissionManager.resetRuntimePermissions(pkg, userId). In PermissionManagerServiceImpl.resetRuntimePermissionsInternal, any runtime permission with FLAG_PERMISSION_GRANTED_BY_DEFAULT or FLAG_PERMISSION_GRANTED_BY_ROLE is re-granted through grantRuntimePermissionInternal as SYSTEM_UID. In AppIdPermissionPolicy.resetRuntimePermissions, permissions with the ROLE or PREGRANT flag get RUNTIME_GRANTED set. So an app could reverse a user's revocation of such permissions just by clearing its own data.",
     "fix_analysis": [
      "core/java/android/app/ActivityManager.java: adds a new private clearApplicationUserData(packageName, observer, restorePregrantedPermissions). The public self-clear clearApplicationUserData() passes false and routes to the new binder method IActivityManager.clearApplicationUserDataWithoutPermissionReset. The hidden two-arg clearApplicationUserData(packageName, observer) passes true and keeps using IActivityManager.clearApplicationUserData.",
      "core/java/android/app/IActivityManager.aidl: adds clearApplicationUserDataWithoutPermissionReset(packageName, keepState, observer, userId).",
      "services/core/java/com/android/server/am/ActivityManagerService.java: the clearApplicationUserData binder method forwards true, and the new clearApplicationUserDataWithoutPermissionReset forwards false. The internal clearApplicationUserData(..., isRestore, ..., restorePregrantedPermissions) now takes the own-uid shortcut only when restorePregrantedPermissions is false, so restoring pregrants requires CLEAR_APP_USER_DATA through checkComponentPermission. The flag is passed on to pm.clearApplicationUserData. LocalService.clearApplicationUserData passes true.",
      "core/java/android/content/pm/IPackageManager.aidl and services/core/java/com/android/server/pm/PackageManagerService.java: clearApplicationUserData gains a boolean restorePregrantedPermissions and keeps @EnforcePermission CLEAR_APP_USER_DATA. clearApplicationUserDataLIF passes the flag to mPermissionManager.resetRuntimePermissions(pkg, userId, restorePregrantedPermissions). core/java/android/app/ApplicationPackageManager.java passes true. core/java/android/content/pm/PackageManager.java adds @RequiresPermission(CLEAR_APP_USER_DATA) on the abstract method.",
      "services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java: resetRuntimePermissions and both resetRuntimePermissionsInternal overloads take restorePregrantedPermissions. The GRANTED_BY_DEFAULT/GRANTED_BY_ROLE branch calls grantRuntimePermissionInternal only when the flag is true. Otherwise that branch does nothing, so the permission is not re-granted and is not revoked by that branch. Three callers pass true: the uninstall-with-keep-data path, resetRuntimePermissionsForUser, and LegacyPermissionManagerService.Internal.resetRuntimePermissions.",
      "services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt: resetRuntimePermissions(packageName, userId, restorePregrants) ORs PermissionFlags.RUNTIME_GRANTED into ROLE/PREGRANT permissions only when restorePregrants is true. onPackageUninstalled passes true. In PermissionService.kt, resetRuntimePermissions forwards the flag and resetRuntimePermissionsForUser passes true.",
      "Interface plumbing: PermissionManagerServiceInterface, PermissionManagerServiceInternal, the internal implementation in PermissionManagerService, PermissionManagerServiceLoggingDecorator, PermissionManagerServiceTracingDecorator and PermissionManagerServiceTestingShim are updated to the new 3-arg signature. The tests CrossUserPackageVisibilityTests and AppIdPermissionPolicyPermissionResetTest are updated to pass true."
     ],
     "completeness": [
      "Both permission backends are covered: the legacy Java PermissionManagerServiceImpl.resetRuntimePermissionsInternal and the Kotlin AppIdPermissionPolicy.resetRuntimePermissions.",
      "The AMS gate is server-side. ActivityManagerService.clearApplicationUserData takes the own-uid shortcut only when restorePregrantedPermissions is false. An app that calls the original IActivityManager.clearApplicationUserData binder method directly on itself therefore does not get the own-uid permit and needs CLEAR_APP_USER_DATA.",
      "IPackageManager.clearApplicationUserData is still gated by CLEAR_APP_USER_DATA.",
      "The other resetRuntimePermissions callers in the diff all pass true and keep pre-fix behaviour: the uninstall keep-data path, resetRuntimePermissionsForUser and LegacyPermissionManagerService. On the clearApplicationUserData path, AMS LocalService and ApplicationPackageManager also pass true.",
      "When the flag is false, the default/role-granted branch leaves the permission's grant state as it is.",
      "The hunk shows only the else-branch assignment to permitted, not the ACCESS_INSTANT_APPS branch of the AMS permission check. Whether that branch interacts with the new flag is not visible in the material."
     ],
     "behavior_changes": [
      "An app calling ActivityManager.clearApplicationUserData() on itself no longer has previously revoked default- or role-granted runtime permissions re-granted.",
      "New hidden binder method IActivityManager.clearApplicationUserDataWithoutPermissionReset.",
      "IPackageManager.clearApplicationUserData gains an extra boolean, so any direct binder or test caller must be updated.",
      "Calling the original IActivityManager.clearApplicationUserData on one's own package is no longer permitted through the own-uid shortcut. It now requires CLEAR_APP_USER_DATA.",
      "The PermissionManagerServiceLoggingDecorator log line for resetRuntimePermissions now includes shouldRestorePregrants."
     ],
     "preconditions": [
      "Before the fix, the caller is an installed app that either invokes the public ActivityManager.clearApplicationUserData() on its own package (own-uid path, no permission required) or calls IActivityManager.clearApplicationUserData directly for its own uid.",
      "The app holds runtime permissions carrying FLAG_PERMISSION_GRANTED_BY_DEFAULT / FLAG_PERMISSION_GRANTED_BY_ROLE (legacy impl) or PermissionFlags.ROLE / PREGRANT (AppIdPermissionPolicy) whose grant the user had changed. In AppIdPermissionPolicy, permissions with SYSTEM_OR_POLICY_FIXED_MASK bits are skipped.",
      "Per index search, Settings AppStorageSettings.initiateClearUserData and Car Settings StorageApplicationActionButtonsPreferenceController.initiateClearUserData use the hidden two-arg ActivityManager.clearApplicationUserData(packageName, observer). After the fix that API passes true, so these callers keep the restoring behaviour."
     ],
     "per_major": {
      "14": "Adapted fix (c1f49d2c2cac, RESTRICT AUTOMERGE, android-security-14.0.0_r25). Same flag plumbing, with these differences: AMS has no isRestore overload, so the private 5-arg overload is added directly; PMS uses mContext.enforceCallingOrSelfPermission instead of the generated _enforcePermission; PermissionService.kt resetRuntimePermissions is a TODO stub, so only its signature changes and AppIdPermissionPolicy.kt is not touched; there are no TestingShim or TracingDecorator changes. Region present 8/8 at android-security-14.0.0_r29.",
      "15": "Adapted fix (452f0beb15a9, android-security-15.0.0_r13). Same plumbing as 16, but the AppIdPermissionPolicy.kt condition has no isSystemOrInstalled check and its else branch clears only RUNTIME_GRANTED. Region present 8/8 at android-security-15.0.0_r17.",
      "16": "Fix commit 7b9a906980a3 in android-security-16.0.0_r4 (cherry-pick of 8dba23c13100). Region present 8/8 at android-security-16.0.0_r8.",
      "17": "Resolved in base android-17.0.0_r1 (23da448adbb0, RESTRICT AUTOMERGE). Region present 8/8 at android-security-17.0.0_r1. The diff supplied for 17 closely matches the Android 14 diff (same line numbers and most blob hashes, with a few differences such as PackageManager.java and ActivityManagerService.java). That is unexpected for a 17 tree."
     },
     "backport_notes": [
      "IPackageManager.clearApplicationUserData and PermissionManagerServiceInterface/Internal.resetRuntimePermissions change signature. Every implementor and caller (decorators, TestingShim, LegacyPermissionManagerService, CrossUserPackageVisibilityTests, AppIdPermissionPolicyPermissionResetTest) must be updated in the same change or the build breaks. Pass true everywhere except the AMS self-clear route.",
      "On trees where PermissionService.kt resetRuntimePermissions is still a TODO stub (as in the 14 diff), only the signature needs updating and the AppIdPermissionPolicy.kt hunk does not apply. On trees where it is implemented (15/16), both the Java and Kotlin backends need the flag."
     ],
     "confidence": "high",
     "unknowns": [
      "Whether the ACCESS_INSTANT_APPS branch of the AMS permission check (not shown in the hunk) interacts with the new flag.",
      "Whether DevicePolicyManagerService.clearApplicationUserData reaches AMS through LocalService (true) or another route. The material shows only the DevicePolicyManager client side.",
      "Why the diff labelled as the Android 17 fix closely matches the Android 14 diff. The material shows nothing of the 17 tree beyond the 8/8 region check.",
      "Whether other user-settable flags are still reset when the flag is false. The rest of the reset logic is not shown in the hunks.",
      "Which specific permissions or roles were involved in the reported issue (bug 339109116 is not described)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48652": {
     "cve": "CVE-2025-48652",
     "summary": "PackageInstaller (both the legacy InstallStart activity and the v2 InstallRepository) now applies the unknown-sources user restrictions to ACTION_VIEW / ACTION_INSTALL_PACKAGE intent installs even when the caller holds INSTALL_PACKAGES. The only exemptions left are PackageInstaller-API (non-intent) installs by INSTALL_PACKAGES holders and privileged callers that set EXTRA_NOT_UNKNOWN_SOURCE.",
     "component": "platform/frameworks/base, packages/PackageInstaller (package installer UI, device policy restriction checks)",
     "weakness": "Improper authorization: a device-policy user restriction (unknown sources) was skipped for a class of callers because of an over-broad trust predicate",
     "root_cause": "In InstallStart.onCreate (v1) and InstallRepository.performPreInstallChecks (v2), `isTrustedSource` selected which user restrictions were checked before an install. When it was true, only UserManager.DISALLOW_INSTALL_APPS was checked and the unknown-sources restrictions were skipped. In v2, the removed helper isInstallRequestFromTrustedSource shows the definition: `isPrivilegedAndKnown || isInstallPkgPermissionGranted`, where `isInstallPkgPermissionGranted` depends only on the caller holding INSTALL_PACKAGES, no matter how PackageInstaller was launched. As a result, a caller holding INSTALL_PACKAGES that started PackageInstaller with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent skipped the unknown-sources user restriction check. The commit relands a previously reverted change (revert 29962260bd04).",
     "fix_analysis": [
      "InstallStart.java:onCreate: captures `intentAction = intent.getAction()`, computes `isIntentInstall = ACTION_VIEW || ACTION_INSTALL_PACKAGE`, then computes `bypassUnknownSourceRestrictions = (!isIntentInstall && isInstallPkgPermissionGranted) || isPrivilegedAndKnown`. This value, not `isTrustedSource`, is now passed to checkDevicePolicyRestrictions.",
      "InstallStart.java:checkDevicePolicyRestrictions: the parameter is renamed to `bypassUnknownSourceRestrictions`. When it is true, only DISALLOW_INSTALL_APPS is checked. Otherwise the full restriction array is used (truncated in the diff).",
      "InstallRepository.kt:performPreInstallChecks: removes the helper isInstallRequestFromTrustedSource and inlines `isPrivilegedAndKnown` (sourceInfo is privileged and EXTRA_NOT_UNKNOWN_SOURCE is true) and `isInstallPkgPermissionGranted`, which is now guarded with `callingUid != Process.INVALID_UID` before isPermissionGranted is called. `isTrustedSource` is still `isPrivilegedAndKnown || isInstallPkgPermissionGranted`. The call to isInstallPermissionGrantedOrRequested is now skipped when callingUid is INVALID_UID.",
      "InstallRepository.kt:performPreInstallChecks: computes `isIntentInstall` and `bypassUnknownSourceRestrictions` the same way as v1 and passes the result to getDevicePolicyRestrictions (parameter renamed). Intent-based installs by INSTALL_PACKAGES holders therefore now reach the non-bypass restriction set, and if a restriction applies, the admin support intent path runs."
     ],
     "completeness": [
      "Both PackageInstaller UI paths present on Android 16 (v1 InstallStart.java and v2 InstallRepository.kt) are patched with the same predicate.",
      "By design, two classes are still exempt from the unknown-sources restriction: INSTALL_PACKAGES holders whose intent action is not ACTION_VIEW or ACTION_INSTALL_PACKAGE (for example the session actions ACTION_CONFIRM_INSTALL / ACTION_CONFIRM_PRE_APPROVAL), and privileged apps that set EXTRA_NOT_UNKNOWN_SOURCE on an intent install. Both match the commit message.",
      "In the bypass branch, DISALLOW_INSTALL_APPS is still checked. The fix only changes which branch is selected.",
      "The restriction list in the non-bypass branch is truncated in the diff, so the material does not show which unknown-sources restrictions it contains.",
      "The Android 15 commit shown only touches InstallStart.java. The material does not show whether a v2 InstallRepository.kt exists on 15 or needed patching."
     ],
     "behavior_changes": [
      "Apps holding INSTALL_PACKAGES that launch PackageInstaller via ACTION_VIEW or ACTION_INSTALL_PACKAGE are now subject to the unknown-sources user restrictions (admin support intent / install aborted) unless they are privileged and set EXTRA_NOT_UNKNOWN_SOURCE.",
      "v2 InstallRepository: when callingUid is INVALID_UID, isPermissionGranted and isInstallPermissionGrantedOrRequested are no longer called with the invalid uid."
     ],
     "preconditions": [
      "A device or profile owner has set an unknown-sources user restriction. The referenced CTS tests are MixedProfileOwnerTest and MixedManagedProfileOwnerTest, testPackageInstallUserRestrictions.",
      "PackageInstaller is started with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent, and the launching uid (getLaunchedFromUid / CallerInfo.uid) holds INSTALL_PACKAGES. INSTALL_PACKAGES is a signature|privileged permission (general platform knowledge)."
     ],
     "per_major": {
      "14": "Not affected: Android 14 is not listed by the bulletin and no fix commit was identified. 6/8 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "Adapted fix: commit 0985478406c2 in android-security-15.0.0_r13 applies the same InstallStart.java change (intentAction, isIntentInstall, bypassUnknownSourceRestrictions, renamed checkDevicePolicyRestrictions parameter). No InstallRepository.kt change is shown. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: commit 4dc009cd724c in android-security-16.0.0_r4 (reland of the reverted change) patches InstallStart.java and InstallRepository.kt (callingUid-based). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "Adapted fix: commit 81f83fdb0944 in android-17.0.0_r1 (the Cherrypick-From source) makes the same InstallStart.java change. Its InstallRepository.kt version uses originatingUid instead of callingUid for the INSTALL_PACKAGES check, and the surrounding comments and flow differ. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a reapply: account for both the original patch and its revert (29962260bd04) so the tree ends with the reland. The v1 InstallStart.java hunk relies on the locals `isInstallPkgPermissionGranted` and `isPrivilegedAndKnown` already existing in onCreate.",
      "For the v2 InstallRepository.kt hunk, use the uid variable that matches the branch: Android 16 uses callingUid, Android 17 uses originatingUid. In both cases the helper isInstallRequestFromTrustedSource is removed and its logic inlined."
     ],
     "confidence": "high",
     "unknowns": [
      "Why the original landing was reverted and which issues the reland fixed (the original patch is not in the material).",
      "The full restriction array in the non-bypass branch (truncated in all diffs).",
      "Whether Android 15 has a v2 InstallRepository.kt path and whether it was patched separately.",
      "How isTrustedSource is computed and used elsewhere in v1 InstallStart.onCreate."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0009": {
     "cve": "CVE-2026-0009",
     "summary": "Removes the Photopicker MainActivity collector that auto-confirmed and returned the result as soon as one item was selected in single-select mode, and instead always enables the SelectionBar so the user must explicitly confirm the selection, with SelectionImpl.toggle replacing the existing item when selectionLimit == 1.",
     "component": "platform/packages/providers/MediaProvider, Compose Photopicker (photopicker/ MainActivity, core/selection, features/selectionbar, core/components/mediagrid)",
     "weakness": "Missing user confirmation step: a single tap on a media item immediately finalized the picker result and handed the media to the calling app (insufficient UI consent for granting access)",
     "root_cause": "In photopicker/src/com/android/photopicker/MainActivity.kt, onCreate called listenForSelectionIfSingleSelect(), which, when configurationManager.configuration.value.selectionLimit == 1, collected selection.get().flow and invoked onMediaSelectionConfirmed() as soon as the emitted set had size 1. SelectionBarFeature.Registration.isEnabled returned config.selectionLimit > 1 in ACTIVITY runtime, so no confirm bar existed in single-select activity mode, and MediaGrid.kt suppressed the selected-state visuals when selectionLimit was not > 1. The net effect was that the first tap on a grid item ended the activity and returned that item to the caller with no explicit confirmation by the user.",
     "fix_analysis": [
      "MainActivity.kt: deletes the call to listenForSelectionIfSingleSelect() in onCreate and the function itself, so the activity no longer calls onMediaSelectionConfirmed() on its own when the selection flow reaches size 1; the remaining confirmation paths shown in the material are the PhotopickerAppWithBottomSheet onMediaSelectionConfirmed callback (selection bar) and the Event.MediaSelectionConfirmed path mentioned in the removed comment.",
      "SelectionBarFeature.kt: SelectionBarFeature.Registration.isEnabled now returns true unconditionally (previously selectionLimit > 1 for ACTIVITY runtime, otherwise true only for EMBEDDED), so the confirm bar is available in single-select activity sessions; the unused PhotopickerRuntimeEnv import is dropped.",
      "SelectionImpl.kt: toggle() is rewritten as a when: if the item is already selected it is removed; else if configuration.value.selectionLimit == 1 the selection is cleared and the new item added (replace semantics); else the previous ensureSelectionLimitLocked(1) path applies. Covered by new test testToggleWithSelectionLimitOneReplacesExistingItem in SelectionImplTest.kt.",
      "MediaGrid.kt: defaultBuildMediaItem and SelectedIconOverlay drop the selectionLimit > 1 gating (shouldIndicateSelected) and use isSelected directly, so a selected item is visibly marked in single-select mode before confirmation.",
      "SelectionBar.kt: renames visible to showSelectionBar (currentSelection.isNotEmpty() || action == ACTION_USER_SELECT_IMAGES_FOR_APP) with no logic change on Android 16.",
      "SelectionBarFeatureTest.kt: testSelectionBarNotEnabledForSingleSelectInActivityMode becomes testSelectionBarIsEnabledForSingleSelectInActivityMode expecting true for TEST_ACTION, ACTION_PICK_IMAGES and ACTION_GET_CONTENT with default selectionLimit."
     ],
     "completeness": [
      "The only automatic confirmation path shown in the material (MainActivity.listenForSelectionIfSingleSelect) is removed; the other confirmation paths shown are user-driven and untouched.",
      "SelectionBarFeature.isEnabled now returns true for all runtime environments, including EMBEDDED, so the change is broader than the ACTIVITY single-select case.",
      "The comment above SelectionBarFeature.Registration.isEnabled still states the bar is only shown in multi-select mode and that the activity ends on first selection; it is stale after the fix but has no functional effect.",
      "The fix does not add any validation of the caller or of the returned URIs; it only inserts the confirmation step into the UI flow.",
      "Code context at android-security-16.0.0_r8 shows the patched state (toggle when-block, isEnabled = true, no listenForSelectionIfSingleSelect call in onCreate)."
     ],
     "behavior_changes": [
      "Single-select Photopicker sessions no longer finish on the first tap; the selection bar appears and the user must confirm to return a result.",
      "Tapping a different item in single-select mode clears the selection and adds the new item, and toggle returns SUCCESS (per the new test).",
      "Selected items in single-select mode now show the selected padding/rounded-corner treatment and the check icon or order index overlay.",
      "SelectionBarFeature is registered as enabled for every PhotopickerConfiguration, including single-select ACTIVITY runtime."
     ],
     "preconditions": [
      "The Compose Photopicker (MediaProvider photopicker/ MainActivity) is launched by a caller in single-select mode (PhotopickerConfiguration.selectionLimit == 1, e.g. ACTION_PICK_IMAGES or ACTION_GET_CONTENT with default limit, per SelectionBarFeatureTest).",
      "Runtime environment ACTIVITY (the removed isEnabled already returned true for EMBEDDED on Android 16).",
      "A tap on a media grid item triggered SelectionImpl.toggle and the selection flow emission of size 1 that the removed collector acted on."
     ],
     "per_major": {
      "14": "not affected per bulletin; patched files absent at android-security-14.0.0_r29",
      "15": "adapted fix e7bcd640810f in android-security-15.0.0_r13: same removal of listenForSelectionIfSingleSelect (15 variant used withContext(background)), same SelectionImpl.toggle rewrite and tests; SelectionBarFeature.isEnabled (previously selectionLimit > 1) gains the deferredPrefetchResultsMap parameter and returns true, plus an Event import and an embedded-mode test; SelectionBar.kt adds the ACTION_USER_SELECT_IMAGES_FOR_APP visibility condition; MediaGrid.kt change is whitespace only; the diff also adds PhotopickerEventLogger start lines to onCreate",
      "16": "fix commit 61808742c17a in android-security-16.0.0_r4 (Revert^2, reverting revert c5d89a70a250; Cherrypick-From bf034ab4e140 and 9b753d8558b9); patched state shown in code context at android-security-16.0.0_r8",
      "17": "adapted fix bf034ab4e140 in android-17.0.0_r1: same core changes plus removal of EMBEDDED-specific gating (isEmbedded/shouldIndicateSelected in MediaGrid.kt defaultBuildMediaItem and SelectedIconOverlay, and the embedded selectionLimit > 1 'visible' check in SelectionBar.kt)"
     },
     "backport_notes": [
      "This is a Revert^2: an earlier landing was reverted and relanded after failing CTS tests were fixed; the CTS changes are not in the material, so a backport should check the corresponding CTS state in addition to carrying the photopicker unit test updates (SelectionImplTest, SelectionBarFeatureTest).",
      "Branch-specific pieces differ: Android 15 needs the two-argument isEnabled(config, deferredPrefetchResultsMap) signature and the PhotopickerEventLogger lines in onCreate (verify PhotopickerEventLogger exists in the target), while Android 17 additionally strips EMBEDDED gating in MediaGrid.kt and SelectionBar.kt."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not describe how the missing confirmation step leads to the EoP the bulletin rates High, only that the fix inserts an explicit confirmation.",
      "Which CTS tests failed before the reland, and what the companion change 9b753d8558b9 contained.",
      "Whether MediaProvider module builds carried this fix separately from the platform security tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0016": {
     "cve": "CVE-2026-0016",
     "summary": "Makes Credential Manager package-update handling user-correct: CredentialManagerService.updateProvidersWhenServiceRemoved now reads and writes the Secure settings of the passed userId instead of UserHandle.myUserId(), and AbstractMasterSystemService.removeInvalidCachedServicesLocked only acts on cached services belonging to the user whose package changed.",
     "component": "platform/frameworks/base, Credential Manager service (services/credentials) and the shared AbstractMasterSystemService infra (services/core/java/com/android/server/infra)",
     "weakness": "Wrong user context in multi-user handling: settings updated for the calling process's user instead of the target user, and a cross-user loop missing a per-user filter",
     "root_cause": "Two defects on the package-update path. (1) In CredentialManagerService.updateProvidersWhenServiceRemoved(settingsWrapper, componentName, userId), three settings accesses used UserHandle.myUserId() (the user of the system_server process) instead of the userId parameter: the write of Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, and the read and the write of Settings.Secure.CREDENTIAL_SERVICE. The initial read of CREDENTIAL_SERVICE_PRIMARY, isAutofillProviderPresent and clearAllCredentialProviders already used userId. A service removal handled for one user could therefore change the credential provider settings of the system process's user. (2) In AbstractMasterSystemService.removeInvalidCachedServicesLocked(validServices, packageName, userId), visitServicesLocked iterates mServicesCacheList across all users (per index search) and the check matched only on package name. A package change in one user could therefore call handleServiceRemovedMultiModeLocked(serviceComponentName, userId) for another user's service with the same package name, pairing that service's component with the changing user's userId. The package monitor is registered with UserHandle.ALL (mPackageMonitor.register(getContext(), null, UserHandle.ALL, true)).",
     "fix_analysis": [
      "Commit 10b748dbc348 (android-security-16.0.0_r7), services/core/java/com/android/server/infra/AbstractMasterSystemService.java:removeInvalidCachedServicesLocked: inside the visitServicesLocked lambda, reads int serviceUserId = s.getUserId() and adds serviceUserId == userId to the condition. Only services of the changing user with the matching package are now checked against validServices and passed to handleServiceRemovedMultiModeLocked.",
      "Same commit, services/credentials/java/com/android/server/credentials/CredentialManagerService.java:updateProvidersWhenServiceRemoved: replaces UserHandle.myUserId() with the userId parameter in three places: putStringForUser(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, ...), getStringForUser(Settings.Secure.CREDENTIAL_SERVICE, ...) and putStringForUser(Settings.Secure.CREDENTIAL_SERVICE, ...). On the Android 16 security branch this is applied unconditionally (no flag check in production code).",
      "Same commit, services/tests/servicestests/Android.bp: adds android.credentials.flags-aconfig-java to static_libs. CredentialManagerServiceTest.java: rewrites the tests to pass explicit user ids and adds getStoredProvidersExceptService and updateProvidersWhenServiceRemoved tests. These include updateProvidersWhenServiceRemoved_multiUser_otherUserUnchanged (a removal for USER_ID_10 leaves the USER_ID_0 settings untouched). The test also enables Flags.FLAG_MULTI_USER_FIX_ENABLED via SetFlagsRule.",
      "Commit ffdfa5c70fca (android-security-16.0.0_r7), CredentialManagerServiceTest.java only ('Remove multi user flag reference'): removes the Flags import, the SetFlagsRule and the enableFlags call. This matches the Android 16 production diff, which has no flag gate.",
      "Android 17 (a631806c7aef in android-17.0.0_r1): same AbstractMasterSystemService change. In CredentialManagerService.updateProvidersWhenServiceRemoved, the userId-based calls are wrapped in if (Flags.multiUserFixEnabled()) and the old UserHandle.myUserId() calls are kept in the else branches. That part of the fix is active only when android.credentials.flags.multi_user_fix_enabled is on."
     ],
     "completeness": [
      "Covers all three remaining UserHandle.myUserId() uses in updateProvidersWhenServiceRemoved. Per the surrounding code, the other settings accesses in that function (initial CREDENTIAL_SERVICE_PRIMARY read, isAutofillProviderPresent, clearAllCredentialProviders) already took userId.",
      "Adds the user filter in removeInvalidCachedServicesLocked only. The adjacent handlePackageUpdateLocked(String packageName) in the same file still calls visitServicesLocked((s) -> s.handlePackageUpdateLocked(packageName)) over all users' services with no user filter. Whether that matters depends on per-service handlers that the material does not show.",
      "updateProvidersWhenPackageRemoved is exercised by the new tests but not modified by the diff. The material does not show how it handles the settings user.",
      "On Android 17 the CredentialManagerService part is flag-gated; with the flag off, the old UserHandle.myUserId() behavior remains. The AbstractMasterSystemService part is unconditional on both branches."
     ],
     "behavior_changes": [
      "Removing a credential provider service for user N now updates CREDENTIAL_SERVICE_PRIMARY and CREDENTIAL_SERVICE for user N instead of the system process's user.",
      "A package change in user N no longer calls handleServiceRemovedMultiModeLocked for cached services of other users that share the package name.",
      "Android 17 only: the settings-user correction depends on the aconfig flag android.credentials.flags.multi_user_fix_enabled.",
      "FrameworksServicesTests now depends on android.credentials.flags-aconfig-java."
     ],
     "preconditions": [
      "Multi-user device with Credential Manager provider settings configured in more than one user.",
      "A package update or change event for a package hosting a credential provider service in a user other than the system process's user. The package monitor in AbstractMasterSystemService is registered for UserHandle.ALL; the exact call path into updateProvidersWhenServiceRemoved is not shown.",
      "On Android 17, the flag behind Flags.multiUserFixEnabled() must be off for the settings-user defect to be present."
     ],
     "per_major": {
      "14": "Not affected per the bulletin. Only 3/8 lines of the patched region are present at android-security-14.0.0_r29, so the patched code differs or is partly absent.",
      "15": "Not affected per the bulletin. Only 2/8 lines of the patched region are present at android-security-15.0.0_r17, so the patched code differs or is partly absent.",
      "16": "Resolved in android-security-16.0.0_r7 by 10b748dbc348 (adapted and unflagged: userId used unconditionally) plus ffdfa5c70fca (test-only removal of the flag reference). 8/8 lines are present at android-security-16.0.0_r8.",
      "17": "Resolved in android-17.0.0_r1 by a631806c7aef. The CredentialManagerService part is gated by Flags.multiUserFixEnabled() with legacy else branches; the AbstractMasterSystemService part is unconditional. 8/8 lines are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Apply 10b748dbc348, then ffdfa5c70fca. On branches without the android.credentials.flags aconfig library, use the Android 16 unflagged form (direct userId substitution) and drop the SetFlagsRule/Flags test references.",
      "Both hunks are small and self-contained. The AbstractMasterSystemService hunk requires the per-user service class to expose getUserId() (called as s.getUserId() in the lambda)."
     ],
     "confidence": "high",
     "unknowns": [
      "What information is disclosed (the bulletin classifies this as ID, High) is not shown.",
      "Which path from the package monitor callbacks into updateProvidersWhenServiceRemoved is involved is not shown.",
      "Why Android 14 and 15 are not affected is not shown beyond the partial presence of the patched region.",
      "Whether android.credentials.flags.multi_user_fix_enabled is enabled by default on Android 17 builds.",
      "What handleServiceRemovedMultiModeLocked and the per-service handlePackageUpdateLocked implementations do."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0018": {
     "cve": "CVE-2026-0018",
     "summary": "AccessibilityManagerService now validates, via PackageManagerInternal and outside its lock, that every tileService name declared by an accessibility service or shortcut target resolves to an existing, exported, enabled TileService protected by BIND_QUICK_SETTINGS_TILE before AccessibilityUserState records it in its feature-to-tile maps.",
     "component": "platform/frameworks/base, services/accessibility (AccessibilityManagerService, AccessibilityUserState, new AccessibilityTileUtils)",
     "weakness": "Missing input validation of app-supplied component name (tile service declared in accessibility service / shortcut metadata)",
     "root_cause": "AccessibilityUserState.updateTileServiceMapForAccessibilityServiceLocked and updateTileServiceMapForAccessibilityActivityLocked built ComponentName objects from the tileServiceName string returned by AccessibilityServiceInfo.getTileServiceName() / AccessibilityShortcutInfo.getTileServiceName() (app-declared metadata), combined with the declaring feature's package name. They stored them in mA11yServiceToTileService / mA11yActivityToTileService without checking that the component exists, is exported, is enabled, or is protected by BIND_QUICK_SETTINGS_TILE. An app declaring an a11y service or shortcut target could therefore get an unvalidated (possibly non-existent or non-tile) component name from its own package recorded in system_server's QS tile bookkeeping. The bulletin classifies the impact as DoS (High); the mechanism is not shown in the material.",
     "fix_analysis": [
      "New file services/accessibility/java/com/android/server/accessibility/AccessibilityTileUtils.java: getValidA11yTileServices(context, PackageManagerInternal, serviceInfos, shortcutInfos, userId) iterates both lists and derives ComponentName(a11yFeature.getPackageName(), tileServiceName). It keeps only names that pass isComponentValidTileService. That check requires: pm.resolveService(Intent(TileService.ACTION_QS_TILE) with the component set, flags 0, userId, Process.myUid()) returns a serviceInfo; serviceInfo.exported is true; serviceInfo.permission equals Manifest.permission.BIND_QUICK_SETTINGS_TILE; and pm.getComponentEnabledSetting combined with serviceInfo.enabled via resolveEnabledComponent gives enabled. It returns an empty set if pm is null.",
      "AccessibilityUserState.java: updateTileServiceMapForAccessibilityServiceLocked and updateTileServiceMapForAccessibilityActivityLocked now take @NonNull Set<ComponentName> validA11yTileServices. Each one clears its map, returns early if the set is empty, and only calls put(a11yFeature, tileService) when validA11yTileServices.contains(tileService).",
      "AccessibilityManagerService.java: validA11yTileServices is computed by AccessibilityTileUtils.getValidA11yTileServices after the parse calls and before entering the synchronized lock block, at three sites: the user-initialization path (the one that logs 'already initialized'), a package-monitor handler that calls onSomePackagesChangedLocked, and a second package-monitor handler that calls readConfigurationForUserStateLocked. The commit message states this is to avoid AccessibilityManagerService holding the lock for a long time while calling PackageManager.",
      "AccessibilityManagerService.java: onSomePackagesChangedLocked, readConfigurationForUserStateLocked, readInstalledAccessibilityServiceLocked and readInstalledAccessibilityShortcutLocked gain the validA11yTileServices parameter and pass it through. readInstalledAccessibilityServiceLocked and readInstalledAccessibilityShortcutLocked now always call the update*Locked map rebuild after the equals() check, instead of only when the installed list changed, and still return the changed flag. The added code comment explains the reason: PackageManager may not be able to resolve the TileService at the time of a package change (especially the initial load), so the map is always rebuilt.",
      "Tests: new AccessibilityTileUtilsTest and a new test helper services/tests/servicestests/.../utils/TileServiceUtil.java (mocks PackageManagerInternal.resolveService/getComponentEnabledSetting). AccessibilityManagerServiceTest and AccessibilityUserStateTest are updated to pass the valid set."
     ],
     "completeness": [
      "Both producers of tile mappings are covered: the accessibility-service path (mInstalledServices) and the accessibility-activity/shortcut path (mInstalledShortcuts).",
      "The three AccessibilityManagerService sites changed in the diff compute and pass the validated set. The index search found no other callers of onSomePackagesChangedLocked, readConfigurationForUserStateLocked or readInstalledAccessibilityServiceLocked among its top results, but that search may be incomplete.",
      "Validation is a snapshot taken outside the lock. The map is then populated under the lock from the same parsed lists, so the check and the use are not atomic. However, the maps are now rebuilt on every configuration read, so a later package change re-validates.",
      "The ComponentName is always built with the a11y feature's own package name, so validation only ever targets components in the declaring package.",
      "If PackageManagerInternal is not available from LocalServices, the set is empty and no tile mappings are created.",
      "The material does not show the consumers of mA11yServiceToTileService / mA11yActivityToTileService, so whether any consumer performs its own validation is not visible here."
     ],
     "behavior_changes": [
      "Accessibility services or shortcut targets whose declared tileService does not resolve, is not exported, is disabled, or is not protected by BIND_QUICK_SETTINGS_TILE no longer get a QS tile mapping. A Slog.w with tag AccessibilityTileUtils is logged for each rejected component.",
      "Feature-to-tile maps are rebuilt on every readConfigurationForUserStateLocked call, even when the installed service/shortcut lists are unchanged.",
      "Each declared tile service now triggers extra PackageManagerInternal.resolveService and getComponentEnabledSetting calls, made outside the lock, at each of the three changed sites (user initialization and package-change handling).",
      "Signature changes: AccessibilityUserState.updateTileServiceMapForAccessibilityServiceLocked/ActivityLocked and several private AccessibilityManagerService methods now require a Set<ComponentName> argument."
     ],
     "preconditions": [
      "An installed package for the current user declares an AccessibilityService or accessibility shortcut target (AccessibilityShortcutInfo) whose metadata includes a tileService name.",
      "The code runs in system_server during the user-initialization path or when the AccessibilityManagerService package monitor handles a package change for the current user. These are the entry points shown in the diff.",
      "How the resulting map entries are consumed to produce the DoS is not shown in the material."
     ],
     "per_major": {
      "14": "Not affected: Android 14 is not listed by the bulletin. No fix is recorded. Per-major facts show 5/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "Adapted fix: platform/frameworks/base f3ef4dd5056e in android-security-15.0.0_r13. AccessibilityTileUtils.java and the AccessibilityUserState changes are the same. Some AccessibilityManagerService and test hunks differ slightly: readInstalledAccessibilityShortcutLocked has no Flags.clearShortcutsWhenActivityUpdatesToService branch, the user-initialization early return has no Slog.w, and the test helper sendBroadcastToAccessibilityManagerService takes no userId. 6/8 lines present at android-security-15.0.0_r17.",
      "16": "Same fix (this commit): platform/frameworks/base 7783a5265907 in android-security-16.0.0_r4. Its commit message lists Cherrypick-From internal 637f5b4aa4fb and 9d9034b7f4fb. 6/8 lines present at android-security-16.0.0_r8.",
      "17": "Adapted fix: platform/frameworks/base 637f5b4aa4fb in android-17.0.0_r1. It uses userState.buildInstalledServicesMapLocked / getInstalledServices() / mInstalledServicesMap instead of mInstalledServices. It also includes extra AccessibilityManagerServiceTest updates: the mock helper with hasQsTile sets up the PackageManager mock, and an additional test passes a valid set for three services. 6/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The 16 commit lists two Cherrypick-From changes (637f5b4aa4fb and 9d9034b7f4fb). When backporting, carry AccessibilityTileUtils.java, the AccessibilityUserState signature change, and all AccessibilityManagerService call sites together. The old zero-argument update*Locked methods are removed, so callers must compile against the new signatures.",
      "Branches without Flags.clearShortcutsWhenActivityUpdatesToService (as on 15) or with mInstalledServicesMap (as on 17) need the readInstalledAccessibilityServiceLocked / readInstalledAccessibilityShortcutLocked hunks re-contextualized. The logic is the same on every branch: always rebuild the map, then return the changed flag."
     ],
     "confidence": "medium",
     "unknowns": [
      "The downstream consumer of mA11yServiceToTileService / mA11yActivityToTileService and the exact mechanism by which an unvalidated component led to the DoS are not shown in the material.",
      "The enclosing method names of the three changed AccessibilityManagerService call sites are not shown in the diff hunks.",
      "The material does not show what the second cherry-picked internal change 9d9034b7f4fb (Bug 441506794) contributed compared with the first.",
      "The material does not explain why Android 14 is treated as not affected while 5/8 lines of the patched region are present there."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0036": {
     "cve": "CVE-2026-0036",
     "summary": "StageCoordinator.startAnimation now checks a non-pending transition that arrives while split is inactive. If its opening tasks belong to only one split stage, it starts a follow-up TRANSIT_CLOSE transition that reparents those tasks back to the TaskDisplayArea, then still returns false so another handler plays the original transition.",
     "component": "platform/frameworks/base, WindowManager Shell split-screen (libs/WindowManager/Shell, StageCoordinator / SplitTransitionUtils)",
     "weakness": "Improper handling of an inconsistent window-hierarchy state (a task left parented to a split stage while split is not active). The bulletin classifies it as ID.",
     "root_cause": "Per the commit message: when two tasks are launched immediately one after the other and the second is an adjacent launch, both launches land in the same shell transition, but the transition request reports the first task as the trigger. As a result StageCoordinator is not resolved as the handler. In StageCoordinator.startAnimation, the non-pending path simply did `if (!isSplitActive()) return false;` (`!mMainStage.isActive()` on 14/15/17). The transition therefore fell through to the default transition handler, which only shows the adjacent task without it being in split. No code checked that an enter-split transition contained tasks for at least two stages.",
     "fix_analysis": [
      "libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitTransitionUtils.java (new), handleMalformedEnterTransition(TransitionInfo, Function<RunningTaskInfo, StageTaskListener>): iterates info.getChanges(). A change is recorded, together with its stage, when it has a non-null getTaskInfo(), the stageResolver returns a non-null StageTaskListener for it, and its mode satisfies isOpeningMode(). If at least one such task exists but there are at most one distinct stage, the transition counts as malformed. In that case the method logs to WM_SHELL_SPLIT_SCREEN and returns a WindowContainerTransaction that calls wct.reparent(task.token, null /* parent */, true /* onTop */) for each recorded task, which moves them back to the TaskDisplayArea. Otherwise it returns null.",
      "libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java:startAnimation: in the `!mSplitTransitions.isPendingTransition(transition)` branch, the `if (!isSplitActive()) return false;` early return is replaced by a block that calls SplitTransitionUtils.handleMalformedEnterTransition(info, taskInfo -> getStageOfTask(taskInfo)). If a WCT comes back, the block calls mTransitions.startTransition(TRANSIT_CLOSE, wct, null). The method still returns false either way. On 16, a ProtoLog line that includes isSplitActive() replaces the earlier 'startAnimation: transition=%d' log.",
      "libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionUtilsTests.java (new): covers five cases: tasks in two stages (null), one stage with an opening task (non-null WCT), no stage, a non-opening mode, and a null taskInfo."
     ],
     "completeness": [
      "The check runs only in StageCoordinator.startAnimation, and only when the transition is not a pending split transition and split is not active. The split-active path and the pending-transition paths are unchanged.",
      "Only changes that have a task info, a resolvable stage (getStageOfTask) and an opening mode (isOpeningMode) are considered. Stage tasks with non-opening modes are ignored by the detection. On 14/15/17, isOpeningMode is defined locally as TRANSIT_OPEN or TRANSIT_TO_FRONT. On 16 it comes from TransitionUtil, whose definition is not shown.",
      "The fix is a recovery in the Shell: a follow-up TRANSIT_CLOSE transition reparents the tasks back to the TDA. It does not change how WM core builds the merged transition or which task it names as the trigger. The original transition is still passed on (return false).",
      "If a transition arrives while split is inactive and has opening tasks in both stages, handleMalformedEnterTransition returns null and behavior is the same as before."
     ],
     "behavior_changes": [
      "When split is inactive and a non-pending transition carries opening tasks that belong to exactly one split stage, the Shell now starts an extra TRANSIT_CLOSE transition that reparents those tasks to the TaskDisplayArea.",
      "New ProtoLog entries in WM_SHELL_SPLIT_SCREEN: 'startAnimation: transition=%d isSplitActive=%b' and 'Malformed enter transition=%d detected'.",
      "On 14/15/17, StageTaskListener becomes public so the new utility class in another package can use it."
     ],
     "preconditions": [
      "Shell transitions path: StageCoordinator is registered as a transition handler, and StageCoordinator.startAnimation is called for a transition that is not a pending split transition.",
      "Split screen is not active (isSplitActive() is false on 16; mMainStage.isActive() is false on 14/15/17).",
      "Per the commit message: two tasks are launched immediately one after the other, the second as an adjacent launch, so both end up in one transition whose request names the first task as the trigger.",
      "The commit was tested manually with a test app from b/405392600. What that app needs to do exactly is not in the material."
     ],
     "per_major": {
      "14": "adapted fix, commit 3ef64c4444d5 in android-security-14.0.0_r28. Same logic, plus: a new mTransitions field set in both constructors, a TRANSIT_CLOSE import, StageTaskListener made public, a local isOpeningMode() in SplitTransitionUtils, ProtoLog imported from com.android.internal.protolog.common, and the condition on mMainStage.isActive(). 8/8 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "adapted fix, commit fa1c418fd8aa in android-security-15.0.0_r16. Same adaptations as 14 (mTransitions field, public StageTaskListener, local isOpeningMode, mMainStage.isActive()). 8/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix (this commit, 04814cb8a86e in android-security-16.0.0_r7). It uses the existing mTransitions, isSplitActive() and TransitionUtil.isOpeningMode. 8/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "adapted fix, commit f60c7771358c in android-17.0.0_r1, shaped like the 14/15 variant (mTransitions field, public StageTaskListener, local isOpeningMode, mMainStage.isActive()). 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "On branches where StageCoordinator does not keep a Transitions reference: add the mTransitions field and set it in every constructor, make StageTaskListener public, and provide isOpeningMode locally if com.android.wm.shell.shared.TransitionUtil is absent. The ProtoLog import also differs: com.android.internal.protolog.common.ProtoLog on 14/15/17 versus com.android.internal.protolog.ProtoLog on 16.",
      "Use the branch's own split-active check (isSplitActive() on 16, mMainStage.isActive() on 14/15/17). The test file has the same blob hash on all branches."
     ],
     "confidence": "high",
     "unknowns": [
      "What information is disclosed and to whom. The bulletin only gives type ID and severity High; the diff shows a task being shown outside a valid split state.",
      "Why the Android 17 security tag shows 7/8 lines of the patched region instead of 8/8, and whether the 17 branch code otherwise matches the 14-style diff.",
      "Whether the WM core side (which task a merged launch names as the trigger) was changed in a separate commit. The material shows only the Shell recovery.",
      "The exact definition of TransitionUtil.isOpeningMode on 16, and the order in which the original transition and the follow-up TRANSIT_CLOSE transition play out."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0045": {
     "cve": "CVE-2026-0045",
     "summary": "Stops outgoing secure RFCOMM socket connections from registering their security mask on the shared RFC_MUX security service record (which made later incoming RFCOMM MUX connections enforce bonding and start pairing), and makes the temporary-bonding security upgrade check in btm_sec_is_upgrade_possible direction-aware and unconditional (flag upgrade_temp_bonding_on_auth_req removed).",
     "component": "platform/packages/modules/Bluetooth, Bluetooth stack (BTA JV RFCOMM client, BTM security manager)",
     "weakness": "Incorrect security requirement state leaking between connection directions (outgoing socket security mask persisted in a shared service record and applied to incoming connections), plus a direction bug in the authentication upgrade check (OUT_AUTHENTICATE tested for both originator and acceptor)",
     "root_cause": "In system/bta/jv/bta_jv_act.cc:bta_jv_rfcomm_connect, every outgoing RFCOMM client connection called BTM_SetSecurityLevel(true, \"RFC_MUX\", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0), writing the socket's sec_mask into the single RFC_MUX security service record. Per the commit message, a secure socket thereby added bonding requirements to that record, and the record was only reset when a later outgoing insecure socket was requested. Because the same record governs incoming RFCOMM MUX connection requests, all subsequent incoming RFCOMM connections also enforced the bonding requirement, so an incoming RFCOMM connection led the local stack to initiate bonding. Separately, in system/stack/btm/btm_sec.cc:btm_sec_is_upgrade_possible the temporary-bonding upgrade condition was behind the flag upgrade_temp_bonding_on_auth_req and only tested BTM_SEC_OUT_AUTHENTICATE regardless of is_originator.",
     "fix_analysis": [
      "system/bta/jv/bta_jv_act.cc:bta_jv_rfcomm_connect: the BTM_SetSecurityLevel(... \"RFC_MUX\", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0) call is deleted. Outgoing connections no longer rewrite the shared RFC_MUX service record; the socket's sec_mask is still passed to RFCOMM_CreateConnectionWithSecurity for the outgoing connection itself.",
      "system/stack/btm/btm_sec.cc:btm_sec_is_upgrade_possible: inside the BTM_SEC_LINK_KEY_KNOWN branch, the condition that sets is_possible = true for a device whose bond type is temporary (p_dev_rec->sec_rec.is_bond_type_temporary()) now tests security_required against BTM_SEC_OUT_AUTHENTICATE when is_originator and BTM_SEC_IN_AUTHENTICATE otherwise. The com::android::bluetooth::flags::upgrade_temp_bonding_on_auth_req() guard is removed, so the check is always active.",
      "flags/security.aconfig: the flag definition upgrade_temp_bonding_on_auth_req (bug 385747513) is removed; the behavior is unconditional."
     ],
     "completeness": [
      "Covers the RFCOMM client path in bta_jv_rfcomm_connect only; that was the only BTM_SetSecurityLevel RFC_MUX call shown in the material.",
      "The upgrade check now handles both originator and acceptor directions, but only when BTM_SEC_LINK_KEY_KNOWN is set and the bond type is temporary; other cases of the function are unchanged.",
      "Whether other callers still modify the RFC_MUX service record (e.g. an insecure-socket reset path mentioned in the commit message) is not shown in the material."
     ],
     "behavior_changes": [
      "Incoming RFCOMM MUX connection requests no longer inherit bonding/authentication requirements left behind by an earlier outgoing secure socket connection, so they no longer cause the local stack to initiate bonding on that basis.",
      "For devices on a temporary bond with an authentication requirement in the relevant direction (incoming or outgoing), btm_sec_is_upgrade_possible now always reports that an upgrade is possible. Previously this depended on the flag and only on the outgoing bit.",
      "Runtime flag upgrade_temp_bonding_on_auth_req no longer exists; no toggle to revert to prior behavior."
     ],
     "preconditions": [
      "Bluetooth Classic enabled; the affected path is reached when a local app has made an outgoing secure RFCOMM socket connection via BTA_JvRfcommConnect (system/bta/jv/bta_jv_api.cc, which posts bta_jv_rfcomm_connect to the main thread, per index search) and no subsequent outgoing insecure socket reset the RFC_MUX record.",
      "A remote device then issues an incoming RFCOMM MUX connection request, which is evaluated against the RFC_MUX security service record (per commit message).",
      "For the btm_sec_is_upgrade_possible change: a known link key and temporary bond type for the peer."
     ],
     "per_major": {
      "14": "adapted fix: commit 2e2a7bf09a3f (android-security-14.0.0_r28) adds only the direction-aware temporary-bonding upgrade check in btm_sec_is_upgrade_possible, using p_dev_rec->security_required / p_dev_rec->is_bond_type_temporary() (no sec_rec) and no flag; no bta_jv_act.cc change is shown. 4/4 patched lines present at android-security-14.0.0_r29.",
      "15": "adapted fix: commit c1a809d2333e (android-security-15.0.0_r16) removes the BTM_SetSecurityLevel RFC_MUX call that was guarded by rfcomm_always_use_mitm() in bta_jv_rfcomm_connect and adds the unflagged direction-aware check in btm_sec_is_upgrade_possible (sec_rec form). 3/8 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: commit 2ad8e1e0e0e5 (android-security-16.0.0_r7) removes the unconditional BTM_SetSecurityLevel RFC_MUX call, removes the upgrade_temp_bonding_on_auth_req flag and makes the check direction-aware and unconditional. 4/8 lines matched at android-security-16.0.0_r8 (diff modifies existing lines); the r8 code context shows the fixed state.",
      "17": "resolved in base of android-17.0.0_r1 via commit 92611bfc4d26 (adds the direction-aware check in p_dev_rec->security_required form); 0/4 patched lines present at android-security-17.0.0_r1, reason not shown. Whether 17 also dropped the bta_jv_act.cc BTM_SetSecurityLevel call is not shown."
     },
     "backport_notes": [
      "The BTM_SetSecurityLevel RFC_MUX call in bta_jv_rfcomm_connect appears in three forms across branches: guarded by rfcomm_always_use_mitm() (15), guarded by !upgrade_temp_bonding_on_auth_req() (16 release per index search at android-16.0.0_r4), or unconditional (16 security branch); remove it in whichever form is present and drop the flag definition if it exists.",
      "Field access differs: older branches use p_dev_rec->security_required and p_dev_rec->is_bond_type_temporary(), newer ones use p_dev_rec->sec_rec.*; adapt the btm_sec_is_upgrade_possible hunk accordingly and leave it unflagged."
     ],
     "confidence": "medium",
     "unknowns": [
      "Whether Android 14 and 17 ever contained the BTM_SetSecurityLevel RFC_MUX call in bta_jv_rfcomm_connect, and if so whether it was removed there.",
      "How the RFC_MUX service record is consumed on the incoming path and where the insecure-socket reset occurs; the commit message describes it but no such code is in the material.",
      "Whether the btm_sec_is_upgrade_possible change is intended to replace the MITM authentication that the removed BTM_SetSecurityLevel call provided for outgoing secure connections.",
      "Why 0/4 patched lines match at android-security-17.0.0_r1 and 3/8 at android-security-15.0.0_r17.",
      "Whether an upgrade being reported as possible always leads to pairing; the callers of btm_sec_is_upgrade_possible are not fully shown.",
      "The exact EoP consequence rated High by the bulletin beyond 'incoming RFCOMM connection leads to initiating bonding'."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0046": {
     "cve": "CVE-2026-0046",
     "summary": "In services/core/java/com/android/server/wm/Letterbox.java, the InputWindowHandle of the Letterbox InputInterceptor now reports the letterboxed app's session pid/uid (win.mSession.mPid/mUid) as its owner instead of WindowManagerService.MY_PID/MY_UID, so the letterbox input window is no longer treated as a trusted system window by the obscured-touch (tapjacking) checks.",
     "component": "platform/frameworks/base, WindowManager (services/core wm, Letterbox input interception)",
     "weakness": "Incorrect identity assignment to an input window: the letterbox input handle was owned by the system uid, which defeated the obscured-touch tapjacking check",
     "root_cause": "Letterbox.InputInterceptor creates an InputWindowHandle for the letterbox around a letterboxed app window. It is configured as NOT_FOCUSABLE with InputConfig.SLIPPERY (or InputConfig.SPY when Flags.scrollingFromLetterbox() is on in Android 16). Per the commit message, a touch can slip out of the letterbox's black bar and reach a lower-layer activity that the letterbox covers. The handle's ownerPid/ownerUid were set to WindowManagerService.MY_PID/MY_UID (system_server). Per the commit message and the in-code comment, the letterbox was therefore considered a trusted system window, and the existing tapjacking prevention (obscured-touch logic) did not work correctly.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/Letterbox.java, InputInterceptor(String namePrefix, WindowState win) constructor: mWindowHandle.ownerPid = WindowManagerService.MY_PID and mWindowHandle.ownerUid = WindowManagerService.MY_UID are replaced with mWindowHandle.ownerPid = win.mSession.mPid and mWindowHandle.ownerUid = win.mSession.mUid. The letterbox input window takes on the identity of the app it surrounds and is no longer treated as a trusted system window for the obscured-touch logic.",
      "No other logic changes. layoutParamsType, dispatchingTimeoutMillis, scaleFactor and inputConfig (NOT_FOCUSABLE | SLIPPERY, or SPY under Flags.scrollingFromLetterbox() on 16) are unchanged. A three-line comment explains the reason."
     ],
     "completeness": [
      "The change is in the InputInterceptor constructor shown. On 16, both the SLIPPERY and the flag-gated SPY configurations are set in this same constructor, so both get the new owner identity.",
      "The fix does not change the slippery/spy inputConfig. It only changes the owner identity so that the obscured-touch checks can apply.",
      "Only Letterbox.java is changed. The input-side check logic is not modified and is not shown in the material. The commit has no automated test (Test: Manual)."
     ],
     "behavior_changes": [
      "The letterbox input window handle now carries the letterboxed app's pid/uid instead of system_server's. Per the in-code comment, it is therefore no longer treated as a trusted system window for input security checks (obscured touch logic)."
     ],
     "preconditions": [
      "An app window is shown letterboxed, so Letterbox creates an InputInterceptor (built from the letterboxed WindowState win) with a non-empty touchable region. updateTouchableRegion sets the token only when the frame is non-empty.",
      "Another activity is in a lower layer covered by the letterbox, so a touch that slips out of the letterbox reaches it (per the commit message).",
      "A user touch is required. The bulletin rates this High severity EoP."
     ],
     "per_major": {
      "14": "adapted fix (same owner pid/uid change, different context): platform/frameworks/base 8284ae9c703a in android-security-14.0.0_r28. layoutParamsType is always TYPE_INPUT_CONSUMER and inputConfig is NOT_FOCUSABLE | SLIPPERY; there is no Flags.scrollingFromLetterbox(). 7/7 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "adapted fix (same change, same context as 14): platform/frameworks/base 8eb831ece18d in android-security-15.0.0_r16. 7/7 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base 678475b0c23c in android-security-16.0.0_r7, with the Flags.scrollingFromLetterbox() context. 8/8 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin). 8/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single hunk inside the Letterbox.InputInterceptor constructor. On branches without Flags.scrollingFromLetterbox(), apply it against the TYPE_INPUT_CONSUMER / NOT_FOCUSABLE | SLIPPERY context, as the 14 and 15 commits do. It uses win.mSession.mPid/mUid from the WindowState the constructor already receives."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not include the input-side obscured-touch/trusted-window check that relied on the system owner uid.",
      "The content of the second listed Cherrypick-From commit (43e53be21c7d) is not shown.",
      "Whether other uses of this handle's ownerPid/ownerUid (for example ANR attribution) change behavior is not shown.",
      "Whether other system-owned slippery or spy input windows follow the same pattern is not shown in the material.",
      "Whether InputInterceptor is the only place the letterbox InputWindowHandle is constructed is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0048": {
     "cve": "CVE-2026-0048",
     "summary": "WindowState.hide() now cancels any running animation on a window that is being policy-hidden (force-hidden non-system overlay, suspended, app-op hidden or permanently hidden). If that window is already playing an exit animation, it is also hidden immediately without animation, instead of the hide waiting for the exit animation to finish.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Improper handling of animation state, which delays enforcement of a window-hiding policy (the overlay stays visible; the bulletin classes it as EoP)",
     "root_cause": "In WindowState.hide(boolean doAnimation, boolean requestAnim), when doAnimation was true, the only check that dropped the animation was !mToken.okToAnimate(). Suppose the window was already playing an exit animation (mAnimatingExit), for example because it was being removed with a long exit animation. Per the commit message, WSA#applyAnimationLocked then skipped the hide animation, and the hide waited for the previous exit animation to finish, which could take a long time. So a non-system overlay window that should have been force-hidden (mForceHideNonSystemOverlayWindow, for example when an activity is launched with the hide-overlay flag), or hidden because of suspension, app op or permanent hiding, could stay visible for the duration of that exit animation.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowState.java:hide(): a new block is added inside the existing 'if (doAnimation)' block, after the okToAnimate check. It runs when any of mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility or mPermanentlyHidden is set. These are the same four policy-hide conditions on which show() refuses to show the window.",
      "Inside that block, if the window isAnimating(): when mAnimatingExit is true, doAnimation is set to false, so the later 'current' computation uses isLegacyPolicyVisibility() and the hide takes effect immediately. In every animating case, cancelAnimation() is called.",
      "This is a single-commit fix. WindowStateAnimator.applyAnimationLocked itself is not changed."
     ],
     "completeness": [
      "Covers all four policy-hide conditions that show() checks: mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, !mAppOpVisibility and mPermanentlyHidden.",
      "Only acts when doAnimation is true and isAnimating() is true. If the window is animating but not exiting (mAnimatingExit false), the animation is cancelled but doAnimation stays true, so a hide animation is still attempted.",
      "Does not change the skip behavior in WSA#applyAnimationLocked. hide() calls where none of the four conditions is set are unchanged and may still be subject to the original wait.",
      "show() is not changed. The material shows no other paths that were adjusted."
     ],
     "behavior_changes": [
      "A window that is policy-hidden while playing an exit animation is now hidden immediately, with no exit animation.",
      "Any running animation on a window being policy-hidden is cancelled via cancelAnimation(). This may visibly cut short exit or other animations on such windows."
     ],
     "preconditions": [
      "A window (per the commit title and message, a non-system overlay window) is being removed with a long exit animation, so mAnimatingExit and isAnimating() are both true.",
      "A policy hide is then requested through WindowState.hide() with doAnimation true and one of these set: mForceHideNonSystemOverlayWindow (for example, an activity launched with the hide-overlay flag, per the commit's Test line), mHiddenWhileSuspended, !mAppOpVisibility or mPermanentlyHidden.",
      "mToken.okToAnimate() returns true. Otherwise doAnimation was already false and the issue did not arise."
     ],
     "per_major": {
      "14": "Same fix: platform/frameworks/base 7c5437456c5d in android-security-14.0.0_r28. Identical 10-line hunk in WindowState.hide() at a different offset (line 3102). 4/4 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/frameworks/base c9bc382318dd in android-security-15.0.0_r16. Identical hunk at line 3081. 4/4 lines present at android-security-15.0.0_r17.",
      "16": "Same fix: platform/frameworks/base 36a774d72399 in android-security-16.0.0_r7. Hunk at line 2964. 4/4 lines present at android-security-16.0.0_r8.",
      "17": "Same fix: platform/frameworks/base 6ca1d6b26237, included in the android-17.0.0_r1 base release. Hunk at line 3096. 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The hunk is identical across 14 through 17. It only uses existing WindowState fields (mForceHideNonSystemOverlayWindow, mHiddenWhileSuspended, mAppOpVisibility, mPermanentlyHidden, mAnimatingExit) and existing isAnimating()/cancelAnimation() methods. Place it right after the '!mToken.okToAnimate()' check inside the 'if (doAnimation)' block of hide()."
     ],
     "confidence": "high",
     "unknowns": [
      "The code in WindowStateAnimator.applyAnimationLocked that skips the hide animation is not shown; its behavior is known only from the commit message.",
      "The rest of hide() after the 'current' computation (including any exit-animation call) is not shown.",
      "Which hide() caller with the force-hide flag was used in the report is not shown beyond the commit's Test description.",
      "Index search returned no relevant callers, so the material does not establish reachability beyond the commit message."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0050": {
     "cve": "CVE-2026-0050",
     "summary": "AdapterService.handleBondStateChanged now runs its existing permission revocation block whenever fromState is BOND_BONDED, not only when toState is BOND_NONE. As a result, a rebond attempt by a device reusing a bonded address does not keep the previous device's granted access permissions.",
     "component": "platform/packages/modules/Bluetooth, btservice AdapterService (bond state handling / device access permissions)",
     "weakness": "Missing revocation of previously granted access permissions on a state transition (stale authorization carried over to a new peer with the same address)",
     "root_cause": "In AdapterService.handleBondStateChanged, the block that resets the device's access permissions to ACCESS_UNKNOWN (setMessageAccessPermission, setPhonebookAccessPermission, ...) ran only when toState == BOND_NONE. According to the commit message, when a device with the same address as a previously bonded device bonds again, the adapter service may not receive a BOND_NONE state change, even if the new device is different. The old permissions were then not revoked, so the user was not asked to give consent for the new device. The new unit test exercises the BOND_BONDED -> BOND_BONDING transition as the rebond case.",
     "fix_analysis": [
      "android/app/src/com/android/bluetooth/btservice/AdapterService.java:handleBondStateChanged: the condition `if (toState == BOND_NONE)` becomes `if (toState == BOND_NONE || fromState == BOND_BONDED)`. Any transition out of BOND_BONDED (including BONDED -> BONDING) now runs the existing revocation block that sets the stored access permissions to BluetoothDevice.ACCESS_UNKNOWN. A static import of BluetoothDevice.BOND_BONDED is added.",
      "On Android 17 (921aaec06df0) the new clause is gated: `|| (Flags.rebokePermissionOnUnbond() && fromState == BOND_BONDED)`. The Android 15 (da365e87dce9) and 16 (d0f7d128d436) versions have no flag check and apply the new condition unconditionally.",
      "android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java: the new test testRemovePermissionBondedToBonding mocks SharedPreferences, calls handleBondStateChanged(mDevice, BOND_BONDED, BOND_BONDING) and verifies three SharedPreferences.Editor.remove calls. On 17 the test is annotated @EnableFlags(Flags.FLAG_REBOKE_PERMISSION_ON_UNBOND)."
     ],
     "completeness": [
      "The fix covers every transition whose fromState is BOND_BONDED, in addition to the existing toState == BOND_NONE case. The revocation block itself is unchanged.",
      "The fix only works if handleBondStateChanged is actually called for the BONDED -> BONDING transition. If no bond state change is delivered at all for a same-address rebond, this check is never reached. The material does not show the native side.",
      "On Android 17 the new behavior is active only when Flags.rebokePermissionOnUnbond() is enabled. With the flag off, the original BOND_NONE-only behavior remains.",
      "Nothing changes in how permissions are granted. Only the revocation trigger is widened."
     ],
     "behavior_changes": [
      "Stored device access permissions (message, phonebook and the others in the block) are reset to ACCESS_UNKNOWN whenever a device leaves BOND_BONDED (for example to BOND_BONDING). Per the commit message, this means the user is asked for consent again with the new device.",
      "On Android 17 this is controlled by the flag com.android.bluetooth.flags.reboke_permission_on_unbond. On 15 and 16 it is always on."
     ],
     "preconditions": [
      "A remote device previously bonded with this adapter was granted access permissions, and they were stored.",
      "A device with the same Bluetooth address starts a new bond, so AdapterService.handleBondStateChanged is called with fromState == BOND_BONDED and a toState other than BOND_NONE (for example BOND_BONDING). This comes from the commit message and the new unit test."
     ],
     "per_major": {
      "14": "Not affected per the bulletin (not listed). At android-security-14.0.0_r29 only 1/8 lines of the patched region are present.",
      "15": "Adapted fix: da365e87dce9 in android-security-15.0.0_r16. Same condition change without the flag gate, plus the unit test. 7/8 lines present at android-security-15.0.0_r17.",
      "16": "Adapted fix: d0f7d128d436 in android-security-16.0.0_r7, a cherry-pick of 921aaec06df0. Same condition change without the flag gate, plus the unit test. 7/8 lines present at android-security-16.0.0_r8.",
      "17": "Same fix (base): 921aaec06df0 in android-17.0.0_r1. The new clause is gated by Flags.rebokePermissionOnUnbond() and the test is annotated with @EnableFlags. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The Android 15/16 versions use the unconditional form `toState == BOND_NONE || fromState == BOND_BONDED`, without the Flags.rebokePermissionOnUnbond() gate used on 17. Use that form on branches that lack the aconfig flag, and add the BOND_BONDED static import. The hunk sits in handleBondStateChanged right after mDatabaseManager.handleBondStateChanged(device, fromState, toState)."
     ],
     "confidence": "high",
     "unknowns": [
      "Why BOND_NONE is not delivered for a same-address rebond, and whether every rebond path produces a BONDED -> BONDING state change on the Java side.",
      "Which permission is the third one cleared in the block. Only the message and phonebook setters are visible in the diff context, and the test asserts three removals.",
      "Default state of the reboke_permission_on_unbond flag on Android 17 builds.",
      "Which callers and permissions are needed to reach handleBondStateChanged."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0055": {
     "cve": "CVE-2026-0055",
     "summary": "PackageInstallerService.createSessionInternal now validates params.volumeUuid with StorageManager.convert() when a session is created with INSTALL_FORCE_VOLUME_UUID, rejecting malformed values (such as \"../../../data\") with an IllegalArgumentException instead of carrying them further into the install stack.",
     "component": "platform/frameworks/base, PackageManager / PackageInstallerService (services/core/java/com/android/server/pm)",
     "weakness": "missing input validation of a caller-supplied volume identifier (path-like string accepted as volumeUuid)",
     "root_cause": "In services/core/java/com/android/server/pm/PackageInstallerService.java, createSessionInternal() handled the INSTALL_FORCE_VOLUME_UUID case by only OR-ing PackageManager.INSTALL_INTERNAL into params.installFlags and keeping the caller-supplied params.volumeUuid as is. No check was made that the string was a well-formed volume UUID, so a value such as \"../../../data\" (from the commit message's test output) was accepted and only failed later, deeper in the stack, where per the commit message it crashed the system; the bulletin rates the issue as a High severity EoP.",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/PackageInstallerService.java:createSessionInternal: in the `else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0)` branch, right after `params.installFlags |= PackageManager.INSTALL_INTERNAL`, the fix calls `StorageManager.convert(params.volumeUuid)` inside a try block.",
      "If StorageManager.convert() throws IllegalArgumentException, the fix rethrows a new IllegalArgumentException with the message \"Invalid volumeUuid value in session params: <volumeUuid>\", so the createSession call fails early in createSessionInternal instead of crashing the system later (per the commit message).",
      "Single-commit fix, 8 added lines, no other files changed. The return value of convert() is discarded; it is used purely as a format validator."
     ],
     "completeness": [
      "Covers only the INSTALL_FORCE_VOLUME_UUID branch of createSessionInternal.",
      "The sibling INSTALL_INTERNAL branch (lines 951-954) also leaves the caller's params.volumeUuid unchanged; it calls InstallLocationUtils.fitsOnInternal(mContext, params) and does not call StorageManager.convert(). Whether fitsOnInternal or later code validates volumeUuid there is not shown in the material.",
      "The third (default) branch resolves params.volumeUuid via InstallLocationUtils.resolveInstallVolume, so the value there is system-derived rather than caller-supplied.",
      "Validation is limited to whatever StorageManager.convert() accepts as a UUID string; the fix does not check that the UUID refers to an existing or mounted volume, and no downstream consumer of volumeUuid was changed."
     ],
     "behavior_changes": [
      "createSession requests with INSTALL_FORCE_VOLUME_UUID and a volumeUuid that StorageManager.convert() rejects now fail immediately with IllegalArgumentException(\"Invalid volumeUuid value in session params: ...\") instead of creating a session.",
      "The `pm install` shell path surfaces this as \"Exception occurred while executing 'install'\" per the commit message.",
      "No behavior change for sessions without INSTALL_FORCE_VOLUME_UUID or with a well-formed volumeUuid."
     ],
     "preconditions": [
      "Caller reaches PackageInstallerService.createSessionInternal (IPackageInstaller.createSession / the pm install shell command per the commit message's test trace).",
      "SessionParams.installFlags has INSTALL_FORCE_VOLUME_UUID set and INSTALL_INTERNAL not set, so the patched else-if branch is taken.",
      "SessionParams.volumeUuid contains a caller-controlled string that is not a valid UUID.",
      "Whether an unprivileged app can set INSTALL_FORCE_VOLUME_UUID, or what permission gates that flag, is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 06fb4af542b7 in android-security-14.0.0_r28, identical 8-line hunk in PackageInstallerService.java (at line 874); 4/4 lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit 613fa6465e4d in android-security-15.0.0_r16, identical hunk (at line 945); 4/4 lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit 62ec466efd80 in android-security-16.0.0_r7, cherry-pick of fe2d4eb0555a (Change-Id Ifbd9c37565eae47d85ab3198b7bf76fe15a20b4f), hunk at line 956; 4/4 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit c17553943026 in android-17.0.0_r1, identical hunk; 4/4 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained hunk inside the INSTALL_FORCE_VOLUME_UUID else-if of createSessionInternal; the hunk context is identical on 14/15/16/17. The diff adds no import, which implies android.os.StorageManager was already imported in PackageInstallerService.java on those branches; verify this on forks."
     ],
     "confidence": "high",
     "unknowns": [
      "Which permission or caller identity is required to use INSTALL_FORCE_VOLUME_UUID (the material does not show the enforcement for that flag).",
      "Where downstream the malformed volumeUuid was consumed and what exactly crashed or was escalated; the commit message only says it crashed the system later.",
      "Whether InstallLocationUtils.fitsOnInternal validates params.volumeUuid in the INSTALL_INTERNAL branch.",
      "Contents of the PackageInstallerServiceTest changes referenced in the commit message (not included in the diff)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0056": {
     "cve": "CVE-2026-0056",
     "summary": "Adds validation in ResStringPool::setTo so that a ResStringPool_header.styleCount that would overflow when multiplied by sizeof(uint32_t), or whose style offset array would extend past stringsStart, is rejected with BAD_TYPE instead of being accepted.",
     "component": "platform/frameworks/base, libs/androidfw (ResourceTypes.cpp, ResStringPool resource string pool parser)",
     "weakness": "Missing bounds check on an attacker-controlled count field (styleCount) leading to out-of-bounds read; also an unchecked integer multiplication (styleCount * sizeof(uint32_t)).",
     "root_cause": "ResStringPool::setTo (libs/androidfw/ResourceTypes.cpp) validated stringCount against the chunk size (headerSize + stringCount*sizeof(uint32_t) <= size, line 586 in the r8 context). It also validated stylesStart (it must lie before the end of the data and after stringsStart). It never validated styleCount itself. Nothing ensured that styleCount uint32_t style offset entries, starting at mEntryStyles, fit before stringsStart. A crafted header with a large styleCount could therefore describe a style offset array extending beyond its space, which per the commit message could cause \"potential out-of-bounds reads beyond the bounds of the string pool chunk\". In addition, styleCount * sizeof(uint32_t) was not guarded against overflow.",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo, immediately after an existing check on stylesStart versus header.size (it returns BAD_TYPE) and before mStylePoolSize is computed: rejects any styleCount greater than numeric_limits<decltype(styleCount)>::max() / sizeof(uint32_t), so the multiplication that follows cannot wrap. The rejection sets mError = BAD_TYPE and logs ALOGW 'potential integer overflow when finding style entries'.",
      "Same function, next: computes styleOffsetsStart as the byte offset of mEntryStyles from mHeader (mEntryStyles.convert<uint8_t>() - mHeader.convert<uint8_t>()). It then rejects any pool where styleCount * sizeof(uint32_t) > mHeader->stringsStart - styleOffsetsStart, i.e. where the declared style offset array would run past the start of string data. The rejection sets mError = BAD_TYPE and logs ALOGW 'style offsets extend past style data start'.",
      "libs/androidfw/tests/ResourceTypes_test.cpp (new) and libs/androidfw/Android.bp: adds two tests. ResStringPool_PotentialOverflowFindingStyles sets styleCount = UINT32_MAX. ResStringPool_HeaderStyleCountExceedsStyleOffsetCount sets styleCount = 2 for a pool with 1 style. Both expect setTo to return BAD_TYPE. The test file is added to the cc_test srcs list (libandroidfw_tests per the commit's Test line)."
     ],
     "completeness": [
      "The fix validates styleCount at parse time (setTo) only. It does not add bounds checks to the later readers of mEntryStyles, which are not shown in the material.",
      "The new check bounds the style offset array against stringsStart. It relies on stringsStart being greater than or equal to styleOffsetsStart. The material does not show an earlier check establishing that. If stringsStart were smaller, the size_t subtraction would wrap and the new comparison would pass trivially.",
      "The overflow guard divides the maximum of the 32-bit styleCount type by sizeof(uint32_t). Because the product is computed in size_t, it can only wrap where size_t is 32 bits, so the guard matters mainly for 32-bit builds.",
      "The stringCount path already had a similar bounds check (line 586 in context) and is unchanged."
     ],
     "behavior_changes": [
      "ResStringPool::setTo now returns BAD_TYPE (and logs ALOGW) in two cases: when styleCount * 4 bytes do not fit between the style offset array start and stringsStart, or when styleCount exceeds UINT32_MAX / 4.",
      "Per the commit message, an APK with an invalid styleCount now fails to install.",
      "libandroidfw_tests gains a new test source file ResourceTypes_test.cpp with two tests."
     ],
     "preconditions": [
      "A resource string pool chunk (ResStringPool_header) from an untrusted source is parsed by ResStringPool::setTo, for example from an APK (the commit message's manual test used an APK with an invalid styleCount).",
      "styleCount in the header is nonzero, and the declared styleCount needs more space than the style offsets have before stringsStart."
     ],
     "per_major": {
      "14": "same fix: commit f1c0bc9dd174 in android-security-14.0.0_r28, identical hunks (ResourceTypes.cpp at line 652, Android.bp test list, new test file); 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: commit e80f76ded67a in android-security-15.0.0_r16, identical hunks (ResourceTypes.cpp at line 670); 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: commit d5a52f737cab in android-security-16.0.0_r7 (references two Cherrypick-From internal commits, Change-Id If846443caa357197cd393e85c1461119b8264eb4); 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit d69b0c6010e5 included in android-17.0.0_r1; 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, applied verbatim on 14 through 17 with only line-offset differences. The ResourceTypes.cpp hunk goes after the existing stylesStart-vs-header.size check and right before mStylePoolSize is assigned. mEntryStyles must already be set at that point.",
      "The new test uses androidfw/StringPool.h, androidfw/Util.h (android::util::Copy) and BigBuffer. The test file is identical on all four branches, which suggests these helpers are available there. No 'Fix in fork' section was provided, so no fork verdict can be reported."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the code that performs the out-of-bounds read using styleCount (the consumers of mEntryStyles after setTo).",
      "The material does not show whether setTo is the only path through which string pools are accepted.",
      "The provided context does not show whether existing checks guarantee stringsStart >= styleOffsetsStart, i.e. that the new subtraction cannot wrap.",
      "The provided context does not show where mEntryStyles is assigned, or the message and enclosing block of the check directly before the hunk.",
      "The two internal commits referenced by Cherrypick-From are given only as URLs. Their contents are not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0059": {
     "cve": "CVE-2026-0059",
     "summary": "Adds a remaining-bytes argument and bounds check to sdpu_build_attrib_seq so that the attribute-ID sequence written into the fixed-size SDP request buffer (SDP_DATA_BUF_SIZE, osi_malloc'd) cannot exceed the buffer, and adds the missing buffer accounting (bytes_left / base_bytes) to process_service_attr_rsp.",
     "component": "platform/packages/modules/Bluetooth, system/stack/sdp (SDP client discovery: sdp_discovery.cc, sdp_utils.cc)",
     "weakness": "Missing bounds check leading to heap out-of-bounds write (buffer overflow) when serializing the SDP attribute sequence",
     "root_cause": "sdpu_build_attrib_seq(p_out, p_attr, num_attrs) wrote a 2 or 3 byte data-element header into p_out, followed by 3 bytes per attribute (num_attrs * 3, or 5 bytes for the wildcard). It had no knowledge of how much space remained in the destination. Its callers process_service_search_attr_rsp and process_service_attr_rsp build the request in a heap buffer of SDP_DATA_BUF_SIZE obtained from osi_malloc, and num_attrs comes from p_ccb->p_db->num_attr_filters. process_service_search_attr_rsp already tracked bytes_left for the header and continuation state but did not pass it to sdpu_build_attrib_seq. process_service_attr_rsp had no bytes_left accounting at all: none for the header, the 4-byte handle, the max attr count, or the remote-supplied continuation state length *p_reply. The commit message states that the attribute count can be large, so the write could run past the end of the buffer. In addition, the length was held in a uint16_t (xx = num_attrs * 3), which could truncate for large num_attrs.",
     "fix_analysis": [
      "system/stack/sdp/sdpint.h and sdp_utils.cc:sdpu_build_attrib_seq: the signature becomes sdpu_build_attrib_seq(uint8_t* p_out, uint16_t* p_attr, uint16_t num_attrs, uint16_t& bytes_left). The length is now computed in an int: content_len = 5 for the wildcard, else num_attrs * 3; header_len = 3 if content_len > 255, else 2. Before writing anything, it checks bytes_left < content_len + header_len. On failure it does DCHECK(0) << \"SDP: No space for attrib seq\" and returns p_out unchanged, with nothing written. On success it subtracts header_len + content_len from bytes_left and then emits the header and entries as before.",
      "system/stack/sdp/sdp_discovery.cc:process_service_attr_rsp: introduces uint16_t bytes_left = SDP_DATA_BUF_SIZE and computes base_bytes = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 3 (PDU header) + 2 (param len) + 4 (handle) + 2 (max attr count) + 1 (cont state len) + (p_reply ? *p_reply : 0). If base_bytes > bytes_left it calls sdp_disconnect(p_ccb, tSDP_STATUS::SDP_INVALID_CONT_STATE), frees p_msg and returns. Otherwise it does bytes_left -= base_bytes and passes bytes_left to both sdpu_build_attrib_seq calls (filtered and wildcard).",
      "system/stack/sdp/sdp_discovery.cc:process_service_search_attr_rsp: the existing bytes_left is now passed to both sdpu_build_attrib_seq calls. The base_bytes expression is rewritten as 2u + 2u + 1u (param len, max attr count, cont state len) instead of 2u + 3u. The total is unchanged; only the comments are corrected.",
      "system/stack/sdp/sdp_discovery.cc:sdp_snd_service_search_req: the same comment-only rewrite of base_bytes (2u + 2u + 1u instead of 2u + 3u); the numeric value is unchanged. This function builds a UUID sequence via sdpu_build_uuid_seq, which already took bytes_left.",
      "sdp_utils.cc: the remaining hunks (sdpu_find_ccb_by_cid, sdpu_find_ccb_by_db, sdpu_allocate_ccb, sdpu_dump_all_ccb, sdpu_get_active_ccb_cid, sdpu_process_pend_ccb_same_cid, sdpu_process_pend_ccb_new_cid, sdpu_clear_pend_ccb, sdpu_is_base_uuid, sdpu_get_attrib_seq_len) only move loop index declarations into the for statements. No functional change."
     ],
     "completeness": [
      "Both call sites of sdpu_build_attrib_seq shown in the material (process_service_search_attr_rsp and process_service_attr_rsp) now pass bytes_left, and the function no longer writes past the accounted space.",
      "On the new failure path, sdpu_build_attrib_seq only DCHECKs (no effect in release builds) and returns p_out unchanged. The overflow is prevented, but the request is not explicitly aborted inside sdpu_build_attrib_seq.",
      "process_service_attr_rsp gains the base_bytes check that process_service_search_attr_rsp already had, covering the remote-controlled continuation state length *p_reply and the 4-byte record handle.",
      "The uint16_t truncation of num_attrs * 3 is removed by computing content_len as an int. A content_len above 0xFFFF cannot pass the bytes_left check, because bytes_left is a uint16_t bounded by SDP_DATA_BUF_SIZE."
     ],
     "behavior_changes": [
      "process_service_attr_rsp now disconnects the SDP connection with tSDP_STATUS::SDP_INVALID_CONT_STATE when the fixed header plus continuation state does not fit in SDP_DATA_BUF_SIZE (previously there was no check).",
      "When the attribute sequence does not fit, sdpu_build_attrib_seq writes nothing and returns the input pointer (DCHECK in debug builds).",
      "The signature of sdpu_build_attrib_seq in sdpint.h changes (extra uint16_t& bytes_left parameter); any out-of-tree caller must be updated.",
      "No feature flag: Flag: EXEMPT BUGFIX."
     ],
     "preconditions": [
      "The device performs SDP client discovery against a remote device (local code sets up a tSDP_DISCOVERY_DB with attr_filters / num_attr_filters) and processes a response that drives process_service_search_attr_rsp or process_service_attr_rsp to build the next request.",
      "Per the commit message, the discovery database holds a large number of attribute filters (num_attr_filters), so that num_attrs * 3 plus headers exceeds the space left in the SDP_DATA_BUF_SIZE buffer. In process_service_attr_rsp, the remote-supplied continuation state length (*p_reply) also reduces the available space.",
      "The value of SDP_DATA_BUF_SIZE and who controls num_attr_filters are not shown in the material."
     ],
     "per_major": {
      "14": "Adapted fix: [RESTRICT AUTOMERGE] commit ce4be454822b in android-security-14.0.0_r28. Same logic, adapted to the older code style: SDP_STATE_* constants, memset-based ccb reset, unbraced if/else, and SDP_INVALID_CONT_STATE without enum scope in process_service_search_attr_rsp. At android-security-14.0.0_r29, 5/8 lines of the patched region are present.",
      "15": "Adapted fix: commit 59f3f429e37e in android-security-15.0.0_r16. Same three files and logic with older formatting; the comment text in process_service_search_attr_rsp differs slightly from the Android 16 version. At android-security-15.0.0_r17, 5/8 lines of the patched region are present.",
      "16": "Same fix: commit 94566a29d020 in android-security-16.0.0_r7 (cherry-pick of ce1de9ef78ca, Change-Id I6c0a8ee92c3b4d25849f29b7023c05694deec1e6). At android-security-16.0.0_r8, 6/8 lines of the patched region are present; the r8 context shown contains the updated sdpu_build_attrib_seq prototype and the base_bytes comments.",
      "17": "Resolved from base: [RESTRICT AUTOMERGE] commit 5288e3e85ba9 in android-17.0.0_r1. Its sdp_discovery.cc and sdpint.h hunks have the same blob hashes as the Android 14 diff; sdp_utils.cc has different hashes and offsets but the same logic. At android-security-17.0.0_r1, 5/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single commit. It relies on the pre-existing bytes_left accounting in sdp_snd_service_search_req / process_service_search_attr_rsp and on the bytes_left-aware sdpu_build_uuid_seq. If a tree lacks those, port that change first or add bytes_left locally. The loop-index cleanups in sdp_utils.cc are optional and can be dropped to reduce conflicts.",
      "Google used [RESTRICT AUTOMERGE] per-branch versions for 14 and 17. When adapting, match the SDP_INVALID_CONT_STATE spelling (enum-scoped or bare) to the target branch."
     ],
     "confidence": "high",
     "unknowns": [
      "The value of SDP_DATA_BUF_SIZE and the maximum of num_attr_filters / the attr_filters array size in tSDP_DISCOVERY_DB are not in the material, so the exact overflow margin is unknown.",
      "Whether num_attr_filters is bounded where the discovery database is populated is not shown.",
      "What the callers do with the request after sdpu_build_attrib_seq fails (whether it is still sent without an attribute list) is not fully shown.",
      "Whether and how a remote peer alone (without a large local filter list) can reach the overflow condition is not shown. The bulletin rates it High / RCE, but the material does not describe the trigger.",
      "The reason for the partial (5/8, 6/8) patched-region matches at the latest security tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0060": {
     "cve": "CVE-2026-0060",
     "summary": "The Settings developer option \"Experimental: Enable ANGLE\" sets ANGLE as the system OpenGL ES driver (persist.graphics.egl), which the commit message says can leave a commercial device unusable until a factory reset. The Android 16 fix only adds warning text (back up your data; a factory reset may be needed) to the switch summary and the reboot dialog. The Android 14 fix also gates the switch behind a debug system property.",
     "component": "platform/packages/apps/Settings, Developer options / Graphics Driver Preferences (GraphicsDriverEnableAngleAsSystemDriverController, res/values/strings.xml)",
     "weakness": "A dangerous developer-option toggle (switching the system GLES driver to ANGLE) without adequate safeguard or warning. The outcome is persistent denial of service: the device is unusable until a factory reset, per the commit message. The diff shows no memory-safety or permission flaw.",
     "root_cause": "The \"Experimental: Enable ANGLE\" switch in Developer options makes ANGLE the default system OpenGL ES driver. The controller references PROPERTY_PERSISTENT_GRAPHICS_EGL = \"persist.graphics.egl\", and the change needs a reboot. Commit dc00e06e9bb1 says the switch is meant for dogfood users and developers, and that on released commercial devices toggling it \"can brick the device into unusable state and need a factory reset to fix\". Before the fix, the summary string (enable_angle_as_system_driver_summary) and the reboot dialog string (reboot_dialog_enable_angle_as_system_driver) mentioned only app incompatibility and the need to reboot. They gave no warning about data loss or a factory reset.",
     "fix_analysis": [
      "Android 16 commit 1 (dc00e06e9bb1, res/values/strings.xml): adds 'Please backup your device data before turning on the switch' to enable_angle_as_system_driver_summary, and 'Please back up your device data before rebooting. In case the reboot failed, you need a factory reset to restore the device' to reboot_dialog_enable_angle_as_system_driver. No Java or controller change.",
      "Android 16 commit 2 (9666d567fd5a, 84 res/values-*/strings.xml files): imports translations of the two new strings. The msgids change from 2170215556348477481/2619263039763150810 to 1552818142467085308/14147627096976157.",
      "Android 16 commit 3 (32c6cb147d3e, res/values/strings.xml): rewords the two English strings to match the partner's style ('Warning: ANGLE will be set as the default OpenGL ES driver... Back up any important data before turning this on.' and 'To change the system OpenGL ES driver, the device will restart. Back up any important data before restarting. If restarting fails, a factory reset will be needed to use the device.').",
      "Android 14 commit (2097b24534af): GraphicsDriverEnableAngleAsSystemDriverController gains PROPERTY_DEBUG_ANGLE_DEVELOPER_OPTION = 'debug.graphics.angle.developeroption.enable' and isAngleDeveloperOptionEnabled(). The method that disables the preference when ANGLE is not supported now also calls mPreference.setEnabled(false) unless that property is true (it can be set with adb shell setprop). The method that sets mShouldToggleSwitchBackOnRebootDialogDismiss also logs the current persist.graphics.egl value. GraphicsDriverSystemPropertiesWrapper gains getBoolean(), and the tests override the property to true. The commit also changes the title to 'Experimental: Enable ANGLE' and the summary to the 'Warning: ...' text that Android 16 had before its fix."
     ],
     "completeness": [
      "On Android 16, and in the 15/17 commits shown, the fix is text-only. It does not change what the switch does, does not block the driver change, and adds no recovery path if the device fails to boot with ANGLE. The mitigation relies on the user reading the warning and backing up.",
      "The controller gate (debug.graphics.angle.developeroption.enable) appears only in the Android 14 commit. The material does not show the 15/16/17 controller code, so it cannot confirm whether those branches have the gate.",
      "The translations imported in 9666d567fd5a match the first English wording (dc00e06e9bb1), not the restyled wording of 32c6cb147d3e.",
      "The per-app ANGLE selection strings in the same section are untouched, which fits the commit scope."
     ],
     "behavior_changes": [
      "Developer options UI: the ANGLE switch summary and the reboot confirmation dialog now tell the user to back up device data and say that a factory reset may be needed if the reboot fails. Translations of this text were imported on 15, 16 and 17.",
      "Android 14 only: the 'Experimental: Enable ANGLE' preference is disabled unless debug.graphics.angle.developeroption.enable is true, and the current persist.graphics.egl value is logged at verbose level."
     ],
     "preconditions": [
      "Access to Settings > Developer options > Graphics Driver Preferences, with Developer options enabled.",
      "The Android 14 controller checks ANGLE support (ro.gfx.angle.supported via isAngleSupported()). The diff does not show the full enable condition.",
      "On Android 14 after the fix, debug.graphics.angle.developeroption.enable must also be true.",
      "The user confirms the reboot dialog; the driver change takes effect after reboot."
     ],
     "per_major": {
      "14": "Adapted fix: commit 2097b24534af in android-security-14.0.0_r28 adds the debug.graphics.angle.developeroption.enable gate in GraphicsDriverEnableAngleAsSystemDriverController, plus the wrapper and test changes. It also changes the title and summary to 'Experimental: Enable ANGLE' / 'Warning: ...'. The back-up-data strings are not shown for 14. At android-security-14.0.0_r29, 5 of 8 lines of the patched region are present.",
      "15": "Same fix (strings): translation import 2b5834d47f28 in android-security-15.0.0_r16 carries the back-up-data wording (msgids 1552818142467085308 / 14147627096976157). The default strings.xml commit for 15 is not shown. At android-security-15.0.0_r17, 6 of 8 lines are present.",
      "16": "Same fix: three commits in android-security-16.0.0_r7 (dc00e06e9bb1 strings, 9666d567fd5a translations, 32c6cb147d3e restyled English). At android-security-16.0.0_r8, 6 of 8 lines are present and the restyled text is in res/values/strings.xml.",
      "17": "Same fix (strings): translation import 1c2cf9256912 is in the base tag android-17.0.0_r1. At android-security-17.0.0_r1, only 4 of 8 lines of the patched region are present, so the restyled English wording may not be there."
     },
     "backport_notes": [
      "Order for branches like 16: dc00e06e9bb1 (default strings), then 9666d567fd5a (translations; marked 'DO NOT MERGE ANYWHERE', so branch-specific), then 32c6cb147d3e (restyle).",
      "The text-only commits add no functional safeguard. Consider also taking the Android 14 controller gate (2097b24534af) on branches that lack it. The material does not show whether 15/16/17 already have it."
     ],
     "confidence": "medium",
     "unknowns": [
      "How exactly enabling ANGLE as the system GLES driver leaves the device unusable. Only the commit message states it.",
      "Whether the Android 15/16/17 branches contain the debug.graphics.angle.developeroption.enable gate, and the full condition under which the preference is enabled before the fix.",
      "Whether the restyled English wording of 32c6cb147d3e was later translated.",
      "Whether the back-up-data default strings commit was applied on 15 and 17. Only translation imports are shown for them, and only the gate commit for 14.",
      "Why the bulletin rates a text-only mitigation as the fix for a High DoS."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0061": {
     "cve": "CVE-2026-0061",
     "summary": "WindowManager now applies legacy policy visibility (hide/show) to the server-owned WindowState surface (mSurfaceControl) in addition to the existing mWinAnimator hide, so a client cannot undo a policy-driven hide by re-showing its own ViewRootImpl surface.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowState.java)",
     "weakness": "Server-side visibility policy enforced only on a client-controllable object (missing enforcement on the server-owned surface), allowing bypass of policy hiding",
     "root_cause": "When WindowState decided a window was not visible by policy, it did not hide its own surface (mSurfaceControl). checkPolicyVisibilityChange() only called mWinAnimator.hide(...). According to the commit message, the client side can access the ViewRootImpl surface and change its visibility, so an app could re-show its window (for example an overlay) after the server had hidden it by policy. The WindowState surface, which the client cannot access, was not hidden or shown by the policy paths in checkPolicyVisibilityChange(), hide() or show().",
     "fix_analysis": [
      "WindowState.java:checkPolicyVisibilityChange: when !isVisibleByPolicy(), right after mWinAnimator.hide(...), the fix also calls getPendingTransaction().hide(mSurfaceControl) if mSurfaceControl != null. This path runs when legacy policy visibility changes after an animation.",
      "WindowState.java:show(...): after setting LEGACY_POLICY_VISIBILITY and mLegacyPolicyVisibilityAfterAnim = true, the fix calls getPendingTransaction().show(mSurfaceControl) if mSurfaceControl != null. This happens before the optional TRANSIT_ENTER animation.",
      "WindowState.java:hide(...): right after the mFocusMayChange handling, inside the same block, the fix calls getPendingTransaction().hide(mSurfaceControl) if mSurfaceControl != null.",
      "WindowStateTests.java:testIsOnScreen_hiddenByPolicy: verifies mTransaction.hide(window.mSurfaceControl) after hide(false,false) and mTransaction.show(window.mSurfaceControl) after show(false,false)."
     ],
     "completeness": [
      "Covers the legacy policy visibility transitions in WindowState that are shown in the material: hide(), the deferred hide in checkPolicyVisibilityChange(), and re-show in show().",
      "All three new operations are guarded by mSurfaceControl != null. If the WindowState surface does not exist yet when policy changes, nothing is recorded on it. The material does not show whether surface creation later checks policy visibility.",
      "Only the LEGACY_POLICY_VISIBILITY paths are changed. Any other callers of setPolicyVisibilityFlag/clearPolicyVisibilityFlag are not in the material and are not touched by this commit.",
      "The new hide in checkPolicyVisibilityChange() uses getPendingTransaction(). The existing mWinAnimator.hide call uses getPendingTransaction() on 15/16 but getGlobalTransaction() on 14/17, so on 14/17 the two operations may be applied in different transactions."
     ],
     "behavior_changes": [
      "Policy-hidden windows now have their WindowState surface hidden by the server, which is intended to stop a client from making the window reappear by changing its ViewRootImpl surface visibility.",
      "show() now explicitly shows mSurfaceControl in the pending transaction when policy visibility is restored, whether or not doAnimation is set.",
      "Unit test testIsOnScreen_hiddenByPolicy now asserts the hide/show calls on window.mSurfaceControl."
     ],
     "preconditions": [
      "A window owned by the attacker's process that the server hides via legacy policy visibility. The commit's manual test uses an overlay window that should not be visible when Settings is launched.",
      "The WindowState has a live mSurfaceControl when the policy hide is applied (otherwise the new code does nothing).",
      "The client has access to its ViewRootImpl surface (per the commit message)."
     ],
     "per_major": {
      "14": "same fix: b53981d87f80 in android-security-14.0.0_r28. The same three WindowState.java hunks (9 added lines) at shifted offsets. Here mWinAnimator.hide uses getGlobalTransaction(), while the new code still uses getPendingTransaction(). The test context uses ANIMATION_TYPE_WINDOW_ANIMATION. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: 0922d84b1c64 in android-security-15.0.0_r16, identical change at shifted offsets. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: 4518f78f8b34 in android-security-16.0.0_r7 (cherry-pick, Change-Id I326fcaf71f901a11cf4aee3d734494090a1d0fb3). 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: e58fd9cd11e4 in the base release android-17.0.0_r1 (mWinAnimator.hide uses getGlobalTransaction()). 7/8 lines of the patched region matched at android-security-17.0.0_r1; the material does not say why one line differs."
     },
     "backport_notes": [
      "A single commit made of three small additive blocks in WindowState.checkPolicyVisibilityChange(), show() and hide(). The same hunks appear in the 14, 15, 16 and 17 commits, even though the nearby mWinAnimator.hide call uses getGlobalTransaction() on 14/17 and getPendingTransaction() on 15/16."
     ],
     "confidence": "high",
     "unknowns": [
      "The commit message describes the client-side mechanism (changing the ViewRootImpl surface visibility) only at a high level; no client code is in the material.",
      "Which policy decisions clear LEGACY_POLICY_VISIBILITY in practice, beyond the commit's test scenario (an overlay window that should be hidden when Settings is launched), is not shown.",
      "Whether the surface creation path honors policy visibility when mSurfaceControl is null at hide time is not shown.",
      "The condition guarding the new hide in hide() is not visible in the diff context.",
      "Why the Android 17 region matches 7/8 rather than 8/8 lines is not shown in the material.",
      "Whether the per-major cherry-picks applied cleanly is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0067": {
     "cve": "CVE-2026-0067",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error from library-supplied handlers instead of going to the ubsan_minimal runtime, which hard-aborts the process (per commit message).",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new UBSan handler runtime",
     "weakness": "Integer overflow in libdng_sdk arithmetic, which the UBSan minimal runtime turns into a process abort (DoS). The specific overflowing expressions are not shown in the material.",
     "root_cause": "libdng_sdk-defaults enables sanitize.misc_undefined = [unsigned-integer-overflow, signed-integer-overflow] (Android.bp lines 144-149). With this configuration Soong links the minimal UBSan runtime, whose handlers abort the process on a detected overflow (general platform knowledge; the commit message calls it a 'hard abort'). The library is built with rtti: true and cppflags -fexceptions. Input that drives add/sub/mul/negate/div arithmetic inside libdng_sdk to overflow therefore aborted the hosting process. The commit references seven bug IDs. It changes none of the arithmetic sites.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true } so that, per the comment, the ubsan_minimal runtime is not used. It then re-enables the two checks through cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow together with -fno-sanitize-link-runtime, so the compiler still emits the checks but links no UBSan runtime.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs, so the library supplies its own handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines hidden-visibility extern \"C\" handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\"). libdng_sdk-defaults sets rtti: true and -fexceptions, so the throw can propagate as a C++ exception. Where it is caught is not shown.",
      "OWNERS: adds a bug component and includes the frameworks/base graphics OWNERS (non-functional)."
     ],
     "completeness": [
      "This is a generic mitigation. The overflowing arithmetic in the dng_sdk sources is not corrected; detections are converted from an abort into an exception.",
      "Only the five recoverable handler names are provided (add, sub, mul, negate, divrem). The *_abort variants are not defined, so the build depends on these checks staying in Clang's default recover mode.",
      "The handlers are visibility(\"hidden\") and the -fsanitize cflags apply only to the libdng_sdk module. Overflows in dependencies (libz, libjpeg) or in callers are not covered.",
      "The exception prevents an abort only if a catch block exists on every path that reaches it. The material does not show the catch sites, so whether every entry point into libdng_sdk handles std::runtime_error cannot be verified from the diff."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error with the message \"ubsan: add-overflow\", \"sub-overflow\", \"mul-overflow\", \"negate-overflow\" or \"divrem-overflow\" instead of aborting.",
      "No UBSan runtime is linked into libdng_sdk. The replacement handlers log nothing; they only throw."
     ],
     "preconditions": [
      "A process that links libdng_sdk parses a crafted DNG file whose contents make integer arithmetic in the library overflow.",
      "The commit's test (atest ImageDecoderTest#testInterestingDngs) points to the ImageDecoder path as an affected consumer. The exact entry points are not shown.",
      "Index search results (frameworks/base android_hardware_camera2_DngCreator.cpp, frameworks/av img_utils DngUtils.cpp) are DNG writers and do not clearly show use of libdng_sdk. They are not reachability evidence."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28 (resolved by change_id). The Android.bp and ubsan_throwing_runtime.cpp hunks are identical (blob ids 1978b39..3d95a7b); the OWNERS hunk differs only in the previous owner names. At android-security-14.0.0_r29, 3/5 lines of the patched region are present.",
      "15": "same fix: 1ec41401b3d4 in android-security-15.0.0_r16 (resolved by change_id). The Android.bp and runtime hunks are identical and OWNERS differs trivially. At android-security-15.0.0_r17, 3/4 lines of the patched region are present.",
      "16": "same fix: 74a5a91d169a in android-security-16.0.0_r7 (resolved by change_id). It lists three Cherrypick-From commits. The code context at android-security-16.0.0_r8 shows the sanitize/cflags/srcs block and the runtime file; the resolver reports 3/5 lines of the patched region present.",
      "17": "adapted fix: 81ded662eee0 in android-17.0.0_r1 (resolved by related bug), one of the Cherrypick-From commits. The Android.bp hunk is at a different offset (line 105), and the new source file is appended to the module's inline srcs list instead of a new srcs block. There is no OWNERS change. At android-security-17.0.0_r1, 5/6 lines of the patched region are present."
     },
     "backport_notes": [
      "Apply the sanitize/cflags/srcs changes to the libdng_sdk cc_library, not to libdng_sdk-defaults, and check that the defaults still set rtti: true and cppflags -fexceptions. On trees where the module lists its sources inline (as on 17), append the new file to that srcs list.",
      "Do not add -fno-sanitize-recover for these checks. With it, the compiler references *_abort handler symbols that the replacement runtime does not define."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic expressions in dng_sdk overflow for the seven referenced bugs; the diff changes no parsing code.",
      "Whether the three Cherrypick-From commits contained anything beyond the content shown.",
      "Which processes or entry points link libdng_sdk, and whether all of them catch std::runtime_error (only the ImageDecoder test is referenced).",
      "Whether sanitize.never: true also disables other Soong-managed sanitizers for libdng_sdk on builds that enable them.",
      "Why the resolver reports only partial patched-region line matches on the 14, 15 and 16 security tags.",
      "Whether libdng_sdk is a mainline module or platform-only on these branches."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 9,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0070": {
     "cve": "CVE-2026-0070",
     "summary": "Adds a new framework resource list application_hidden_policy_exempt_apps (com.android.nfc, com.android.settings, com.android.providers.settings) and makes DevicePolicyManagerService.setApplicationHidden refuse to hide any package on that list, because hiding them in a work profile could prevent the device from booting.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (device policy / managed profile app hiding)",
     "weakness": "Missing exemption check for device-critical packages in a privileged policy API (setApplicationHidden), leading to denial of service (boot failure)",
     "root_cause": "DevicePolicyManagerService.setApplicationHidden only skipped packages returned by listPolicyExemptAppsUnchecked(mContext). The AOSP policy_exempt_apps array (core/res/res/values/policy_exempt_apps.xml) is empty. There was no built-in list of packages that must never be hidden, so a device policy caller could hide system packages such as com.android.settings, com.android.providers.settings or com.android.nfc. Per the commit message, hiding these apps in the work profile may cause failure to boot.",
     "fix_analysis": [
      "core/res/res/values/policy_exempt_apps.xml: adds a new non-translatable string-array application_hidden_policy_exempt_apps containing com.android.nfc, com.android.settings and com.android.providers.settings. Its comment describes these as apps that will never be affected by DevicePolicyManager.setApplicationHidden.",
      "core/res/res/values/symbols.xml: exposes the new array as a java-symbol (R.array.application_hidden_policy_exempt_apps) so the system server can read it.",
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java: adds private isPackageExemptFromHiding(String packageName), which reads R.array.application_hidden_policy_exempt_apps via mContext.getResources().getStringArray and returns true on an exact String.equals match.",
      "DevicePolicyManagerService.setApplicationHidden: the existing early-return condition `exemptApps.contains(packageName)` becomes `exemptApps.contains(packageName) || isPackageExemptFromHiding(packageName)`. For the new critical packages, the method now logs 'ignoring %s as it's on policy-exempt list' and returns false. In the 15/16 code this check comes after the enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, ...) call.",
      "DevicePolicyManagerService.dumpResources: also dumps the new array under the name application_hidden_policy_exempt_apps, so it is visible in dumpsys."
     ],
     "completeness": [
      "Covers only the setApplicationHidden entry point. The diff does not consult the new list anywhere else. Other device-policy mechanisms that affect package availability are not touched by this change. One example is the suspension path, which has its own separate config_packagesExemptFromSuspension list (visible in dumpResources).",
      "The protection is a static allowlist of three exact package names. Equally critical packages that are not listed (including vendor or OEM equivalents) are not protected unless the OEM overlays the array. Unlike policy_exempt_apps, this list gets no companion vendor_* array.",
      "The check applies to every user, not only work profiles, because isPackageExemptFromHiding looks only at the package name. This is broader than the boot-failure scenario described in the commit message.",
      "Matching is exact String.equals on the package name, so there is no case or prefix handling. The resource is read on every call rather than cached."
     ],
     "behavior_changes": [
      "DevicePolicyManager.setApplicationHidden now returns false and takes no action for com.android.nfc, com.android.settings and com.android.providers.settings (and any OEM-overlaid additions). It logs a debug message saying the package is on the policy-exempt list.",
      "The device policy dump output gains an application_hidden_policy_exempt_apps section.",
      "The fix has no code that changes existing hidden state. It only blocks new requests for the listed packages."
     ],
     "preconditions": [
      "The caller must reach DevicePolicyManagerService.setApplicationHidden and pass enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, ...). In the 15/16 diffs this call appears immediately before the patched check.",
      "Per the commit message, the boot-failure effect arises when the critical apps are hidden in the work (managed) profile.",
      "The default list works only on builds that ship the listed packages under those exact names, or that overlay the array."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 987b313aefa0 in android-security-14.0.0_r28, with identical resource, symbol and Java changes. Only the surrounding context differs: the symbols.xml location, and the patched condition follows a closing brace instead of enforcePermission. 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base 060fe5399350 in android-security-15.0.0_r16, identical changes; 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base dc3785a739bc in android-security-16.0.0_r7 (cherry-pick of internal 79cca47a6201 / b4b10f40dfd9, Change-Id I2ade10f20636fe8dfe875f4827e5e97d734bf8a1); 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base 4a97f5239d57 in android-17.0.0_r1; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit, but all three files must be ported together: the string-array in core/res/res/values/policy_exempt_apps.xml, the java-symbol in core/res/res/values/symbols.xml (without it R.array.application_hidden_policy_exempt_apps does not resolve), and the DevicePolicyManagerService changes. The resource lives in framework-res, so framework-res must be rebuilt (general platform knowledge). OEMs whose Settings or NFC packages use different names need to overlay the array.",
      "The patched line is identical in all branches. Only the preceding context line differs (the enforcePermission line in 15/16, a closing brace in 14/17), so the Java hunk may need manual adjustment. Validation test: CtsDevicePolicyManagerTestCases ManagedProfileTest#testHideAllApps."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact mechanism by which hiding these packages causes boot failure is not shown beyond the commit message statement.",
      "The material does not show what listPolicyExemptAppsUnchecked returns (for example, whether it includes vendor_policy_exempt_apps).",
      "The material does not show whether other DevicePolicyManager paths (suspension, uninstall blocking, component disabling) can produce the same effect.",
      "The content of the two internal cherry-picked commits is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0074": {
     "cve": "CVE-2026-0074",
     "summary": "Adds hidden per-process/per-thread default OnHeaderDecodedListener hooks to android.graphics.ImageDecoder and registers a Launcher-process default listener (LauncherProcessImageListener) that downscales images whose estimated decoded size exceeds 300 MB and rejects mime types outside an allowlist, to limit memory use when the Launcher decodes large images.",
     "component": "platform/frameworks/base (android.graphics.ImageDecoder) and platform/packages/apps/Launcher3 (quickstep process initialization)",
     "weakness": "Missing bound on decoded image dimensions in the Launcher process (uncontrolled memory allocation leading to DoS)",
     "root_cause": "Before the fix, ImageDecoder.callHeaderDecoded only invoked a listener explicitly passed to the decode call, and the Launcher process set no size limit on images it decoded through ImageDecoder. The new listener's comment states the motivation: \"Remote size may have returned a giant image, so we need to defensively limit it to something reasonable.\" A sufficiently large image (width x height) decoded in the Launcher process could therefore be allocated at full size (the fix assumes a worst case of 8 bytes per pixel for RGBA_F16), consuming excessive memory in the Launcher process. The bulletin rates this High / DoS. The specific Launcher decode call sites that receive externally supplied images are not shown in the material.",
     "fix_analysis": [
      "1. platform/frameworks/base graphics/java/android/graphics/ImageDecoder.java: adds static sDefaultProcessListener (guarded by a dedicated sListenerLock object because the field is nullable) and a ThreadLocal sDefaultThreadListener, plus @hide static accessors setDefaultThreadListener/getDefaultThreadListener and setDefaultProcessListener/getDefaultProcessListener.",
      "2. ImageDecoder.callHeaderDecoded(listener, src): now reads the thread default and process default; if any of the three listeners is non-null it builds the ImageInfo (Size(mWidth, mHeight), mAnimated, mime type, color space), runs the thread default if present, otherwise the process default, and then always runs the listener passed to the decode call last. Previously the body only ran when the passed-in listener was non-null.",
      "3. platform/packages/apps/Launcher3 quickstep/res/values/config.xml: adds integer max_launcher_memory_mb = 300 and string-array allowed_image_mime_types (image/png, image/jpeg, image/webp, image/gif, image/bmp, image/x-ico, image/vnd.wap.wbmp, image/heif, image/heic, image/avif).",
      "4. Launcher3 quickstep/src/com/android/quickstep/LauncherProcessImageListener.kt (new): implements ImageDecoder.OnHeaderDecodedListener. onHeaderDecoded first lower-cases info.mimeType (Locale.US) and throws IOException(\"Image mime type (...) is not allowed.\") if it is null or not in allowedMimeTypes. It then calls getPreferredSize(width, height, maxMemoryBytes): estimatedSize = width*height*8 (BYTES_PER_PIXEL, long arithmetic); if estimatedSize > maxMemoryBytes, scale = sqrt((maxMemoryBytes/8) / (width*height)) and target dims are max(1, (dim*scale + 0.5).toInt()), passed to decoder.setTargetSize; otherwise no change. An ImageDecoderWrapper interface and AndroidImageDecoderWrapper exist only to allow unit testing.",
      "5. Launcher3 quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java: init(context) now calls setupImageDecoder(context), which reads the two resources and calls ImageDecoder.setDefaultProcessListener(new LauncherProcessImageListener(maxMemoryBytes, allowedMimeTypes)), so the limit applies as a process-wide default to ImageDecoder decodes in the quickstep Launcher process.",
      "6. Launcher3 quickstep/tests/multivalentTests/.../LauncherProcessImageListenerTest.kt (new): unit tests for the scaling math and the mime allowlist."
     ],
     "completeness": [
      "The hook lives only in ImageDecoder.callHeaderDecoded, so only decodes performed through android.graphics.ImageDecoder in the Launcher process are limited. Any Launcher image decoding that does not go through ImageDecoder is not covered by this mechanism (judged from the code: no other decode path is touched).",
      "The default listener is registered only in QuickstepProcessInitializer (quickstep/ source set). Launcher3 variants that do not use QuickstepProcessInitializer do not register it.",
      "By design, a thread-level default listener (setDefaultThreadListener), if any code in the process sets one, causes the process-level Launcher listener to be skipped on that thread. A listener passed at decode time runs after the default and may call setTargetSize again, overriding the downscale.",
      "The limit is a heuristic: 8 bytes/pixel worst case against a 300 MB resource-configured budget. The listener does not treat animated images (ImageInfo.mAnimated) differently.",
      "The frameworks/base commit message references a Flag (com.android.graphics.hwui.flags.image_decoder_default_listener) and new CTS tests, but the diff shown contains no flag check and no test files (1 file changed), so the shown code is unconditionally active.",
      "The disallowed-mime path converts the decode into an IOException; how Launcher call sites handle that exception is not shown."
     ],
     "behavior_changes": [
      "ImageDecoder.callHeaderDecoded now allocates an ImageInfo and invokes default listeners even when the decode call passed no listener, if a thread or process default is set.",
      "New @hide static APIs on ImageDecoder: setDefaultThreadListener, getDefaultThreadListener, setDefaultProcessListener, getDefaultProcessListener.",
      "In the quickstep Launcher process, any ImageDecoder decode whose estimated decoded size (w*h*8 bytes) exceeds 300 MB is transparently downscaled (aspect ratio preserved) via setTargetSize; results may be smaller than the source image.",
      "In the quickstep Launcher process, ImageDecoder decodes of images whose mime type is null or not in allowed_image_mime_types now fail with IOException(\"Image mime type (...) is not allowed.\").",
      "These limits apply process-wide, including to decodes that supply their own OnHeaderDecodedListener (default runs first, the supplied listener runs last)."
     ],
     "preconditions": [
      "An image is decoded via android.graphics.ImageDecoder inside the quickstep Launcher process; the listener comment indicates the image may originate from a remote source (\"Remote size may have returned a giant image\").",
      "The image has dimensions large enough that w*h*8 bytes exceeds the configured budget (300 MB default), or a mime type outside the allowlist.",
      "The exact Launcher code paths that decode externally supplied images are not shown in the material (unknown)."
     ],
     "per_major": {
      "14": "Adapted fix: Launcher3 c466fd8d9215 in android-security-14.0.0_r28 (same LauncherProcessImageListener.kt, config.xml and test; the QuickstepProcessInitializer hunk is inserted after ThreadedRenderer.setContextPriority, and the config.xml context is taskbar_icon_size 48.4). 8/8 lines of the patched region present at android-security-14.0.0_r29. No frameworks/base ImageDecoder commit is listed for 14 in the material.",
      "15": "Adapted fix: Launcher3 8fd9af41d56b in android-security-15.0.0_r16 (identical new files; QuickstepProcessInitializer hunk after the BinderTracker block). 8/8 lines present at android-security-15.0.0_r17. No frameworks/base ImageDecoder commit is listed for 15 in the material.",
      "16": "Same fix: frameworks/base 4abc0b8da1e9 (ImageDecoder default listeners) and Launcher3 8c33391368b8 (LauncherProcessImageListener) both in android-security-16.0.0_r7; 7/7 lines present at android-security-16.0.0_r8.",
      "17": "Same Launcher3 change with adapted context: Launcher3 56e4dc5d2b9a in android-17.0.0_r1 (resolver status 'base'). The QuickstepProcessInitializer hunk comes after the BinderTracker block, and the config.xml context is taskbar_icon_size 48.4. 7/8 lines of the patched region present at android-security-17.0.0_r1, so one line differs from the reference region. No frameworks/base commit is listed for 17 in the material."
     },
     "backport_notes": [
      "Apply the frameworks/base ImageDecoder change first: the Launcher3 commit calls the @hide ImageDecoder.setDefaultProcessListener, which will not compile or will fail at runtime without it. The material lists the base commit only for Android 16, so confirm ImageDecoder has setDefaultProcessListener on the target branch before taking the Launcher3 change. Launcher3 quickstep builds against the framework internal API, so hidden methods are reachable (general platform knowledge).",
      "The Launcher3 patch is self-contained new files plus two small hunks; only the QuickstepProcessInitializer.init insertion point and config.xml trailing context differ per branch."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which Launcher code paths decode externally supplied images and how the oversized image reaches the Launcher process.",
      "Whether the frameworks/base ImageDecoder change was shipped on Android 14, 15 and 17 branches (only the Launcher3 commits are listed for those majors).",
      "Whether the referenced flag com.android.graphics.hwui.flags.image_decoder_default_listener gates anything; no flag check appears in the diffs.",
      "The CtsGraphicsTestCases additions named in the base commit message are not in the shown diff.",
      "Which line differs in the Android 17 patched region (7/8)."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0075": {
     "cve": "CVE-2026-0075",
     "summary": "ContactsProvider2 now replaces SQLiteException messages containing \"json\" with a generic \"Stripped exception message\" for callers that fail checkCallingPermission(READ_PERMISSION) (query) or checkCallingPermission(WRITE_PERMISSION) (insert/update/delete). It also consumes the query cursor inside the try block, so SQLite errors raised while the cursor is filled reach the same stripping path.",
     "component": "platform/packages/providers/ContactsProvider, ContactsProvider2 (CP2) query/insert/update/delete entry points",
     "weakness": "Information exposure through SQLite error messages (a side channel built from caller-influenced SQL error text); no strict SQL checks on caller input",
     "root_cause": "ContactsProvider2.query/insert/update/delete caught any Exception, logged it and rethrew it unchanged to the caller. The commit message says CP2 imposed no strict SQL checks, so callers without READ_CONTACTS could send malformed queries and parse the text of the resulting SQLiteException as a side channel. The fix comment links this to certain \"json\" tokens. In query(), cursor.getCount() was called in the finally block, so a SQLiteException raised while the cursor was materialized was not handled by the catch block.",
     "fix_analysis": [
      "src/com/android/providers/contacts/ContactsProvider2.java: adds a private helper, maybeStripAndThrowSQLiteExceptionWithJson(Exception e, String permission, LogFields.Builder logBuilder). It acts when three things are true: e is a SQLiteException, getContext().checkCallingPermission(permission) is not PERMISSION_GRANTED, and e.getMessage() lower-cased with Locale.ROOT contains \"json\". In that case it records a new SQLiteException(\"Stripped exception message\") in the log builder and throws that instead of the original. The code comment says checkCallingPermission (not checkCallingOrSelfPermission) is used on purpose, so non-binder or self calls after clearing identity do not get the raw message.",
      "ContactsProvider2.insert, ContactsProvider2.update, ContactsProvider2.delete: each existing catch (Exception e) block now calls maybeStripAndThrowSQLiteExceptionWithJson(e, WRITE_PERMISSION, logBuilder) before the existing logBuilder.setException(e); throw e;.",
      "ContactsProvider2.query: after queryInternal(...) returns, if cursor != null, logBuilder.setResultCount(cursor.getCount()) now runs inside the try block, so an exception raised while the cursor is consumed is caught by the catch block. The catch block now calls maybeStripAndThrowSQLiteExceptionWithJson(e, READ_PERMISSION, logBuilder). The finally block is reduced to LogUtils.log(logBuilder.build()), and the old getCount() call there is removed.",
      "Adds an import for android.database.sqlite.SQLiteException."
     ],
     "completeness": [
      "Covers the four ContentProvider overrides in ContactsProvider2 (query, insert, update, delete), and only exceptions that are instances of SQLiteException.",
      "Filtering is keyword based. Only messages containing \"json\" (case-insensitive) are stripped. SQLite error messages without that substring still reach callers unchanged.",
      "Write paths check WRITE_PERMISSION, not READ_PERMISSION. A caller that holds the write permission but not the read permission still gets raw insert/update/delete error messages, even though the commit message frames the protection in terms of READ_CONTACTS.",
      "The diff does not modify other provider entry points (e.g. bulkInsert, applyBatch, call, openAssetFile). The material does not show whether they can surface raw SQLiteException text."
     ],
     "behavior_changes": [
      "Callers that fail the permission check (READ_PERMISSION for query, WRITE_PERMISSION for insert/update/delete) and trigger a SQLiteException whose message contains \"json\" now get a new SQLiteException with the fixed message \"Stripped exception message\" and no cause attached.",
      "Self-calls on non-binder threads (identity cleared) are also stripped, because checkCallingPermission is used.",
      "query() now consumes the cursor (getCount()) inside the try block before returning. The logged result count is set only when a non-null cursor is returned.",
      "In the stripped case, LogUtils log entries record the stripped exception instead of the original."
     ],
     "preconditions": [
      "The caller can invoke ContactsProvider2.query, insert, update or delete through the Contacts content provider without holding the permission checked on that path. The material does not show which URIs are reachable without those permissions.",
      "The caller controls SQL fragments (selection, sort order, projection or values) that make SQLite raise an error whose message contains \"json\"."
     ],
     "per_major": {
      "14": "same fix: commit 7d344b678bde in android-security-14.0.0_r28, identical hunks at different line offsets; 8/8 patched lines present at android-security-14.0.0_r29",
      "15": "same fix: commit 930817423647 in android-security-15.0.0_r16, identical hunks; 8/8 patched lines present at android-security-15.0.0_r17",
      "16": "same fix: commit bb9f36b3ff6d in android-security-16.0.0_r7 (cherry-pick of 6044ca5db93f); 8/8 patched lines present at android-security-16.0.0_r8",
      "17": "same fix: commit 819ebe4a7c68 already in base android-17.0.0_r1; 8/8 patched lines present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Single self-contained commit. It relies on code already in ContactsProvider2: the LogFields.Builder/LogUtils logging in the catch/finally blocks of query/insert/update/delete, the READ_PERMISSION/WRITE_PERMISSION constants, PERMISSION_GRANTED and Locale. All four Google branches carry identical hunks. Trees without this logging code need the helper call placed in whichever catch block rethrows to the caller.",
      "The material includes no fork comparison, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact SQLite error messages and which \"json\" functions or tokens were used for the side channel (referenced only as b/465133716).",
      "Which CP2 URIs can be reached without READ_CONTACTS/WRITE_CONTACTS and therefore form the actual entry surface.",
      "The values of the READ_PERMISSION/WRITE_PERMISSION constants (not shown in the material).",
      "Whether other provider entry points (bulkInsert, applyBatch, call) go through the patched overrides or expose raw SQLiteException text.",
      "Whether SQLite errors raised after getCount(), for example during later cursor window fills on the client side, can still carry unstripped messages.",
      "The test changes behind 'atest ContactsProviderTests' are not in the diff."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0076": {
     "cve": "CVE-2026-0076",
     "summary": "Adds a check in ResXMLTree::validateNode (libs/androidfw/ResourceTypes.cpp) that rejects a START_ELEMENT node whose attrExt->attributeSize is smaller than sizeof(ResXMLTree_attribute), returning BAD_TYPE instead of accepting the node.",
     "component": "platform/frameworks/base, libs/androidfw (libandroidfw binary XML / resource parser, ResXMLTree)",
     "weakness": "Missing minimum-size check on an attacker-controlled size field, leading to an out-of-bounds read",
     "root_cause": "ResXMLTree::validateNode checked a start element's attribute block only by testing that attributeStart + attributeSize * attributeCount fits within the node (size - headerSize). It never required attributeSize to be at least sizeof(ResXMLTree_attribute). According to the commit message, the code that later reads attributes uses the minimum (structure) size rather than the declared attributeSize. A node that declares an attributeSize smaller than the minimum could therefore pass the product check, and the later attribute accesses would read beyond the end of memory.",
     "fix_analysis": [
      "libs/androidfw/ResourceTypes.cpp:ResXMLTree::validateNode: the new check sits inside the existing branch that already requires size >= headerSize + sizeof(ResXMLTree_attrExt) and attrExt > node. It compares dtohs(attrExt->attributeSize) against sizeof(ResXMLTree_attribute). If the value is smaller, it logs 'Bad XML block: attribute size %d is smaller than min expected %d' via ALOGW and returns BAD_TYPE before the attributeSize * attributeCount product check runs.",
      "libs/androidfw/tests/ResourceTypes_test.cpp: the new test ResXMLTree_ValidateNode_SmallAttributeSize builds a mock RES_XML_TYPE blob with an empty string pool and one RES_XML_START_ELEMENT_TYPE node. That node has attributeSize sizeof(ResXMLTree_attribute) - 1, attributeCount 1 and a node size large enough to pass the old product check. The test asserts that ResXMLTree::setTo returns BAD_TYPE."
     ],
     "completeness": [
      "Covers the case the commit describes: attributeSize below the structure size on START_ELEMENT nodes. Because the check lives in validateNode, it applies wherever validateNode runs on start nodes. The test exercises it through ResXMLTree::setTo.",
      "The existing product check (attributeStart + attributeSize*attributeCount <= size - headerSize) is unchanged and still catches attribute blocks that are too large.",
      "The code that reads attributes (which the commit message says uses minimum sizes) is not modified. The fix only rejects bad input at validation time, so an attribute access path that is not preceded by validateNode would not be protected. The material shows no such path.",
      "attributeStart has no minimum (for example >= sizeof(ResXMLTree_attrExt)), so attributes could still be declared to overlap the attrExt header. Such attributes stay inside the node bounds, so this is not the out-of-bounds condition addressed here."
     ],
     "behavior_changes": [
      "ResXMLTree::validateNode returns BAD_TYPE, and ResXMLTree::setTo therefore fails, for binary XML whose start element declares attributeSize < sizeof(ResXMLTree_attribute). Such documents were previously accepted if the product check passed.",
      "New ALOGW log line: 'Bad XML block: attribute size %d is smaller than min expected %d'."
     ],
     "preconditions": [
      "ResXMLTree parses a crafted compiled binary XML chunk (RES_XML_TYPE), for example via ResXMLTree::setTo as in the new test. The chunk contains a RES_XML_START_ELEMENT_TYPE node whose ResXMLTree_attrExt declares attributeSize below sizeof(ResXMLTree_attribute), with a node size large enough to pass the attributeStart + attributeSize*attributeCount check.",
      "The material does not show which processes parse untrusted binary XML with libandroidfw, and so it does not show the EoP context."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 40ed3bb3016e in android-security-14.0.0_r28. Identical hunk in ResXMLTree::validateNode (at line ~1915) plus the same test. 8/8 patched lines present at android-security-14.0.0_r29.",
      "15": "same fix: platform/frameworks/base b4f448b356b1 in android-security-15.0.0_r16. Identical hunk (at line ~1986) plus the same test. 8/8 patched lines present at android-security-15.0.0_r17.",
      "16": "same fix: platform/frameworks/base a91d16a02b51 in android-security-16.0.0_r7, a cherry-pick of 4d869cd4de74 (Change-Id Iaaaca9bb4c196e7acde0895d64da919c52448a44). Hunk at line ~1989 plus the test. 8/8 patched lines present at android-security-16.0.0_r8.",
      "17": "same fix: platform/frameworks/base 881e565347a5 in android-17.0.0_r1 (base release). Identical hunk and test. 8/8 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained 5-line check inside ResXMLTree::validateNode with no new code dependencies. Google applied the same hunk on 14 through 17, differing only in line offsets. The test file already includes androidfw/Util.h, and its existing tests use util::HostToDevice32. The new test also uses util::HostToDevice16."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not include the attribute accessor code that reads using the fixed structure size (described in the commit message), so the exact out-of-bounds read site and read length are not shown.",
      "The material does not show which privileged process parses attacker-controlled binary XML, which is what would make this an EoP.",
      "The material does not show whether other consumers of ResXMLTree_attrExt outside validateNode (for example aapt2 or host tools) validate independently.",
      "The material does not show whether the patch applies cleanly to forked trees with customized ResourceTypes.cpp."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0078": {
     "cve": "CVE-2026-0078",
     "summary": "DevicePolicyManagerService now rejects a global proxy spec or exclusion list string longer than 65535 modified UTF-8 bytes via PolicySizeVerifier.enforceMaxStringLength before storing it in the ActiveAdmin.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (device policy, global proxy)",
     "weakness": "Missing input length validation on persisted policy strings (improper input validation)",
     "root_cause": "In the global proxy setter of DevicePolicyManagerService (the handler behind DevicePolicyManager.setGlobalProxy, enforcing DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY), the caller-supplied proxySpec and exclusionList strings were assigned directly to admin.globalProxySpec and admin.globalProxyExclusionList with no size check. PolicySizeVerifier's own doc describes the limit as \"too long to be serialized\" and the test comment states the maximum is 65535 UTF bytes, so strings exceeding what the policy serialization format can represent were accepted into persistent admin state. The exact downstream failure when such a string is written or read back is not shown in the material.",
     "fix_analysis": [
      "services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java, global proxy setter (setGlobalProxy handler): in the proxySpec != null branch, before setting admin.specifiesGlobalProxy/globalProxySpec/globalProxyExclusionList, call PolicySizeVerifier.enforceMaxStringLength(proxySpec, \"proxySpec\") and, if exclusionList != null, PolicySizeVerifier.enforceMaxStringLength(exclusionList, \"exclusionList\"). In the class shipped with the 14/17 commits, enforceMaxStringLength counts modified UTF-8 bytes (1 for 0x01..0x7F, 2 up to 0x7FF including U+0000, 3 otherwise) and throws IllegalArgumentException(argName + \" too long\") once the count exceeds 65535.",
      "The check runs after getActiveAdminForCallerLocked, after the other-admin-already-set scan, and after the USER_SYSTEM check, so it only affects callers that would otherwise have been allowed to store the proxy.",
      "On Android 14 and 17 the same commit also adds core/java/android/app/admin/PolicySizeVerifier.java (new self-contained class with enforceMaxStringLength and a private countBytes) and the import android.app.admin.PolicySizeVerifier in DevicePolicyManagerService.java. On 15 and 16 the diff adds only the call sites, which implies the class is already present.",
      "Tests: services/tests/servicestests/res/xml/device_admin_sample.xml gains <set-global-proxy /> so the test admin holds the policy; DevicePolicyManagerTest.testSetGlobalProxy_tooLongStrings asserts IllegalArgumentException for a 65536-char host and for a 65536-char exclusion list entry."
     ],
     "completeness": [
      "Covers both strings stored by this path: proxySpec unconditionally in the non-null branch, exclusionList only when non-null (a null exclusionList is stored as-is, which is fine for the length concern).",
      "The proxySpec == null branch clears state and needs no check.",
      "Only the setGlobalProxy handler is changed; no other DevicePolicyManagerService string-accepting entry point is touched in this diff, and the material does not show whether other persisted policy strings already go through PolicySizeVerifier.",
      "The limit is applied to the whole exclusionList string as received by the service, not per entry; how the framework side joins the List<String> into that string is not shown."
     ],
     "behavior_changes": [
      "DevicePolicyManager.setGlobalProxy now throws IllegalArgumentException (\"proxySpec too long\" or \"exclusionList too long\") when either string exceeds 65535 modified UTF-8 bytes; previously the values were accepted and persisted.",
      "No change for strings within the limit or for clearing the proxy (null proxySpec)."
     ],
     "preconditions": [
      "Caller is an active admin for the given ComponentName that declares the set-global-proxy policy (DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); the test uses a device owner.",
      "Call originates from the system user (UserHandle.USER_SYSTEM); for other users the method returns null silently.",
      "No other admin already specifies a global proxy (otherwise that component is returned before the store)."
     ],
     "per_major": {
      "14": "adapted fix: fa2c8531ae6f in android-security-14.0.0_r28 adds the same two PolicySizeVerifier calls plus the new core/java/android/app/admin/PolicySizeVerifier.java class and its import (class absent on 14); 8/8 lines present at android-security-14.0.0_r29.",
      "15": "same fix: 71bc705a2542 in android-security-15.0.0_r16, service call sites and tests only (PolicySizeVerifier already present); 8/8 lines present at android-security-15.0.0_r17.",
      "16": "same fix: 50cbf4422ac8 in android-security-16.0.0_r7, service call sites and tests only; 8/8 lines present at android-security-16.0.0_r8.",
      "17": "adapted fix: cf8446b51ae1 in android-17.0.0_r1 (base) adds the call sites plus the new PolicySizeVerifier.java class and import, same shape as the 14 commit; 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The only dependency is android.app.admin.PolicySizeVerifier.enforceMaxStringLength(String, String). If the target branch lacks that class, take the 14/17 variant of the commit, which ships a self-contained PolicySizeVerifier.java. Its com.android.modules.utils.ModifiedUtf8, Bundle, Parcelable, PersistableBundle, ComponentName, ArrayDeque and Queue imports are unused by the shipped code and can be dropped if they do not resolve.",
      "The test change requires adding <set-global-proxy /> to device_admin_sample.xml so that the test admin holds the policy checked by getActiveAdminForCallerLocked before the new check is reached."
     ],
     "confidence": "high",
     "unknowns": [
      "The concrete consequence of persisting an oversized proxy string (what fails when the policy file is written or parsed, and how that yields EoP) is not shown in the material.",
      "The exact signature and name of the enclosing service method are not in the provided context; it is identified via the test's dpm.setGlobalProxy call and the USES_POLICY_SETS_GLOBAL_PROXY check.",
      "The condition guarding the early 'return null' at the start of the setter (for example a device admin feature check) is not shown.",
      "Whether the PolicySizeVerifier already present on 15/16 has the same limit and message as the class shipped on 14/17 is not shown, though the test comment states 65535 UTF bytes.",
      "Whether other string-valued device policies in DevicePolicyManagerService are already size-checked is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0079": {
     "cve": "CVE-2026-0079",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow checks call a library-local runtime that throws std::runtime_error, instead of the ubsan minimal runtime that hard-aborts the hosting process.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG/raw image decoding library): Android.bp build configuration and new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG processing arithmetic surfaced as a process abort by the UBSan minimal runtime (DoS); the specific overflow sites are not in the diff",
     "root_cause": "In Android.bp, libdng_sdk-defaults enables sanitize.misc_undefined [\"unsigned-integer-overflow\", \"signed-integer-overflow\"]. Per the commit message, this used the ubsan-minimal runtime, which does a hard abort when it detects an error. So any detected signed or unsigned integer overflow in libdng_sdk ended the process hosting the decoder, even though C++ exceptions are enabled in the library (cppflags -fexceptions, rtti: true). The commit references seven bug IDs. None of the overflowing arithmetic is changed by this commit.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize { never: true }. Per the inline comment, this disables the ubsan_minimal runtime for this module so a custom runtime can be injected.",
      "Android.bp, cc_library libdng_sdk: re-enables the overflow checks through cflags -fsanitize=signed-integer-overflow,unsigned-integer-overflow and adds -fno-sanitize-link-runtime so the sanitizer runtime is not linked. The existing -DqDNGValidate=0 is kept.",
      "Android.bp, cc_library libdng_sdk: adds ubsan-replacement/ubsan_throwing_runtime.cpp to srcs.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow with hidden visibility. Each one throws std::runtime_error with a descriptive message, so a detected overflow becomes a C++ exception instead of an abort.",
      "OWNERS (14/15/16 cherry-picks only): adds a bug component and includes the frameworks/base graphics OWNERS; no functional effect."
     ],
     "completeness": [
      "The fix is a generic mitigation. It changes how every detected signed or unsigned integer overflow in libdng_sdk is reported, but it does not correct any of the underlying arithmetic.",
      "Only the libdng_sdk cc_library module is changed. libdng_sdk-defaults still carries misc_undefined, so any other module built from those defaults (none shown in the material) is not covered by this change.",
      "Only the five overflow handlers are provided, matching the two enabled check groups (signed-integer-overflow, unsigned-integer-overflow).",
      "The material does not show whether the thrown std::runtime_error is caught by dng_sdk itself or by the Android caller. If it reaches a frame with no handler, or is thrown from a noexcept context or a destructor, std::terminate still ends the process (general platform knowledge)."
     ],
     "behavior_changes": [
      "Integer overflow detected by UBSan inside libdng_sdk now raises a C++ std::runtime_error (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\", \"ubsan: divrem-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the sanitizer runtime (-fno-sanitize-link-runtime). The overflow handler symbols are defined inside the library with hidden visibility.",
      "Soong-level sanitizer configuration is disabled for the module (sanitize.never: true). The overflow checks now come only from the explicit -fsanitize cflags."
     ],
     "preconditions": [
      "A process decodes a crafted DNG/raw file through libdng_sdk. The commit's test (ImageDecoderTest#testInterestingDngs) indicates that the ImageDecoder DNG path is the exercised consumer.",
      "The file must drive dng_sdk arithmetic into a signed or unsigned integer overflow that the enabled checks detect. The concrete sites are not in the material."
     ],
     "per_major": {
      "14": "same fix: identical Android.bp hunk (at line 168, the cflags line is replaced with sanitize/cflags/srcs blocks) and identical ubsan_throwing_runtime.cpp; the OWNERS edit differs trivially. Resolved by change_id in android-security-14.0.0_r28 (commit 42e65547eb33).",
      "15": "same fix: identical Android.bp change and runtime file; trivial OWNERS difference. Resolved by change_id in android-security-15.0.0_r16 (commit 1ec41401b3d4).",
      "16": "same fix: commit 74a5a91d169a in android-security-16.0.0_r7, with Cherrypick-From references to 81ded662eee0 (the Android 17 commit) and two other commits. The Android.bp change and runtime file are identical to 14/15.",
      "17": "adapted fix: commit 81ded662eee0 in android-17.0.0_r1, resolved by related_bug. The Android.bp layout differs: the sanitize/cflags hunk is at line 105, and the new source is appended to the module's existing srcs list at line 220. No OWNERS change; the runtime file is identical."
     },
     "backport_notes": [
      "Apply the Android.bp pieces together: sanitize.never: true, the explicit -fsanitize cflags, -fno-sanitize-link-runtime and the new source file. The inline comment says 'never' is meant to be combined with the -fsanitize= cflags. The module relies on cppflags -fexceptions and rtti: true, which are present in libdng_sdk-defaults in the shown context.",
      "Branch layout differs: on 14/15/16 the module only has a cflags line to replace, and srcs is added as a new block. On the 17 Android.bp the file is appended to the module's existing source list."
     ],
     "confidence": "high",
     "unknowns": [
      "The specific overflow sites in the dng_sdk source behind the seven referenced bugs are not in the material.",
      "Whether dng_sdk's internal exception handling or the Android caller catches std::runtime_error is not shown, so where the exception is finally handled is unknown.",
      "Whether other modules in dng_sdk's Android.bp use libdng_sdk-defaults is not shown.",
      "The two other Cherrypick-From commits (475c8cce973f, 1337d2e14a41) are not provided, so their content is unknown.",
      "How the build behaves if only part of the Android.bp change is applied is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0085": {
     "cve": "CVE-2026-0085",
     "summary": "Makes DataRowHandler.applySimpleFieldMaxSize in ContactsProvider look up the ContentValues key case-insensitively, using a new findKey helper. A simple field supplied under a case-variant column name such as \"DaTa1\" is now truncated to the configured maximum size instead of slipping past the limit.",
     "component": "platform/packages/providers/ContactsProvider, DataRowHandler (size enforcement on data row insert/update)",
     "weakness": "Improper input validation: the size limit could be bypassed because the key lookup was case-sensitive while the column namespace is not (CWE-20 / CWE-178 style)",
     "root_cause": "applySimpleFieldMaxSize(ContentValues cv, String column) read the value with cv.getAsString(column), which needs an exact, case-sensitive key match. The added tests show that a case-variant key such as \"daTa1\" still updates data1 when the update goes through. So a caller who supplied the field under a differently-cased key got null from getAsString, the method returned early, and the oversized string was stored without truncation. The commit message calls this a size check bypass; the bulletin classifies the impact as DoS.",
     "fix_analysis": [
      "src/com/android/providers/contacts/DataRowHandler.java:applySimpleFieldMaxSize: instead of reading cv.getAsString(column) directly, it now calls findKey(cv, column) to get the key actually present in cv, and reads the value through that key. The length check against ContactsDatabaseHelper.getSimpleFieldMaxSize() and the truncation cv.put(column, v.substring(0, maxSize)) are unchanged, so the truncated value is written under the canonical column name.",
      "src/com/android/providers/contacts/DataRowHandler.java:findKey (new, private static): returns column if cv.containsKey(column). Otherwise it walks cv.keySet() and returns the first key for which key.equalsIgnoreCase(column). If nothing matches it returns column, so getAsString yields null and the method returns early as before.",
      "tests/src/com/android/providers/contacts/ContactsProvider2Test.java: adds three tests. testUpdateFailsWithOnlyCaseMismatchedColumn: an update with only a case-mismatched key is rejected (0 rows). testUpdateSucceedsWithAtLeastOneCaseSensitiveColumn: the mismatched key is applied when an exact-case key is also present. testCaseMismatchingFieldUpdatesAreTruncatedToMaxSize: the variants data1, daTa1, DATA1 and Data1 all end up truncated. The commit also removes an unused mMockContactsDatabaseHelper field."
     ],
     "completeness": [
      "Covers every caller of applySimpleFieldMaxSize, because the case-insensitive lookup sits inside the shared helper. No per-handler changes were needed.",
      "The truncated value is stored under the canonical name (cv.put(column, ...)), but the original case-variant key is neither removed nor overwritten. After the call, cv can hold both the mismatched key with the full-length value and the canonical key with the truncated value. The added test asserts that the stored data1 is the truncated value for every tested variant, so the canonical value wins in the tested path. The code itself still does not clear the oversized entry.",
      "findKey returns only the first case-insensitive match. If one ContentValues holds more than one case variant of the same column, only the first one found is examined and truncated.",
      "The diff does not change how the provider decides whether an update goes ahead. The tests record that an update whose keys are all case-mismatched is rejected (0 rows), and that a mix with at least one exact-case key is accepted."
     ],
     "behavior_changes": [
      "Oversized values supplied under a case-variant key of a simple field are now truncated to getSimpleFieldMaxSize() and stored under the canonical column name. Before the fix they were stored at full length.",
      "The existing Log.w \"Truncating field ...\" warning is now also logged for case-mismatched keys.",
      "After truncation, the ContentValues also contains the canonical column key, which was not there before when only a case-variant key was supplied.",
      "Small extra cost: a linear scan over cv.keySet() when the exact key is absent."
     ],
     "preconditions": [
      "The caller can insert or update Data rows through ContactsProvider, so the ContentValues reaches a DataRowHandler that calls applySimpleFieldMaxSize. The tests exercise mResolver.update on a StructuredName data URI. Writing contacts normally requires the contacts write permission (general platform knowledge).",
      "The ContentValues uses a column key whose case differs from the canonical name (e.g. \"DaTa1\" instead of \"data1\"), with a value longer than ContactsDatabaseHelper.getSimpleFieldMaxSize().",
      "Per the added tests, on the update path at least one key with exact case must also be present for the update to be applied at all."
     ],
     "per_major": {
      "14": "Same fix: commit adef234467ab in android-security-14.0.0_r28, with an identical DataRowHandler.java change (hunk at line 446). Tests added without @Test annotations.",
      "15": "Same fix: commit 7d266fdeb2ec in android-security-15.0.0_r16, with an identical DataRowHandler.java change. Tests added without @Test annotations.",
      "16": "Same fix: commit 0d925d762547 in android-security-16.0.0_r7 (hunk at line 445). Tests added with @Test annotations. The code context at android-security-16.0.0_r8 shows findKey and the new lookup in place.",
      "17": "Same fix: commit d857ac867433, already in the base android-17.0.0_r1, with an identical DataRowHandler.java change. Tests added without @Test annotations."
     },
     "backport_notes": [
      "Single self-contained commit. The source change is identical on all four branches and touches only applySimpleFieldMaxSize plus the new private findKey. The only difference between branches is in the tests: @Test annotations on 16, none on 14, 15 or 17."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show which DataRowHandler subclasses call applySimpleFieldMaxSize, or for which columns. It also does not show whether the insert path accepts case-variant keys the same way the update path does.",
      "The provided material does not describe the exact DoS mechanism of an oversized stored field, i.e. what breaks downstream.",
      "The per-major figure '7/8 lines of the patched region present' is not explained. The android-security-16.0.0_r8 context excerpt shows the fix in place but is cut off before the end of findKey."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0087": {
     "cve": "CVE-2026-0087",
     "summary": "Tightens wildcard domain matching in DomainVerificationService so a verified \"*.xyz.com\" entry only approves the host \"xyz.com\" or hosts ending in \".xyz.com\", instead of any host whose string merely ends in \"xyz.com\" (e.g. \"abcxyz.com\").",
     "component": "platform/frameworks/base, PackageManager domain verification (services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java)",
     "weakness": "Incorrect string comparison: suffix match without the label delimiter, letting a wildcard domain match unrelated domains (improper input validation / incorrect comparison)",
     "root_cause": "In DomainVerificationService.java, the approval-level computation (the loop over pkgState.getStateMap() that returns APPROVAL_LEVEL_VERIFIED with the debug message \"host verified by wildcard\") treated a verified wildcard entry as matching when `domain.startsWith(\"*.\") && host.endsWith(domain.substring(2))`. substring(2) strips the \"*.\" entirely, so for \"*.xyz.com\" the check became host.endsWith(\"xyz.com\"). This matches \"abc.xyz.com\" and \"xyz.com\" as intended, but also any unrelated host that happens to end in the same characters, such as \"abcxyz.com\". A package with a verified wildcard domain was therefore treated as a verified owner (APPROVAL_LEVEL_VERIFIED) of domains it does not own.",
     "fix_analysis": [
      "services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java, wildcard loop in the approval-level method (around line 2030 on 15/16, 1887 on 14/17): the condition is changed to `domain.startsWith(\"*.\") && (host.endsWith(domain.substring(1)) || host.equals(domain.substring(2)))`. substring(1) keeps the leading period, so the suffix test is now host.endsWith(\".xyz.com\"), which requires a label boundary before the wildcard base. The explicit host.equals(domain.substring(2)) clause preserves the prior behavior that the bare base domain \"xyz.com\" is also covered by \"*.xyz.com\". A comment documenting the intended semantics is added.",
      "services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt: new testWildcardDomain registers a package with autoVerify domain \"*.xyz.com\", sets it STATE_SUCCESS via setStatus, and asserts getOwnersForDomain returns empty for \"abcxyz.com\" and DomainOwner(PKG_ONE, false) for \"abc.xyz.com\" and \"xyz.com\"."
     ],
     "completeness": [
      "Covers the single wildcard comparison in the verified-state loop of the approval-level method; this is the only place in the shown context where wildcard matching against the host is performed.",
      "Other checks in the same method shown in context (exact stateMap.get(host) at line 2016, mCollector.collectValidAutoVerifyDomains(pkg).contains(host) for instant apps at line 2009, userState.getEnabledHosts().contains(host) at line 2059) are exact matches and did not have the flaw.",
      "The comparison remains a plain case-sensitive String.endsWith/equals; whether host and the stored domain are normalized (e.g. lowercased) before reaching this code is not shown in the material.",
      "Whether any other code path (e.g. the domain collector or other services) performs its own wildcard-to-host matching with the same substring(2) pattern is not shown in the material."
     ],
     "behavior_changes": [
      "Stricter matching: a verified \"*.xyz.com\" entry no longer yields APPROVAL_LEVEL_VERIFIED for hosts like \"abcxyz.com\"; such hosts fall through to the remaining user-state checks and may resolve to APPROVAL_LEVEL_NONE.",
      "No change for the intended cases: \"xyz.com\" and \"abc.xyz.com\" still match \"*.xyz.com\".",
      "getOwnersForDomain results change accordingly (as exercised by the new test)."
     ],
     "preconditions": [
      "A package declares a wildcard autoVerify domain (\"*.<base>\") and that entry is in a verified state in the package's domain verification state map.",
      "A host is being resolved that ends in the wildcard base string without a preceding period (e.g. \"abcxyz.com\" against \"*.xyz.com\"), and no exact verified entry for the host exists in the package's state map.",
      "How an attacker-controlled package gets a wildcard domain into a verified state is not shown in the material (unknown)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base b035d109935c in android-security-14.0.0_r28; identical production change at line ~1887, test uses the older one-argument service.addPackage(pkg). At android-security-14.0.0_r29 all 6 patched lines present.",
      "15": "same fix: platform/frameworks/base b81e2355caf1 in android-security-15.0.0_r16; identical to the Android 16 diff (line ~2030, test uses service.addPackage(pkg, null)). At android-security-15.0.0_r17 all 6 patched lines present.",
      "16": "same fix: platform/frameworks/base 2e3d71d027b0 in android-security-16.0.0_r7 (cherry-pick of 919d0cb125b3). At android-security-16.0.0_r8 all 6 patched lines present.",
      "17": "same fix: platform/frameworks/base c225d09f44e6 in android-17.0.0_r1 (base release). Diff as provided matches the Android 14 variant (line ~1887, one-argument addPackage). At android-security-17.0.0_r1 all 6 patched lines present."
     },
     "backport_notes": [
      "Self-contained single-condition change in DomainVerificationService.java with no dependency shown; the only cross-version difference is in the test (service.addPackage(pkg) on 14/17 diffs vs service.addPackage(pkg, null) on 15/16), so adapt the test to the local addPackage signature."
     ],
     "confidence": "high",
     "unknowns": [
      "The enclosing method name is not shown in the provided context (only that it returns APPROVAL_LEVEL_* values and calls debugApproval).",
      "Whether host/domain strings are case-normalized before this comparison.",
      "How the verified wildcard state is obtained in practice and the concrete EoP impact beyond the bulletin's High/EoP rating."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 1,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0088": {
     "cve": "CVE-2026-0088",
     "summary": "CertInstaller.getCallingAppLabel() now returns appInfo.loadSafeLabel(pm) instead of appInfo.loadLabel(pm), so the calling app's label shown in the CA-certificate redirect dialog is sanitized before display.",
     "component": "platform/packages/apps/CertInstaller, CertInstaller activity (CA certificate install redirect dialog)",
     "weakness": "Improper neutralization of attacker-controlled text in a security UI (UI spoofing via unsanitized application label)",
     "root_cause": "In src/com/android/certinstaller/CertInstaller.java, getCallingAppLabel() resolved the referrer package (mCredentials.getReferrer()) via PackageManager.getApplicationInfo() and returned appInfo.loadLabel(pm), the raw application label, which the calling app controls. createRedirectCaCertificateDialog() formats that label into R.string.redirect_ca_certificate_with_app_info_message and shows it as the dialog message. According to the commit message, a label containing characters such as newlines could push the CA certificate installation warning off-screen, so the user might not see the warning.",
     "fix_analysis": [
      "src/com/android/certinstaller/CertInstaller.java:getCallingAppLabel(): the single return statement changes from appInfo.loadLabel(pm) to appInfo.loadSafeLabel(pm). The rest of the method is unchanged: the null referrer check, getApplicationInfo with MATCH_DISABLED_COMPONENTS, and the NameNotFoundException handling.",
      "The sanitized label flows unchanged into createRedirectCaCertificateDialog(), which builds the message with getString(R.string.redirect_ca_certificate_with_app_info_message, getCallingAppLabel()). Nothing there needed to change.",
      "loadSafeLabel on PackageItemInfo is a hidden framework API that sanitizes the label for display, for example by removing line breaks and limiting length. CertInstaller is a platform app built against the framework, so it can call it (general platform knowledge)."
     ],
     "completeness": [
      "Covers the only label source in the shown code: getCallingAppLabel() is the one place in the excerpt where the calling app's label is loaded, and its only shown consumer is createRedirectCaCertificateDialog().",
      "The fix sanitizes the label text only. It does not change how the referrer package is determined (mCredentials.getReferrer()) or add any check that the referrer is the actual caller. The material does not show whether that matters.",
      "createSelectCertificateUsageDialog(), the other branch of createRedirectOrSelectUsageDialog(), does not display the app label in the shown code, so this change does not affect it.",
      "No test was added (the diff touches one file). The commit message cites manual testing with the PoC app."
     ],
     "behavior_changes": [
      "The app name in the CA certificate redirect dialog is now the sanitized label. Per the commit message, newlines and similar characters are stripped. Very long labels may also be shortened (general platform knowledge about loadSafeLabel). As a result, the displayed name can differ from the raw label for apps with unusual labels.",
      "No change in control flow, in return values on error paths, or in the dialog buttons."
     ],
     "preconditions": [
      "The calling package is the one returned by mCredentials.getReferrer(), and mCredentials.hasOnlyVpnAndAppsTrustAnchors() is true, so createRedirectOrSelectUsageDialog() shows REDIRECT_CA_CERTIFICATE_DIALOG.",
      "The referrer package resolves via PackageManager.getApplicationInfo(). Otherwise getCallingAppLabel() returns null.",
      "The calling app declares a crafted application label (app-controlled manifest data). The material shows no special permission as required."
     ],
     "per_major": {
      "14": "same fix: commit d90ffd033f10 in android-security-14.0.0_r28, identical one-line diff (same file index 883c897..1eff53f). At android-security-14.0.0_r29, 1/2 lines of the patched region are present (which line is not shown).",
      "15": "same fix: commit d29fcd1eff25 in android-security-15.0.0_r16, identical one-line diff. At android-security-15.0.0_r17, 1/2 lines of the patched region are present (which line is not shown).",
      "16": "same fix: commit c935d8d07913 in android-security-16.0.0_r7, cherry-picked from the internal change (Change-Id I4b4613304fde8e3db5ba2d34422ce2cf3ec9a348). The shown code at android-security-16.0.0_r8 contains the loadSafeLabel line.",
      "17": "same fix: commit 3dced430d0a9 in android-17.0.0_r1 (resolved in the base release), identical one-line diff. At android-security-17.0.0_r1, 1/2 lines of the patched region are present (which line is not shown)."
     },
     "backport_notes": [
      "Single-commit, single-line change with identical blob hashes (883c897..1eff53f) on all four branches, so it applies cleanly wherever getCallingAppLabel() has the same shape. Forks that moved the label lookup elsewhere should apply loadSafeLabel at every point where the calling app's label is loaded for display.",
      "loadSafeLabel(PackageManager) is a hidden API on PackageItemInfo. The build needs access to framework hidden APIs, which platform CertInstaller has (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "How mCredentials.getReferrer() derives the calling package, and whether the caller can influence it, is not shown.",
      "How CertInstaller is launched by the calling app is not shown.",
      "The exact text of R.string.redirect_ca_certificate_with_app_info_message and the dialog layout that allowed the warning to be pushed off-screen are not in the material.",
      "For Android 14, 15 and 17, the material does not show which of the 2 patched-region lines is present in the latest security tags. Only the Android 16 r8 code is shown, and it contains the fixed line."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0091": {
     "cve": "CVE-2026-0091",
     "summary": "Changes IActivityTaskManager.setRunningRemoteTransitionDelegate to take a transition token instead of a caller-supplied IApplicationThread. The animation delegate is now recorded server-side on the Transition, either from the RemoteTransition at request time or through a new WindowContainerTransaction SET_ANIMATION_DELEGATE hierarchy op. The delegate's IApplicationThread is no longer sent out of WM core in TransitionRequestInfo.",
     "component": "platform/frameworks/base, WindowManager transitions (services/core/java/com/android/server/wm) and WM Shell (libs/WindowManager/Shell)",
     "weakness": "Trust of a client-supplied process identity token (IApplicationThread) to choose which process is marked as a running remote-transition delegate, plus export of another process's IApplicationThread binder from system_server to the shell",
     "root_cause": "Before the fix, TransitionController.requestStartTransition put the full RemoteTransition, including its IApplicationThread (getAppThread()), into the TransitionRequestInfo sent to the shell transition player. Shell handlers (RemoteTransitionHandler, OneShotRemoteHandler, RecentsTransitionHandler) then passed an IApplicationThread back into ActivityTaskManagerService.setRunningRemoteTransitionDelegate(IApplicationThread), which boosted whichever process that token resolved to. The server did not tie the delegate to a specific transition. Any caller that passed the 'already running a remote transition' check, or whose token hit the mRemotePlayer.reportRunning fast path, could name an IApplicationThread of its choosing. The commit message describes the old flow as prone to mismatches and as requiring an IApplicationThread token to be sent out of WMCore.",
     "fix_analysis": [
      "core/java/android/app/IActivityTaskManager.aidl: setRunningRemoteTransitionDelegate(in IApplicationThread caller) becomes setRunningRemoteTransitionDelegate(in IBinder transition). The doc now says core must already know the delegate through RemoteTransition or WindowContainerTransaction.setAnimationDelegate.",
      "services/core/java/com/android/server/wm/Transition.java: adds the field IApplicationThread mRemoteDelegate, the server-side record of the process expected to animate this transition.",
      "services/core/java/com/android/server/wm/TransitionController.java (request path): when a RemoteTransition is present, stores remoteTransition.getAppThread() in transition.mRemoteDelegate. It then builds new RemoteTransition(remoteTransition.getRemoteTransition(), remoteTransition.getDebugName()), without the appThread, for the TransitionRequestInfo sent to the player.",
      "core/java/android/window/WindowContainerTransaction.java: adds a hidden setAnimationDelegate(IBinder) that creates a HierarchyOp of type HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE (value 27 on Android 16) and carries the binder in the mInsetsFrameOwner field. Also adds name and toString handling for the new type.",
      "services/core/java/com/android/server/wm/WindowOrganizerController.java (hierarchy-op switch): a new case HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE sets chain.mTransition.mRemoteDelegate = IApplicationThread.Stub.asInterface(hop.getInsetsFrameOwner()). It logs and skips if there is no transition or no binder.",
      "services/core/java/com/android/server/wm/ActivityTaskManagerService.java:setRunningRemoteTransitionDelegate: resolves Transition.fromBinder(transitionToken), returns if that is null, and uses transition.mRemoteDelegate as the delegate. The rest of the logic is unchanged (reportRunning fast path, CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS enforcement, caller must be isRunningRemoteTransition, mRemotePlayer.update), but it now acts only on the delegate recorded by the server.",
      "libs/WindowManager/Shell Transitions.java:setRunningRemoteTransitionDelegate now takes an IBinder transition token and forwards it. The RemoteTransitionHandler call site and both OneShotRemoteHandler call sites (one in startAnimation) now pass the transition token instead of remote.getAppThread() or mRemote.getAppThread().",
      "libs/WindowManager/Shell RecentsTransitionHandler.java: startRecentsTransition no longer caches mAnimApp. startRealRecentsTransition receives appThread and calls wct.setAnimationDelegate(appThread.asBinder()) so core records the recents animator. The boost call in startAnimation passes the transition token.",
      "libs/WindowManager/Shell SplitScreenTransitions.java:startEnterTransition: if remoteTransition has an appThread, adds wct.setAnimationDelegate(remoteTransition.getAppThread().asBinder()) before mTransitions.startTransition."
     ],
     "completeness": [
      "Covers every shell call site of Transitions.setRunningRemoteTransitionDelegate shown in the diff (RemoteTransitionHandler, OneShotRemoteHandler twice, RecentsTransitionHandler). Also covers the two shell paths that start transitions with a non-shell animator: recents launch and split-screen enter.",
      "The IApplicationThread is stripped only from the RemoteTransition in the TransitionRequestInfo built in TransitionController. The existing transition.setRemoteAnimationApp(remoteTransition.getAppThread()) call after requestStartTransition is unchanged and stays server-side.",
      "The new SET_ANIMATION_DELEGATE hierarchy op accepts any IBinder and stores it via IApplicationThread.Stub.asInterface without checking it against a known process at that point. The only later check shown is getProcessController(delegate) returning null in setRunningRemoteTransitionDelegate. The fix adds no authorization check of its own for this op.",
      "If the transition token is unknown, setRunningRemoteTransitionDelegate returns silently. If mRemoteDelegate is null, the call skips the fast path, goes through permission enforcement and the caller check, and then calls getProcessController(null). The material does not show what that call returns.",
      "On Android 16 the fix does not touch StageCoordinator. On 14 and 15 the legacy RemoteAnimationAdapter path in StageCoordinator had its setRunningRemoteTransitionDelegate(adapter.getCallingApplication()) calls removed outright rather than mapped."
     ],
     "behavior_changes": [
      "Hidden AIDL signature change: IActivityTaskManager.setRunningRemoteTransitionDelegate now takes an IBinder transition token, so the framework and WM Shell must be updated together.",
      "Shell handlers no longer receive the launching app's IApplicationThread. Core builds the RemoteTransition in TransitionRequestInfo with the (remoteTransition, debugName) constructor, which leaves out the appThread.",
      "New hidden WindowContainerTransaction.setAnimationDelegate and HierarchyOp type HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE (value 27 on Android 16). Applying it with no associated transition logs an error and is ignored.",
      "The priority boost goes only to the process recorded on the transition, so a caller can no longer pick the IApplicationThread to boost. The synthetic recents path does not set an animation delegate.",
      "On 14 and 15, split-screen legacy RemoteAnimationAdapter animators are no longer boosted through setRunningRemoteTransitionDelegate because those calls were removed."
     ],
     "preconditions": [
      "Pre-fix: the caller of ActivityTaskManagerService.setRunningRemoteTransitionDelegate had to be a process already running a remote transition, or had to supply an IApplicationThread for which controller.mRemotePlayer.reportRunning returned true. That fast path runs before the permission check.",
      "A remote transition or recents transition must be in flight. Per index search, Launcher3 starts recents transitions via SystemUiProxy.startRecentsTransition, which passes context.iApplicationThread to recentTasks.startRecentsTransition and so reaches RecentsTransitionHandler.startRecentsTransition.",
      "The exact trigger and the privilege gained are not shown in the material beyond the bulletin's EoP/High rating."
     ],
     "per_major": {
      "14": "adapted fix (caffcf6be82b, android-security-14.0.0_r28). Same design. The HierarchyOp carries the binder via setReparentContainer/getNewParent with op value 18, uses the older TransitionRequestInfo constructor and a single mTransitionPlayer. RecentsTransitionHandler keeps mAnimApp but boosts by token. Two legacy StageCoordinator setRunningRemoteTransitionDelegate(adapter.getCallingApplication()) calls are removed.",
      "15": "adapted fix (f7488ad342c2, android-security-15.0.0_r16). Same design. Uses setInsetsFrameOwner/getInsetsFrameOwner with op value 19 and the pipTaskInfo variant of TransitionRequestInfo. The legacy StageCoordinator calls are removed, and the second OneShotRemoteHandler call site is included.",
      "16": "this commit (40c30bb5613e, android-security-16.0.0_r7). Op value 27 with setInsetsFrameOwner; startRealRecentsTransition takes appThread; no StageCoordinator change.",
      "17": "Resolved per material (a29de493b054 in android-17.0.0_r1). The 17 diff in the material matches the 14 diff in shape (setReparentContainer/getNewParent, op value 18, mAnimApp, StageCoordinator removals), and several files share blob hashes with it. StageCoordinator, ActivityTaskManagerService, Transition and TransitionController differ in hashes and offsets. This shape does not match the 16 code, so treat the exact 17 content as unverified."
     },
     "backport_notes": [
      "The change spans the AIDL, the framework (services/core/wm) and WM Shell in one commit; WM Shell is built into SystemUI, so all parts must land together (general platform knowledge). Carry the delegate binder in a HierarchyOp field that exists on the target branch (mInsetsFrameOwner on 15/16, mReparent via setReparentContainer/getNewParent on 14), and pick an unused HIERARCHY_OP_TYPE value.",
      "Branches that still have the legacy RemoteAnimationAdapter path in StageCoordinator (14/15) had the adapter.getCallingApplication() boost calls removed in Google's fix."
     ],
     "confidence": "medium",
     "unknowns": [
      "The concrete abuse scenario (which process could be elevated, or what the leaked IApplicationThread enabled) is not shown. Only the bulletin's EoP/High rating and the commit message are available.",
      "The test testActivityManager_stripsAppThreadFromRemoteTransition is referenced, but its content is not in the material.",
      "Whether the Android 17 diff shown (14-shaped) is the actual commit applied in android-17.0.0_r1. The per-major facts report only 2/8 patched-region lines present at android-security-17.0.0_r1.",
      "The provided context does not show whether the WindowOrganizerController permission gating for hierarchy ops limits who can issue SET_ANIMATION_DELEGATE.",
      "How getProcessController(null) behaves when mRemoteDelegate is null.",
      "The body of the RemoteTransition(IRemoteTransition, String) constructor, which would confirm that the appThread is null in the request."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0093": {
     "cve": "CVE-2026-0093",
     "summary": "Replaces ApplicationInfo.loadLabel() with loadSafeLabel(PackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) at the three places where the Settings credential-management request UI shows the requesting app's label.",
     "component": "platform/packages/apps/Settings, security / credential management app request UI (RequestManageCredentials, CredentialManagementAppAdapter)",
     "weakness": "UI misrepresentation via an unsanitized, app-controlled display string (app label not trimmed or limited in length or lines)",
     "root_cause": "RequestManageCredentials and CredentialManagementAppAdapter showed the requesting app's name with applicationInfo.loadLabel(PackageManager), which returns the app-supplied label without trimming or size/line limits. Per the commit message, this let the app developer hide from the user what permissions the app requests. The fix trims the label using loadSafeLabel.",
     "fix_analysis": [
      "src/com/android/settings/security/CredentialManagementAppAdapter.java (first hunk, around line 93): the title built with TextUtils.expandTemplate(R.string.request_manage_credentials_title, ...) now uses applicationInfo.loadSafeLabel(mPackageManager, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) instead of loadLabel(mPackageManager).",
      "src/com/android/settings/security/CredentialManagementAppAdapter.java (second hunk, around line 156): mAppNameView.setText(String.valueOf(...)) now wraps loadSafeLabel with the same size limit and TRIM | FIRST_LINE flags instead of loadLabel.",
      "src/com/android/settings/security/RequestManageCredentials.java (around line 251): the mTitleView text built with TextUtils.expandTemplate now uses applicationInfo.loadSafeLabel(getPackageManager(), DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_FIRST_LINE) instead of loadLabel(getPackageManager()).",
      "Both files gain static imports for PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, TextUtils.SAFE_STRING_FLAG_FIRST_LINE and TextUtils.SAFE_STRING_FLAG_TRIM. Net effect: the label shown is cut to its first line, trimmed, and size-bounded."
     ],
     "completeness": [
      "All three loadLabel call sites in the diff for this UI (two in CredentialManagementAppAdapter, one in RequestManageCredentials) are converted. No loadLabel call remains in the shown hunks.",
      "The NameNotFoundException fallback paths are unchanged. In the adapter, the app-name row falls back to mAppNameView.setText(appName). The title fallbacks use another expandTemplate call whose arguments are not shown.",
      "The fix only covers the app label string. It does not change how the URI / AppUriAuthenticationPolicy entries are rendered (not shown in the diff).",
      "No automated test was added (Test: Manual, per the commit message)."
     ],
     "behavior_changes": [
      "The requesting app's name in the credential management request title and app-name row is now limited to its first line, trimmed, and capped by DEFAULT_MAX_LABEL_SIZE_PX. Multi-line or very long labels are cut.",
      "No new error paths or API changes. The change is marked Flag: EXEMPT BUGFIX (no feature flag)."
     ],
     "preconditions": [
      "A third-party app must trigger the credential management app request flow handled by the Settings RequestManageCredentials activity. Per the imports in the code context, the activity uses KeyChain, DevicePolicyManager and AppUriAuthenticationPolicy.",
      "The app controls its own application label. The diff implies no special permission.",
      "The user must be shown the consent screen."
     ],
     "per_major": {
      "14": "same fix: commit 47f5449218e4 in android-security-14.0.0_r28. Hunks are identical; the RequestManageCredentials.java context differs only by a neighboring import (android.annotation.Nullable). At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "same fix: commit 7f9aa5805598 in android-security-15.0.0_r16, identical to the Android 16 diff. At android-security-15.0.0_r17, 7/8 lines of the patched region are present.",
      "16": "same fix: commit 88ab2340c255 in android-security-16.0.0_r7, cherry-picked from two internal commits (Change-Id Iefd92a41d84f5cfc3244dbd4a8f7f8d2a9105ad5). At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "same fix: commit 9cc2911dc24d in android-17.0.0_r1 (base), same hunks as the Android 14 version. At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "Single self-contained commit. It needs only ApplicationInfo/PackageItemInfo.loadSafeLabel(PackageManager, float, int), PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX and the TextUtils.SAFE_STRING_FLAG_* constants, which the platform Settings app can use (general platform knowledge). Apply it to all three loadLabel sites. Google applied the same hunks on 14 through 17; only an unrelated neighboring import differs.",
      "The material has no fork comparison section, so no per-file fork verdict can be given."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact enclosing method names for the three patched sites are not shown (hunk headers show only the class declarations).",
      "Why only 7/8 lines of the patched region are reported present at the latest security tags is not shown.",
      "How the label hid the requested permissions (length, newlines, whitespace, or other properties) and which screen content was obscured is not shown. The commit message only says the label is now trimmed via loadSafeLabel.",
      "What the NameNotFoundException title fallbacks display is not shown in the hunks."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0095": {
     "cve": "CVE-2026-0095",
     "summary": "Widens the local buf_size in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the allocation size passed to osi_malloc is no longer truncated before the memcpy of no_of_bytes into the new buffer.",
     "component": "platform/packages/modules/Bluetooth, system/stack/l2cap (L2CAP flow control / retransmission, l2c_fcr.cc)",
     "weakness": "Integer truncation of an allocation size leading to a heap buffer overflow (undersized allocation followed by memcpy of the untruncated length)",
     "root_cause": "In l2c_fcr_clone_buf, buf_size was declared uint16_t and assigned no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN. Both no_of_bytes and new_offset are uint16_t parameters. Their sum plus the header and FCS lengths can exceed 65535, and when it does, the value stored in the uint16_t buf_size wraps to a small number. osi_malloc(buf_size) then returns a buffer smaller than needed, but the memcpy that follows still copies the full no_of_bytes to ((uint8_t*)(p_buf2 + 1)) + new_offset, writing past the end of the allocation.",
     "fix_analysis": [
      "system/stack/l2cap/l2c_fcr.cc:l2c_fcr_clone_buf: changes the type of buf_size from uint16_t to uint32_t. The arithmetic (already done at size_t width because of sizeof(BT_HDR)) is now stored without 16-bit truncation. osi_malloc therefore gets the real size (at most 2*65535 + sizeof(BT_HDR) + L2CAP_FCS_LEN), and the memcpy of no_of_bytes at offset new_offset fits in the allocated buffer.",
      "No other lines change. p_buf2->offset = new_offset and p_buf2->len = no_of_bytes are unchanged; they take the original uint16_t parameters, so no truncation happens there."
     ],
     "completeness": [
      "The fix removes the truncation in the destination allocation size. The destination buffer is now always large enough for new_offset + no_of_bytes + L2CAP_FCS_LEN.",
      "The function still does not check that no_of_bytes stays within the source buffer (p_buf->len). As before the fix, the source-side bound is left to the caller.",
      "No rejection or clamping of large new_offset/no_of_bytes combinations is added. Oversized requests now simply get a larger allocation.",
      "Single-line, single-file fix. The material shows no changes to other call sites or sibling functions."
     ],
     "behavior_changes": [
      "No new error paths or return values. In the case that used to overflow, the function now allocates a correctly sized (larger) buffer instead of an undersized one. Otherwise behavior is the same."
     ],
     "preconditions": [
      "l2c_fcr_clone_buf must be called with new_offset + no_of_bytes + sizeof(BT_HDR) + L2CAP_FCS_LEN greater than 65535, i.e. a large no_of_bytes and/or new_offset. The function is in the L2CAP flow control / retransmission (FCR) code of the Bluetooth stack.",
      "The material (diff, context, index search) does not show the callers or how the arguments are derived from remote data. Which L2CAP mode or peer traffic reaches the oversized case is unknown."
     ],
     "per_major": {
      "14": "same fix: identical one-line uint16_t -> uint32_t change in l2c_fcr_clone_buf, commit 006620f1caaa in android-security-14.0.0_r28 (hunk at line 241). At android-security-14.0.0_r29, 4/5 lines of the patched region are present.",
      "15": "same fix: identical one-line change, commit f82bbb37a852 in android-security-15.0.0_r16 (hunk at line 242). At android-security-15.0.0_r17, 4/5 lines of the patched region are present.",
      "16": "same fix: commit abc8b696b615 in android-security-16.0.0_r7 (cherry-pick of a06794634568), hunk at line 229. The uint32_t buf_size line appears in the code context at android-security-16.0.0_r8, where 4/5 lines of the patched region are reported present.",
      "17": "same fix: commit 554513a00597 in the android-17.0.0_r1 base. At android-security-17.0.0_r1, 4/5 lines of the patched region are present."
     },
     "backport_notes": [
      "Single-line change with no dependencies. Across branches, only the hunk line numbers and the function signature formatting differ (wrapped on 14/15/17, single line on 16)."
     ],
     "confidence": "high",
     "unknowns": [
      "Which callers of l2c_fcr_clone_buf pass attacker-influenced no_of_bytes/new_offset, and under what L2CAP configuration. The material does not include callers.",
      "Whether any caller checks no_of_bytes against p_buf->len before the call. Not shown in the material.",
      "Whether the fix is also delivered through a Bluetooth module update in addition to the platform security tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0096": {
     "cve": "CVE-2026-0096",
     "summary": "Settings' Bluetooth forget-device dialog fragment now loads app labels via ApplicationInfo.loadSafeLabel (MAX_SAFE_LABEL_LENGTH, TRIM | FIRST_LINE flags) instead of the raw PackageManager.getApplicationLabel, so app-supplied labels are sanitized before use in the dialog.",
     "component": "platform/packages/apps/Settings, Bluetooth (ForgetDeviceDialogFragment)",
     "weakness": "Improper neutralization of app-controlled display text (app label injection in a system dialog)",
     "root_cause": "ForgetDeviceDialogFragment.getAppLabel(String packageName) returned mPackageManager.getApplicationLabel(mPackageManager.getApplicationInfo(packageName, 0)) with no sanitization (no length limit, trimming or first-line restriction). The fragment imports AssociationInfo, CompanionDeviceManager and ListFormatter, which suggests the labels belong to companion apps associated with the device and are combined into the dialog text. The material does not show that code directly. The application label is defined by the app itself, so unsanitized app-controlled text could reach the Settings confirmation dialog.",
     "fix_analysis": [
      "src/com/android/settings/bluetooth/ForgetDeviceDialogFragment.java:getAppLabel: replaces mPackageManager.getApplicationLabel(ApplicationInfo) with ApplicationInfo.loadSafeLabel(mPackageManager, PackageItemInfo.MAX_SAFE_LABEL_LENGTH, TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE). Adds the android.content.pm.PackageItemInfo import (android.text.TextUtils was already imported). The NameNotFoundException path (log and return \"\") is unchanged.",
      "tests/robotests/src/com/android/settings/bluetooth/ForgetDeviceDialogFragmentTest.java: the mock setup now stubs appInfo.loadSafeLabel(mPackageManager, MAX_SAFE_LABEL_LENGTH, TRIM | FIRST_LINE) on the mocked ApplicationInfo instead of mPackageManager.getApplicationLabel(appInfo). Imports PackageItemInfo and TextUtils."
     ],
     "completeness": [
      "Covers the label-loading path in ForgetDeviceDialogFragment.getAppLabel, the only production code changed by the diff.",
      "Sanitization is limited to what loadSafeLabel does with the MAX_SAFE_LABEL_LENGTH size argument and the TRIM and FIRST_LINE flags.",
      "No other call sites are changed. Per index search, other getAppLabel helpers elsewhere in the platform still use loadLabel without safe-label flags (see potential_variants). Other helpers, such as CredentialManager DataConverter.getAppLabel and PermissionController Utils.getAppLabel, already use loadSafeLabel."
     ],
     "behavior_changes": [
      "App labels shown via getAppLabel in the forget-device dialog are now limited by the MAX_SAFE_LABEL_LENGTH argument, trimmed and reduced to their first line, so long or multi-line labels render differently than before.",
      "Label loading now goes through the ApplicationInfo instance (loadSafeLabel) instead of PackageManager.getApplicationLabel, which changes how tests must mock it."
     ],
     "preconditions": [
      "User opens the Bluetooth forget/unpair confirmation dialog in Settings for a paired device.",
      "An app's package name must reach getAppLabel. The fragment's companion-device imports suggest this happens when an app has a companion association with the device, but the material does not show that code.",
      "The app controls its own application label.",
      "The companion-app part of this fragment may be flag-gated (the fragment imports com.android.settings.flags.Flags and the test uses RequiresFlagsEnabled). The exact flag is not shown."
     ],
     "per_major": {
      "14": "not affected per bulletin. At android-security-14.0.0_r29, 6/8 lines of the patched region are present. No fix shown.",
      "15": "not affected per bulletin. At android-security-15.0.0_r17, 6/8 lines of the patched region are present. No fix shown.",
      "16": "same fix: commit 24b35b078f39 in android-security-16.0.0_r7 (cherry-pick of 08db8c5058a3, Change-Id I49bf86eec5e1402b5797f5a8118b96e7505c3877, Bug 473005624), linked by bug_id. 7/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: commit 62b7a2483f4e in android-17.0.0_r1 (base). Identical source diff. The test hunk differs only in line offsets. 7/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit touching getAppLabel and its robotest. The robotest must mock loadSafeLabel on the ApplicationInfo mock instead of PackageManager.getApplicationLabel."
     ],
     "confidence": "high",
     "unknowns": [
      "How the app labels are combined into the dialog text, and what else the dialog shows (for example the Bluetooth device name), is not shown.",
      "How package names reach getAppLabel (the AssociationInfo handling) is not shown.",
      "Which aconfig flag gates the companion-app part of the dialog, and whether it is enabled on shipping Android 16 builds.",
      "Why Android 14/15 are not affected cannot be determined from the material beyond the partial line match.",
      "The exact semantics of the MAX_SAFE_LABEL_LENGTH size argument to loadSafeLabel are not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 10,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0098": {
     "cve": "CVE-2026-0098",
     "summary": "DocumentsUI's Shared.getCallingPackageName now only honors the Intent.EXTRA_PACKAGE_NAME calling-package override when the caller's UID has the platform signature (checkSignatures against SYSTEM_UID). Before the fix, any app with FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP was accepted.",
     "component": "platform/packages/apps/DocumentsUI, base/Shared.java (calling package identification)",
     "weakness": "Improper authorization: an overly permissive trust check (installation status used as a stand-in for platform trust) allowed the calling-package identity to be overridden",
     "root_cause": "Shared.getCallingPackageName(Activity) lets the calling app replace the value returned by Activity.getCallingPackage() with the string in Intent.EXTRA_PACKAGE_NAME. The gate for this override was isSystemApp(info) || isUpdatedSystemApp(info), i.e. ApplicationInfo.FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP. These flags reflect installation status, not platform signing. So any preinstalled app that is not platform-signed passed the check and could make DocumentsUI treat an arbitrary package name as the caller. The code also dereferenced activity.getIntent() without a null check.",
     "fix_analysis": [
      "src/com/android/documentsui/base/Shared.java: removed the helpers isSystemApp(ApplicationInfo) and isUpdatedSystemApp(ApplicationInfo), which tested FLAG_SYSTEM and FLAG_UPDATED_SYSTEM_APP.",
      "Shared.getCallingPackageName: the override condition is now `activity.getIntent() != null && isTrustedSystemSignature(activity.getPackageManager(), info.uid)`, where info is the ApplicationInfo looked up for Activity.getCallingPackage(). Only when this holds is Intent.EXTRA_PACKAGE_NAME read and, if non-empty, used as callingPackage.",
      "Shared.isTrustedSystemSignature(PackageManager, int) (new): returns pm.checkSignatures(callingUid, android.os.Process.SYSTEM_UID) == PackageManager.SIGNATURE_MATCH. The caller's UID must therefore be signed with the same certificate as UID 1000 (the platform key).",
      "The NameNotFoundException handling and the fallback to the unmodified Activity.getCallingPackage() value are unchanged."
     ],
     "completeness": [
      "The fix covers the single override site in Shared.getCallingPackageName. EXTRA_PACKAGE_NAME is still honored for platform-signed callers, so the override is narrowed, not removed.",
      "Trust is taken from info.uid of the package reported by Activity.getCallingPackage(). The diff does not change the lookup or the NameNotFoundException handling. What happens with a null calling package is not shown.",
      "This commit touches no other consumer of Intent.EXTRA_PACKAGE_NAME in DocumentsUI. The material does not show whether any exist.",
      "The new null guard on activity.getIntent() hardens the path but is incidental to the authorization change."
     ],
     "behavior_changes": [
      "Apps with FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP that are not signed with the platform certificate can no longer override the calling package name via Intent.EXTRA_PACKAGE_NAME. For them, DocumentsUI uses the real Activity.getCallingPackage() value.",
      "Platform-signed apps (signature match with UID 1000) keep the override.",
      "A null Intent on the activity no longer causes a dereference in this method. The override is simply skipped."
     ],
     "preconditions": [
      "Another app starts a DocumentsUI activity so that Activity.getCallingPackage() returns that app (for example via startActivityForResult, as the picker intents in development/samples/.../DocumentsSample.java do, per index search).",
      "Before the fix: the caller's ApplicationInfo had FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP set, the caller did not need to be platform-signed, and the launching Intent carried a non-empty Intent.EXTRA_PACKAGE_NAME.",
      "After the fix: the caller's UID must share the platform signature for the extra to take effect."
     ],
     "per_major": {
      "14": "same fix: platform/packages/apps/DocumentsUI a712c9468e47 in android-security-14.0.0_r28. Hunks are identical to the 16 commit, offset by 11 lines; blob ids 9f328be69..f691229ab are shared with the 15 and 17 commits. The resolver shows 4/8 region lines at android-security-14.0.0_r29, the same count as Android 16 r8, where the fix is confirmed present.",
      "15": "same fix: platform/packages/apps/DocumentsUI b05353f638a7 in android-security-15.0.0_r16, diff identical to Android 14. Resolver: 4/8 region lines at android-security-15.0.0_r17.",
      "16": "same fix: platform/packages/apps/DocumentsUI 0c8b7870ece9 in android-security-16.0.0_r7 (cherry-pick of internal cdad9a0002bd). The code at android-security-16.0.0_r8 shows the new condition, which calls isTrustedSystemSignature, in Shared.getCallingPackageName.",
      "17": "same fix: platform/packages/apps/DocumentsUI a08d1c7ea084 already in base android-17.0.0_r1, identical diff. Resolver: 3/8 region lines at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single change to Shared.java; the material shows no dependency on other commits. Google applied the same hunks on 14, 15, 16 and 17. Only the hunk line offsets differ: line 227 in the 16 commit, line 238 in the 14/15/17 commits."
     ],
     "confidence": "high",
     "unknowns": [
      "How DocumentsUI uses the callingPackage value returned by Shared.getCallingPackageName downstream (e.g. permission grants or UI attribution), and therefore what privilege a spoofed name gives. The material only shows the identification step.",
      "Whether DocumentsUI reads Intent.EXTRA_PACKAGE_NAME anywhere else.",
      "The content of bugs 424853397 and 397217317 beyond the commit message.",
      "Whether the hunk applies cleanly to customized fork trees."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0099": {
     "cve": "CVE-2026-0099",
     "summary": "Adds onNullBinding overrides to both ServiceConnection implementations in HostEmulationManager, and an onBindingDied override to the per-service connection, so that the NFC process unbinds from a HostApduService whose onBind returned null or whose binding died instead of keeping the binding; also adds a SERVICE_NULL_BINDING event-log enum value.",
     "component": "platform/packages/modules/Nfc (NfcNci), card emulation / HostEmulationManager service binding",
     "weakness": "Missing handling of the ServiceConnection null-binding / binding-died callbacks, which leaves a stale binding from the privileged NFC process to an app service (the Android 14 fix commit describes it as \"Several BAL bypasses in HostApduService\")",
     "root_cause": "HostEmulationManager binds to app-provided HostApduService components through a payment-service ServiceConnection and through HostEmulationServiceConnection. Before the fix, neither connection overrode onNullBinding, and HostEmulationServiceConnection did not override onBindingDied. So when the target service returned null from onBind, or the per-service binding died, no code path called unbindService and the NFC process kept a binding to the app's service. The Android 14 commit for the same bug describes the consequence as background activity launch (BAL) bypasses in HostApduService.",
     "fix_analysis": [
      "NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java, payment-service ServiceConnection (the anonymous class that ends just before HostEmulationServiceConnection): the new onNullBinding(ComponentName) writes a log line, calls mContext.unbindService(this) under synchronized (mLock), then logs an NfcPaymentServiceBindState event with BindState.SERVICE_NULL_BINDING and the component's package and class name.",
      "NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java, class HostEmulationServiceConnection: the new onBindingDied(ComponentName) writes a log line and calls unbindServiceIfNeededLocked(), not wrapped in synchronized (mLock). The new onNullBinding(ComponentName) writes a log line and calls mContext.unbindService(this) under synchronized (mLock).",
      "NfcNci/proto/event.proto, enum BindState: adds SERVICE_NULL_BINDING = 3 so the new payment-connection event can be recorded."
     ],
     "completeness": [
      "Covers the null-binding case for both the payment connection and HostEmulationServiceConnection, and the binding-died case for HostEmulationServiceConnection. The fix does not touch onBindingDied on the payment connection, and the material does not show whether that connection already overrides it.",
      "HostEmulationServiceConnection.onNullBinding only calls mContext.unbindService(this). It does not remove the mComponentNameToConnectionsMap entry keyed by ComponentNameAndUser(mUserId, name), whereas the idle path in onServiceConnected removes that entry after unbinding.",
      "HostEmulationServiceConnection.onBindingDied calls unbindServiceIfNeededLocked() without taking mLock, while the Android 14 version wraps the same call in synchronized (mLock). The material does not show whether the calling context already holds the lock.",
      "The new onNullBinding paths call mContext.unbindService(this) without the try/catch for IllegalArgumentException that onServiceConnected uses around the same call.",
      "The material does not show the body of the private ServiceConnection mConnection field that follows HostEmulationServiceConnection on 16."
     ],
     "behavior_changes": [
      "NFC now unbinds immediately when a bound HostApduService returns null from onBind, for both payment and non-payment connections, and when the binding of a HostEmulationServiceConnection dies.",
      "New Log.i lines \"onNullBinding: <component>\" and \"onBindingDied: <component>\".",
      "A new NfcPaymentServiceBindState event with BindState.SERVICE_NULL_BINDING is written to the NFC event log for the payment connection, and event.proto gains the enum value SERVICE_NULL_BINDING = 3."
     ],
     "preconditions": [
      "An installed app exposes a HostApduService that HostEmulationManager binds to, either as the preferred payment service or as a per-service connection.",
      "That service returns null from onBind (which triggers onNullBinding) or its binding dies (which triggers onBindingDied). The exact triggering sequence is not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix: platform/packages/apps/Nfc 34a7821a1bfe (\"Several BAL bypasses in HostApduService.\") in android-security-14.0.0_r28. It adds both onBindingDied and onNullBinding to the payment connection (calling unbindPaymentServiceLocked()) and to mConnection (calling unbindServiceIfNeededLocked()), all under synchronized (mLock), with no logging and no proto change. 4/4 lines of the patched region are present at r29.",
      "15": "adapted fix: platform/packages/apps/Nfc 7fb6795b51c3 in android-security-15.0.0_r16. It adds onNullBinding to the payment connection (mContext.unbindService(this) under mLock) and both onBindingDied and onNullBinding to mConnection, with Log.i lines but no proto change or event logging. 4/4 lines of the patched region are present at r17.",
      "16": "this fix: platform/packages/modules/Nfc 127162a9e764 in android-security-16.0.0_r7 (cherry-pick of cfbd02ac1630), including the SERVICE_NULL_BINDING proto value and the event logging. 6/6 lines of the patched region are present at r8.",
      "17": "same fix, in the base release: platform/packages/modules/Nfc cfbd02ac1630 in android-17.0.0_r1 (the original of the Android 16 cherry-pick). 6/6 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The source location differs by release: src/com/android/nfc/cardemulation/HostEmulationManager.java in platform/packages/apps/Nfc on 14/15, versus NfcNci/src/... in platform/packages/modules/Nfc on 16/17. NFC is a mainline module on newer releases (general platform knowledge). On trees where NfcEventProto does not exist, drop the proto enum and the NfcEventLog call. The 14 fix also adds onBindingDied to the payment connection.",
      "On 14/15 the per-service connection is an anonymous mConnection, while on 16 it is HostEmulationServiceConnection with mComponentNameToConnectionsMap. The unbind helper you use (unbindServiceIfNeededLocked versus unbindService(this)) must match the state tracking in your tree."
     ],
     "confidence": "high",
     "unknowns": [
      "The precise consequence of the stale binding is not detailed in the material (the bulletin rates it High EoP, and the 14 commit title says BAL bypasses).",
      "Whether the payment connection on 16/17 already overrides onBindingDied.",
      "Whether unbindServiceIfNeededLocked() is safe to call without mLock from onBindingDied on 16, and whether leaving the mComponentNameToConnectionsMap entry in place after onNullBinding has any effect.",
      "The implementation of the mConnection field that follows HostEmulationServiceConnection on 16/17."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0100": {
     "cve": "CVE-2026-0100",
     "summary": "Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp). If a RES_TABLE_PACKAGE_TYPE chunk has a 32-bit package id above 255, the package load now fails instead of storing that value as the package id.",
     "component": "platform/frameworks/base, libs/androidfw (resource table / ARSC parsing, LoadedArsc)",
     "weakness": "missing bounds check on untrusted input (the package id from the ResTable_package header was not validated against the 8-bit package id space)",
     "root_cause": "In LoadedPackage::Load, the package id was copied straight from the ResTable_package header (loaded_package->package_id_ = dtohl(header->id)) with no validation. The header field is 32 bits, but resource IDs reserve only 8 bits for the package id, so a malformed ARSC file could set a package id above 255. The commit message says such an out-of-range id could lead to unexpected behavior or overflow when the table is parsed. In the same function, the neighbouring typeIdOffset field is range-checked against std::numeric_limits<uint8_t>::max(); the fix does not touch that check.",
     "fix_analysis": [
      "libs/androidfw/LoadedArsc.cpp:LoadedPackage::Load: reads dtohl(header->id) into a local const uint32_t package_id. If package_id > std::numeric_limits<uint8_t>::max(), it logs 'RES_TABLE_PACKAGE_TYPE package id is out of valid range' and returns {} (a null unique_ptr), rejecting the package. Only after the check is loaded_package->package_id_ set, via static_cast<int>(package_id). The existing PROPERTY_DYNAMIC logic (id == 0, or id == kAppPackageId with PROPERTY_DYNAMIC) is unchanged and now runs only on validated ids.",
      "libs/androidfw/tests/LoadedArsc_test.cpp: adds TEST(LoadedArscTest, LoadArscWithInvalidPackageId). It builds a minimal in-memory ResTable_header plus ResTable_package with id = 256 and asserts that LoadedArsc::Load returns null."
     ],
     "completeness": [
      "The check covers the package id assignment in LoadedPackage::Load shown in the material. The material does not show other places where package_id_ might be set or used.",
      "After the fix, LoadedPackage::Load range-checks both header->id and header->typeIdOffset against uint8_t max.",
      "The fix rejects the whole package (and, per the test, the whole LoadedArsc load) rather than clamping or skipping the value. This commit touches no other parsing path in LoadedArsc.cpp. The material does not show the downstream code where an oversized id caused the 'unexpected behavior or overflow', so it cannot tell us whether other fields have a similar missing range check."
     ],
     "behavior_changes": [
      "Loading an ARSC whose RES_TABLE_PACKAGE_TYPE chunk has id > 255 now fails: LoadedPackage::Load returns null and LoadedArsc::Load returns null for that table (as the new test asserts). It also writes the ERROR log 'RES_TABLE_PACKAGE_TYPE package id is out of valid range'.",
      "Well-formed tables (package id 0..255) are unaffected: package_id_ gets the same value as before."
     ],
     "preconditions": [
      "A resource table parsed by libandroidfw's LoadedArsc::Load / LoadedPackage::Load that contains a RES_TABLE_PACKAGE_TYPE chunk whose id field is greater than 255.",
      "The table must otherwise pass the earlier checks in LoadedPackage::Load (package chunk header at least kMinPackageSize).",
      "The material does not show how such a table would reach a process in a real attack. libandroidfw parses APK resource tables on behalf of apps and system processes via AssetManager (general platform knowledge)."
     ],
     "per_major": {
      "14": "same fix: platform/frameworks/base 6a9eb9dd74d0 in android-security-14.0.0_r28, identical hunks (only line offsets differ, LoadedArsc.cpp @@ -476). At android-security-14.0.0_r29, 7 of the 8 lines of the patched region are present.",
      "15": "same fix: platform/frameworks/base 86dd9d421620 in android-security-15.0.0_r16, identical hunks (LoadedArsc.cpp @@ -481). At android-security-15.0.0_r17, 7 of the 8 lines of the patched region are present.",
      "16": "same fix: platform/frameworks/base ca855338abcf in android-security-16.0.0_r7 (cherry-pick, Change-Id I438cd0e7f64e2b0ef4cc2bcb4f1f81778216e5e8). The full check is visible in LoadedPackage::Load at android-security-16.0.0_r8 (lines 488-494), even though the per-major facts report 7 of 8 lines.",
      "17": "same fix: platform/frameworks/base 50d18b36c708 in android-17.0.0_r1, identical hunks (LoadedArsc.cpp @@ -549, test file @@ -421). At android-security-17.0.0_r1, 7 of the 8 lines of the patched region are present."
     },
     "backport_notes": [
      "A single self-contained commit. The production hunk replaces 'loaded_package->package_id_ = dtohl(header->id);' in LoadedPackage::Load with 7 lines, and Google applied it identically on 14 through 17. In the Android 16 context, std::numeric_limits<uint8_t> is already used by the adjacent typeIdOffset check."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact downstream location where an out-of-range package id caused unsafe behavior. The commit message only says 'unexpected behavior or overflow when being parsed'.",
      "The concrete attack surface by which an attacker-controlled ARSC reaches a privileged process. The bulletin only rates it High EoP.",
      "Why the per-major facts report only 7 of 8 patched-region lines at the latest security tags."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28574": {
     "cve": "CVE-2026-28574",
     "summary": "In HostEmulationManager.HostEmulationServiceConnection.onServiceConnected, when the HCE state is already STATE_IDLE and the connected service is not the preferred service, the fix now unbinds the service connection and removes its entry from mComponentNameToConnectionsMap. Before the fix, the code returned and left the binding alive.",
     "component": "platform/packages/modules/Nfc, NfcNci card emulation (HostEmulationManager)",
     "weakness": "race condition leading to a leaked service binding (missing cleanup on an early-return path)",
     "root_cause": "HostEmulationManager binds to a host card emulation service. If mState had already gone back to STATE_IDLE by the time onServiceConnected ran, and the component was no longer the preferred service, the code took an early return (\"Service is already deactivated and not preferred, don't bind\"). That early return neither called unbindService nor cleared the tracking entry in mComponentNameToConnectionsMap, so the binding the NFC service started stayed in place. The commit title says this left a permanent background activity launch (BAL) grant tied to the binding, which matches the EoP type in the bulletin.",
     "fix_analysis": [
      "NfcNci/src/com/android/nfc/cardemulation/HostEmulationManager.java:HostEmulationServiceConnection.onServiceConnected: inside the existing `mState == STATE_IDLE && !name.equals(preferredServiceName)` branch (held under mLock), the fix calls mContext.unbindService(this) before returning. It catches IllegalArgumentException and logs the warning 'Failed to unbind <name>'.",
      "Same branch: the fix also removes the ComponentNameAndUser(mUserId, name) key from mComponentNameToConnectionsMap.",
      "Single commit, 7 lines added. No other paths or files are touched."
     ],
     "completeness": [
      "The fix covers only the early-return path in onServiceConnected where the state is idle and the service is not preferred. The normal path (state not idle, or service is preferred) still stores the Messenger and keeps the binding as before.",
      "The map removal is unconditional. When isMultipleBindingSupported() is false, the connection is tracked through mService/mServiceName/mServiceBound rather than the map. This branch does not touch those fields, which is consistent because they are never set on this path.",
      "An unbindService failure is only logged and not retried. If unbinding throws, the map entry is still removed."
     ],
     "behavior_changes": [
      "A host emulation service that connects after the state has already returned to idle, and that is not the preferred service, is now unbound immediately instead of staying bound.",
      "New warning log 'Failed to unbind <name>' if unbindService throws IllegalArgumentException.",
      "The connection entry for that component/user is removed from mComponentNameToConnectionsMap."
     ],
     "preconditions": [
      "HostEmulationManager must have bound a host card emulation service.",
      "The onServiceConnected callback must arrive after mState has returned to STATE_IDLE and while the component is not the preferred service. This is a timing-dependent race.",
      "packages/modules/Nfc (NfcNci) must be present. Per the per-major facts, the project is not present in the Android 14/15 security tags."
     ],
     "per_major": {
      "14": "Not affected per the bulletin. The project packages/modules/Nfc is not present in android-security-14.0.0_r29 (code absent).",
      "15": "Not affected per the bulletin. The project is not present in android-security-15.0.0_r17 (code absent).",
      "16": "Same fix: commit 477d0eb9f0f6 in android-security-16.0.0_r7 (cherry-pick of 6564c573280aad995b6e71536a1b8c190c63afea). 4/4 patched lines are present at android-security-16.0.0_r8.",
      "17": "Listed as affected (17-next), but marked platform-only, with no resolved commit in the material. At android-security-17.0.0_r1 only 3/4 lines of the patched region are present, so the fix status there is unconfirmed."
     },
     "backport_notes": [
      "Single self-contained hunk inside onServiceConnected. It uses mComponentNameToConnectionsMap and ComponentNameAndUser, which already exist in the Android 16 code around the fix."
     ],
     "confidence": "high",
     "unknowns": [
      "How the leaked binding turns into a background activity launch grant is stated only in the commit title. The diff does not show it.",
      "Whether Android 17 (android-security-17.0.0_r1, 3/4 lines present) contains an equivalent fix or a differently formatted version of it.",
      "The provided context does not show which caller or sequence races the deactivation against onServiceConnected.",
      "Whether the fix also reaches devices through a module update separate from the platform security tag."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28577": {
     "cve": "CVE-2026-28577",
     "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the supplied WindowToken already contains a window, enforcing one toast window per toast token.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowManagerService.java)",
     "weakness": "Missing validation / improper enforcement of a one-window-per-token invariant (token reuse check absent)",
     "root_cause": "In WindowManagerService.addWindow, the only guard in the TYPE_TOAST block was displayContent.canAddToastWindowForUid(callingUid), which limits toast windows per calling UID. Nothing checked whether the WindowToken passed by the caller was already occupied. A TYPE_TOAST WindowToken is meant to hold a single toast window, but a second toast window could be attached to the same non-empty token. The added test does this from a second UID, which bypasses the per-UID check. The service therefore did not enforce the design invariant that a toast token holds exactly one toast window.",
     "fix_analysis": [
      "services/core/java/com/android/server/wm/WindowManagerService.java:addWindow: inside the `if (type == TYPE_TOAST)` block, immediately after the canAddToastWindowForUid check, a new condition `if (addToastWindowRequiresToken && !token.isEmpty())` returns WindowManagerGlobal.ADD_BAD_APP_TOKEN. A toast window can therefore only be added to an empty toast token. The check runs before the WINDOW_HIDE_TIMEOUT message is scheduled, and after validateAddingWindowLw and openInputChannel.",
      "On Android 16 and 15 the accompanying ProtoLog.w(WM_ERROR, \"Adding toast window with non-empty token.\") line is commented out. On Android 14 and 17 it is active, and Android 14 also registers the message in data/etc/services.core.protolog.json.",
      "services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java: the new test testAddToastWindow_singleWindowPerToken registers a TYPE_TOAST token via addWindowToken and adds one toast window from uid 1234 (targetSdkVersion O). It then adds a second toast window with the same token from uid 1235 and asserts ADD_BAD_APP_TOKEN."
     ],
     "completeness": [
      "Covers only the addWindow path for type == TYPE_TOAST. The check is a simple token.isEmpty() test and does not look at what kind of window already occupies the token or who owns the token.",
      "The new check is gated by addToastWindowRequiresToken. The surrounding comments indicate that older apps may add toasts without a token, so for such callers the new rejection does not apply and only the existing canAddToastWindowForUid guard remains.",
      "The fix adds no ownership or UID binding between the toast WindowToken and the caller. It only prevents a second window from being added to a token that already has one.",
      "No change to WindowToken, DisplayContent.canAddToastWindowForUid, or the removal and hide-timeout paths."
     ],
     "behavior_changes": [
      "addWindow returns WindowManagerGlobal.ADD_BAD_APP_TOKEN when a TYPE_TOAST window targets a non-empty token and addToastWindowRequiresToken is true.",
      "On Android 14 and 17 a new WM_ERROR ProtoLog warning \"Adding toast window with non-empty token.\" is emitted. On 15 and 16 that log line is commented out, so nothing is logged."
     ],
     "preconditions": [
      "Caller reaches WindowManagerService.addWindow with attrs.type == TYPE_TOAST (normally through the app's IWindowSession / Session binder interface (general platform knowledge)).",
      "addToastWindowRequiresToken is true for the caller. The context comments tie this to newer apps that must supply a token, and the test uses targetSdkVersion O.",
      "The supplied token resolves to a WindowToken that already contains at least one window. The test uses a TYPE_TOAST token registered via addWindowToken and a second caller with a different UID, so canAddToastWindowForUid does not reject the request."
     ],
     "per_major": {
      "14": "adapted fix (2f08e7d895e3 in android-security-14.0.0_r28): same 6-line check, with the ProtoLog.w line active and a new entry added to data/etc/services.core.protolog.json. Test support was backported by adding a @VisibleForTesting Session(service, callback, callingPid, callingUid) constructor in Session.java and a partial-backport WindowTestsBase.createTestSession helper. 8/8 patched lines present at r29.",
      "15": "same fix (9af61140db79 in android-security-15.0.0_r16): identical check with the ProtoLog.w line commented out, and the same test added. 8/8 lines present at r17.",
      "16": "same fix (d9ad94e69f4e in android-security-16.0.0_r7): check with ProtoLog.w commented out, and the test added. 8/8 lines present at r8.",
      "17": "adapted fix (a00072f03623 in android-security-17.0.0_r1): identical check with ProtoLog.w active. The test is adapted to the newer addWindow signature that takes a WindowRelayoutResult. 8/8 lines present at r1."
     },
     "backport_notes": [
      "The production change is a self-contained 6-line insertion after the canAddToastWindowForUid check in addWindow. The only branch-specific choice is the ProtoLog.w line: keep it and add the services.core.protolog.json entry as the Android 14 commit does, or comment it out as 15 and 16 did.",
      "If the target branch lacks WindowTestsBase.createTestSession(atms, pid, uid), take the Session constructor and helper backport from the Android 14 commit (2f08e7d895e3)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how addToastWindowRequiresToken is computed beyond the context comments (newer vs. older apps), nor how a second process obtains a toast token that is already in use.",
      "The concrete privilege-escalation consequence of attaching a second toast window to an occupied token is not shown. The material gives only the bulletin's High/EoP rating.",
      "The material does not show where in addWindow the window is added to the token relative to the new check.",
      "The commits do not say why the ProtoLog line was commented out on 15 and 16 but kept on 14 and 17.",
      "The material does not show how the client handles the ADD_BAD_APP_TOKEN result."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28578": {
     "cve": "CVE-2026-28578",
     "summary": "DevicePolicyManagerService now rejects PackagePolicy objects containing over-long package names in the managed profile contacts/caller-id access setters and the credential manager policy setter. It loops over policy.getPackageNames() and enforces the maximum package name length before the policy is accepted.",
     "component": "platform/frameworks/base, DevicePolicyManagerService (services/devicepolicy)",
     "weakness": "Missing input length validation (package name strings inside PackagePolicy not bounded)",
     "root_cause": "DevicePolicyManagerService.java has three PackagePolicy setters that accepted the package name strings inside the PackagePolicy without any length check. Two are guarded by isProfileOwner(caller) && isManagedProfile(caller.getUserId()); the commit message and tests identify them as the managed profile contacts / caller-id access policies. The third is the credential manager policy setter, guarded by canWriteCredentialManagerPolicy. As a result, a caller could submit arbitrarily long package names. The bulletin classifies the impact as DoS (High).",
     "fix_analysis": [
      "DevicePolicyManagerService.java: new private helper enforcePackagePolicyPackageNamesLength(@NonNull PackagePolicy policy) iterates policy.getPackageNames(), skips null entries, and calls PolicySizeVerifier.enforceMaxPackageNameLength(pkg) on each. The Android 14 variant uses Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, \"Package name too long\") instead.",
      "DevicePolicyManagerService.java: the first managed-profile PackagePolicy setter (authorization isProfileOwner && isManagedProfile) gains `if (policy != null) enforcePackagePolicyPackageNamesLength(policy);`. It sits after the mHasFeature early return and before getCallerIdentity()/checkCallAuthorization.",
      "DevicePolicyManagerService.java: identical insertion in the second managed-profile PackagePolicy setter (same authorization pattern).",
      "DevicePolicyManagerService.java: identical insertion in the credential manager policy setter (the method immediately preceding canWriteCredentialManagerPolicy and getCredentialManagerPolicy, presumably setCredentialManagerPolicy). It is placed before Preconditions.checkCallAuthorization(canWriteCredentialManagerPolicy(caller)).",
      "Single commit per branch touching one file. The diff includes no AIDL, client-side (DevicePolicyManager / PackagePolicy) or storage changes."
     ],
     "completeness": [
      "Covers exactly the three PackagePolicy setters touched in the diff. Validation happens at the service entry point of these setters.",
      "Only the length of each package name is checked. This change does not validate the number of entries in the PackagePolicy or the package-name format; whether they are limited elsewhere is not shown.",
      "A null policy and null entries inside the list are skipped, so the check is purely a per-string length cap.",
      "The fix is a prevention check only. It does not show any cleanup or migration of over-long names already stored on devices before the patch.",
      "The material does not show other setters that accept PackagePolicy or package-name lists, so whether any sibling path remains unvalidated cannot be judged from the diff."
     ],
     "behavior_changes": [
      "Calls to the three setters with a PackagePolicy containing a package name longer than the maximum now throw an exception from the service instead of being accepted. On Android 14 this is IllegalArgumentException from Preconditions.checkArgument. On 15/16/17 it is whatever PolicySizeVerifier.enforceMaxPackageNameLength throws, which the material does not show.",
      "The length check runs before the caller authorization check, so an unauthorized caller supplying over-long names now gets the length error rather than the authorization failure. It runs after the mHasFeature check, so devices without the device admin feature are unchanged.",
      "No change for package names within the limit."
     ],
     "preconditions": [
      "Device has the device admin feature (mHasFeature true); otherwise the setters return early.",
      "For the two managed-profile setters, the caller must be the profile owner of a managed profile (isProfileOwner(caller) && isManagedProfile(caller.getUserId())) for the policy to be accepted. The new check itself runs before that authorization.",
      "For the credential manager policy setter, the caller must satisfy canWriteCredentialManagerPolicy. The context shows hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS) as one of its OR-ed alternatives; the other alternatives are not shown.",
      "Caller supplies a PackagePolicy whose getPackageNames() contains one or more strings exceeding the maximum package name length."
     ],
     "per_major": {
      "14": "adapted fix: same three insertion points and helper, but the helper uses Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, \"Package name too long\") instead of PolicySizeVerifier.enforceMaxPackageNameLength. Commit 1d7703a6b66d in android-security-14.0.0_r28; 8/8 lines present at r29.",
      "15": "same fix: identical diff using PolicySizeVerifier.enforceMaxPackageNameLength. Commit e8ff8af05575 in android-security-15.0.0_r16; 8/8 lines present at r17.",
      "16": "same fix: cherry-pick 48c17df8bdc4 in android-security-16.0.0_r7, Change-Id I34d6c9f8a48aae185e12e4351da52582d285618e; 8/8 lines present at r8.",
      "17": "same fix: change 4914d6e98bd2 (listed as Cherrypick-From of the 16 commit) in android-17.0.0_r1. In the visible context, authorization uses mDeviceAdmins.isProfileOwner(caller). 8/8 lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single self-contained commit. The only dependency is PolicySizeVerifier.enforceMaxPackageNameLength. On trees that lack that helper, substitute Preconditions.checkArgument(pkg.length() <= MAX_PACKAGE_NAME_LENGTH, ...), as Google did for Android 14.",
      "Hunk line numbers differ between branches: about 14.4k/16.4k on 15, 14.4k/16.5k on 16, 14.5k/16.7k on 14 and 13.5k/15.9k on 17. Anchor on the three setters by their `if (!mHasFeature) return;` prologue followed by the isProfileOwner/isManagedProfile or canWriteCredentialManagerPolicy authorization."
     ],
     "confidence": "high",
     "unknowns": [
      "The exact names of the three setters are not shown in the hunk headers. The commit message and CTS tests point to the managed profile caller-id and contacts access policy setters and the credential manager policy setter.",
      "The concrete DoS mechanism (what consumed or failed on over-long names) is not shown in the material.",
      "The exception type thrown by PolicySizeVerifier.enforceMaxPackageNameLength and the value of the maximum length are not shown.",
      "Whether other PackagePolicy or package-list setters in DevicePolicyManagerService were already covered by PolicySizeVerifier is not shown.",
      "Whether and how these policies are persisted is not shown."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 7,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  },
  "17": {
   "counts": {
    "announced": 59,
    "certain": 53,
    "probable": 6,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2025-64505",
     "osv_id": "ASB-A-463980379",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "dc721fee982cebc9cf07057ff85f2bbb28b65e20",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-64720",
     "osv_id": "ASB-A-463995203",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "20c02c533b25e9b5327b9bebefdc9e8193ed27de",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2025-65018",
     "osv_id": "ASB-A-463998243",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/libpng",
       "sha": "0e9750be87cbba8e97ee89de420014d40f9bb883",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/libpng"
     ]
    },
    {
     "cve": "CVE-2026-0039",
     "osv_id": "ASB-A-470966846",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0040",
     "osv_id": "ASB-A-467994860",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0041",
     "osv_id": "ASB-A-467994310",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0042",
     "osv_id": "ASB-A-456471487",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0043",
     "osv_id": "ASB-A-453649377",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0044",
     "osv_id": "ASB-A-449728942",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0051",
     "osv_id": "ASB-A-461790658",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "7937f2f835abbc32896ea515ed7962e377883562",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0052",
     "osv_id": "ASB-A-467352655",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that integer-overflow sanitizer hits throw a C++ std::runtime_error, via a bundled replacement runtime (ubsan-replacement/ubsan_throwing_runtime.cpp), instead of the hard abort of the ubsan minimal runtime.",
      "confidence": "medium",
      "backport_notes": [
       "The change touches only the build file plus one new file. It must land in the cc_library block that produces the shipped libdng_sdk: set sanitize.never, add the -fsanitize cflags and -fno-sanitize-link-runtime, and add ubsan-replacement/ubsan_throwing_runtime.cpp to srcs. Check that C++ exceptions are enabled for the library, as the commit message assumes.",
       "On the Android 17 security tag, confirm that the shipped variant still carries sanitize.never and -fno-sanitize-link-runtime. The resolver matched only 5/6 lines of the patched region there."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "81ded662eee07fcbb830e6b6c83d1a3d85227ceb",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 74a5a91d169a mentions bug 449728942, carried by 81ded662eee0 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0080",
     "osv_id": "ASB-A-462431872",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error from a project-local handler runtime instead of aborting the process through the ubsan-minimal runtime.",
      "confidence": "medium",
      "backport_notes": [
       "The fix is build configuration plus one new source file. It relies on C++ exceptions being enabled for libdng_sdk (the commit message says they are), and on sanitize.never: true together with -fno-sanitize-link-runtime so that ubsan_minimal is not linked. Check where your fork's Android.bp sets the misc_undefined overflow checks for this module."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 4,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "81ded662eee07fcbb830e6b6c83d1a3d85227ceb",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 74a5a91d169a mentions bug 449728942, carried by 81ded662eee0 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0097",
     "osv_id": "ASB-A-446114623",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "57541dad9e1f98f445bd5d25742a96c3f203db6f",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-21352",
     "osv_id": "ASB-A-483693973",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "9a9e97362f2df6f047c3e0cd27374aca34598cd1",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-21353",
     "osv_id": "ASB-A-483697751",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "Critical",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "9a9e97362f2df6f047c3e0cd27374aca34598cd1",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2025-26418",
     "osv_id": "ASB-A-351830787",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/services/Car",
       "sha": "91c7ed0c3871601ee0bbbfd8860117df1349123a",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/services/Car"
     ]
    },
    {
     "cve": "CVE-2025-32348",
     "osv_id": "ASB-A-406880479",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "58822cb0d7e46cc6309de4ae6906305f6eed0fce",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48581",
     "osv_id": "ASB-A-428945391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds sub_dir: \"sysconfig\" to the apexd.mainline_patch_level_2 prebuilt_etc module in platform/system/apex so that com.google.android.mainline.patchlevel.2.xml is installed under etc/sysconfig instead of directly under etc, making the feature declaration it carries accessible to the device.",
      "confidence": "medium",
      "backport_notes": [
       "Only relevant to trees containing system/apex/patchlevel/Android.bp (Android 16+). Apply the single sub_dir line; verify the resulting image has etc/sysconfig/com.google.android.mainline.patchlevel.2.xml and that no device-specific overlay installs a conflicting copy at the old etc/ path."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 2,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/system/apex",
       "sha": "8b89163d61b800ca56ce974e3546790b8617da9d",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 117f34be6322 mentions bug 436433817, carried by 8b89163d61b8 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/system/apex"
     ]
    },
    {
     "cve": "CVE-2025-48595",
     "osv_id": "ASB-A-430889718",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/sqlite",
       "sha": "e13117e12f886a4f3807ae1ba8e284d8c16fadc6",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/sqlite"
     ]
    },
    {
     "cve": "CVE-2025-48612",
     "osv_id": "ASB-A-429417453",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "1553d28f0f574157ed7d9cbcce86f014469979b3",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2025-48615",
     "osv_id": "ASB-A-433250316",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "013bb57034ee44eb4bcaf6e808732b6f1eb780b0",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48616",
     "osv_id": "ASB-A-438973280",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "52a6d6dfda83c584c99a39321f85bb235c6667fc",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48648",
     "osv_id": "ASB-A-396667508",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "44ee96106f25ef7e7393493de65ac53e1b6c6705",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48649",
     "osv_id": "ASB-A-339109116",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "23da448adbb0bdfc2449009fa1c64f019c6c0c9e",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2025-48652",
     "osv_id": "ASB-A-452042097",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "PackageInstaller now skips the unknown-sources user restriction check only when the installer holds INSTALL_PACKAGES and did not start the install via an ACTION_VIEW/ACTION_INSTALL_PACKAGE intent, or when it is a privileged app that set EXTRA_NOT_UNKNOWN_SOURCE; previously any INSTALL_PACKAGES holder bypassed the check regardless of intent action.",
      "confidence": "medium",
      "backport_notes": [
       "This is a Reapply of a previously reverted change. In InstallStart.java the locals isPrivilegedAndKnown, isInstallPkgPermissionGranted and intentAction must be in scope at the checkDevicePolicyRestrictions call (the 15/16 backports add String intentAction).",
       "The InstallRepository.kt hunk differs by branch: on 16 the INSTALL_PACKAGES check uses callingUid and isInstallPermissionGrantedOrRequested gets a new guard; on 17 the check uses originatingUid. The 15 fix did not change InstallRepository.kt at all, so check whether the v2 flow exists and is used on the target branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 5,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "81f83fdb0944d0d8a3337d2578d73dd77d60143b",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 4dc009cd724c mentions bug 438352252, carried by 81f83fdb0944 (score 0.82)",
       "similarity": 0.82
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0009",
     "osv_id": "ASB-A-329631990",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "bf034ab4e140ba913b1d98e7ca9ecfb652f96973",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/MediaProvider"
     ]
    },
    {
     "cve": "CVE-2026-0016",
     "osv_id": "ASB-A-460933604",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a631806c7aef2d0adf40793883957956aaada357",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0018",
     "osv_id": "ASB-A-449392803",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "637f5b4aa4fbc87010f49f69555f6d14caef6a1f",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0036",
     "osv_id": "ASB-A-405392600",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "f60c7771358ce3e5b84e4fe1f57f6f41da985841",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0045",
     "osv_id": "ASB-A-380091558",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "92611bfc4d26f510490144f781662ccfc3da218e",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0048",
     "osv_id": "ASB-A-463364410",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6ca1d6b26237d3f1ae0dac23e5f4bb487b23bf93",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0050",
     "osv_id": "ASB-A-290364858",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "921aaec06df0b5974d0fbbb40443935b15019270",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0055",
     "osv_id": "ASB-A-460779368",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c17553943026257959419fdd7912e191e026a861",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0056",
     "osv_id": "ASB-A-462431486",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "d69b0c6010e5ba1e8c7bc373da4ed74b2f7efa37",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0059",
     "osv_id": "ASB-A-447536200",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "RCE"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "5288e3e85ba98e8ef6b91f287e1aa18f1bb28ac5",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0060",
     "osv_id": "ASB-A-459639258",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "1c2cf92569120fda632767cf8f69e3c9e0756ff9",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0061",
     "osv_id": "ASB-A-452010556",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "e58fd9cd11e400235ac480b4fba6a20f8161ff9b",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0067",
     "osv_id": "ASB-A-470967228",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error, using a hidden-visibility replacement runtime (ubsan-replacement/ubsan_throwing_runtime.cpp), instead of going through the ubsan-minimal runtime, which aborts the process.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit: it only touches Android.bp and adds one new file. Put sanitize { never: true }, the two extra cflags and the new source in the libdng_sdk cc_library block (the one with defaults: [\"libdng_sdk-defaults\"] and -DqDNGValidate=0 in all four branches). If a fork keeps misc_undefined in a defaults block, check that never: true in the library block actually takes effect.",
       "Check that the fork builds libdng_sdk with C++ exceptions enabled, which the commit relies on. On 14/15/16 the Android.bp pre-image (1978b39) and the runtime file are identical, so those parts apply as-is to all three. The OWNERS pre-images differ per branch."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 8,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "81ded662eee07fcbb830e6b6c83d1a3d85227ceb",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 74a5a91d169a mentions bug 449728942, carried by 81ded662eee0 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0070",
     "osv_id": "ASB-A-438186009",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4a97f5239d571788ee26d94a54b7e57f23cbb0d4",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0074",
     "osv_id": "ASB-A-468061774",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Launcher3",
       "sha": "56e4dc5d2b9a070f8a4a4b1d9ea0327c3734f611",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Launcher3"
     ]
    },
    {
     "cve": "CVE-2026-0075",
     "osv_id": "ASB-A-465133716",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "819ebe4a7c68b944bcb4db30cc941982a3de5313",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0076",
     "osv_id": "ASB-A-470115162",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "881e565347a55d9f79699e1e85ac48bba66f89d6",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0077",
     "osv_id": "ASB-A-467082881",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "407d0fcb459d11b1574e1ebe6ccc297a9d00c6b6",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0078",
     "osv_id": "ASB-A-445418705",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "cf8446b51ae194e1ddc2879b9a0fdd9e5e268fb1",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0079",
     "osv_id": "ASB-A-470966318",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow checks call a library-private runtime that throws std::runtime_error instead of the ubsan_minimal runtime that aborts the process. Overflow-triggered crashes while parsing DNG data become catchable exceptions.",
      "confidence": "medium",
      "backport_notes": [
       "The patch changes only the build and adds one new source file. The target library must be compiled with C++ exceptions enabled, which the commit message asserts for libdng_sdk. ubsan_throwing_runtime.cpp must be compiled into the same shared object as the instrumented code, because the handlers have hidden visibility. On 14, 15 and 16 Google added a separate srcs block to the library; on 17 the file was appended to the existing srcs list."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 6,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "related_bug"
     ],
     "confidence": "probable",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/external/dng_sdk",
       "sha": "81ded662eee07fcbb830e6b6c83d1a3d85227ceb",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "related_bug",
       "confidence": "probable",
       "evidence": "probable: reference 74a5a91d169a mentions bug 449728942, carried by 81ded662eee0 (score 1.00)",
       "similarity": 1.0
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/dng_sdk"
     ]
    },
    {
     "cve": "CVE-2026-0085",
     "osv_id": "ASB-A-414389102",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/ContactsProvider",
       "sha": "d857ac86743339985749b158d956c63f28644817",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/providers/ContactsProvider"
     ]
    },
    {
     "cve": "CVE-2026-0086",
     "osv_id": "ASB-A-476417007",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "f0922db8cc4c1cb515c8a2a580993aa061481f65",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0087",
     "osv_id": "ASB-A-483142784",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c225d09f44e65eb473c422f593dcfaeeba213b14",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0088",
     "osv_id": "ASB-A-471127462",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/CertInstaller",
       "sha": "3dced430d0a90c017892ffeed3389c2592ff0378",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/CertInstaller"
     ]
    },
    {
     "cve": "CVE-2026-0089",
     "osv_id": "ASB-A-485397908",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c15dea2dc3bb0ebeefeb59eb74290ac9fa918bf8",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0091",
     "osv_id": "ASB-A-438742644",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a29de493b054518b02e4da0ce7ad34748c4dea76",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-0093",
     "osv_id": "ASB-A-473812391",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "9cc2911dc24d2be82d729cce2f7ea4b2e6dfe229",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0095",
     "osv_id": "ASB-A-484861632",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Bluetooth",
       "sha": "554513a005973790810445b444288c736d8a73e1",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Bluetooth"
     ]
    },
    {
     "cve": "CVE-2026-0096",
     "osv_id": "ASB-A-473005624",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "62b7a2483f4eef4ff0f02ccb81fe6cfea9819ef8",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2026-0098",
     "osv_id": "ASB-A-397217317",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/DocumentsUI",
       "sha": "a08d1c7ea084830a74b9828cc84a1a5380eaad99",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/apps/DocumentsUI"
     ]
    },
    {
     "cve": "CVE-2026-0099",
     "osv_id": "ASB-A-385917501",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Nfc",
       "sha": "cfbd02ac1630e339535cea673c3c4fa155fa668e",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/packages/modules/Nfc"
     ]
    },
    {
     "cve": "CVE-2026-0100",
     "osv_id": "ASB-A-484973621",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "50d18b36c708a7332eedecd88bc5e2cb2323b958",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28577",
     "osv_id": "ASB-A-389950114",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the WindowToken already contains a window, enforcing one toast window per toast token.",
      "confidence": "high",
      "backport_notes": [
       "The production change is a self-contained 6-line insert after the canAddToastWindowForUid check in addWindow (relies on WindowToken.isEmpty() and the WM_ERROR ProtoLog group already used by the adjacent line; check isEmpty() exists on your branch). On 15/16 Google commented out the ProtoLog call; on 14 it kept the log and added a services.core.protolog.json entry.",
       "The test needs a Session constructable with explicit pid/uid; on 14 this required adding a Session(service, callback, callingPid, callingUid) constructor and a WindowTestsBase.createTestSession helper, and the addWindow call signature differs per release (12-argument form on 14-16, WindowRelayoutResult form on 17)."
      ],
      "meta": {
       "generated": "2026-10-02",
       "author_model": "claude-fable-5-1",
       "review_model": "claude-opus-5-5",
       "review_corrections": 3,
       "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
      }
     },
     "method": [
      "bug_id"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-security-17.0.0_r1",
      "date": "2026-09-08",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-17.0.0_r1",
      "date": "2026-09-08",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "a00072f03623577eb1a25bac451c53dedd55e949",
       "tag": "android-security-17.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2026-28578",
     "osv_id": "ASB-A-475228205",
     "source": "osv",
     "announced": "2026-06-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-17.0.0_r1",
      "date": "2026-06-16",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "4914d6e98bd25e620867650e0e431df3ad23a886",
       "tag": "android-17.0.0_r1",
       "provenance": "base_release",
       "method": "base",
       "confidence": "certain",
       "evidence": "fix in the base release, inherited by the security line",
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    }
   ],
   "tags": [],
   "unannounced": [],
   "coverage": {
    "n_ingested_tags": 2,
    "first_ingested_tag": "android-security-17.0.0_r1",
    "latest_ingested_tag": "android-security-17.0.0_r2",
    "window_base_tag": "android-17.0.0_r1",
    "window_start": "2026-06-16",
    "partial_tags": []
   },
   "platform_update_tags": {},
   "briefs": {
    "CVE-2026-0052": {
     "cve": "CVE-2026-0052",
     "summary": "Rebuilds libdng_sdk in platform/external/dng_sdk so that integer-overflow sanitizer hits throw a C++ std::runtime_error, via a bundled replacement runtime (ubsan-replacement/ubsan_throwing_runtime.cpp), instead of the hard abort of the ubsan minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk (DNG image library; the commit's test is ImageDecoderTest#testInterestingDngs)",
     "weakness": "Integer overflow in DNG processing arithmetic turned into a process abort by the sanitizer runtime (reachable abort / DoS). The specific overflow sites are not shown in the material.",
     "root_cause": "libdng_sdk is built with signed-integer-overflow and unsigned-integer-overflow sanitizer checks (sanitize.misc_undefined, visible in the Android.bp context at lines 77-82). Per the commit message, the ubsan minimal runtime responds to a detected overflow with a hard abort. A DNG file whose processing trips one of these checks therefore aborts the process doing the decoding. The commit message says C++ exceptions are enabled in this library and that throwing is a much better response than a hard abort.",
     "fix_analysis": [
      "Android.bp (libdng_sdk cc_library block): adds `sanitize: { never: true }`. The in-file comment says this is done so that the ubsan_minimal runtime is disabled and a custom runtime can be injected.",
      "Android.bp (same block): re-enables the checks through cflags `-fsanitize=signed-integer-overflow,unsigned-integer-overflow` and adds `-fno-sanitize-link-runtime`, so the arithmetic is still instrumented but no sanitizer runtime is linked.",
      "Android.bp (srcs): adds `ubsan-replacement/ubsan_throwing_runtime.cpp` to the library sources.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: new file that defines extern \"C\" handlers with hidden visibility: __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one does `throw std::runtime_error(\"ubsan: <kind>-overflow\")`.",
      "Net effect: a detected overflow now raises a C++ exception instead of aborting. The material does not show where that exception is handled."
     ],
     "completeness": [
      "The fix does not change any DNG arithmetic. It only changes what happens when the instrumented signed or unsigned integer overflow checks fire in libdng_sdk code, so every instrumented site is covered in the same way.",
      "The new cflags enable only signed-integer-overflow and unsigned-integer-overflow, and the replacement runtime defines five overflow handlers. No other UBSan checks are added.",
      "The fix relies on the thrown std::runtime_error being caught somewhere. The material does not show the catch sites. If the exception is not caught, std::terminate would still end the process (general platform knowledge).",
      "libdng_sdk pulls in xmp_toolkit_sdk through whole_static_libs (Android.bp context line 107). The material does not show how that module is sanitized."
     ],
     "behavior_changes": [
      "When an overflow is detected inside libdng_sdk, a std::runtime_error is thrown instead of the process aborting. Its message is \"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\".",
      "The ubsan minimal runtime is no longer linked into libdng_sdk (sanitize.never plus -fno-sanitize-link-runtime).",
      "If the SDK or the caller catches the exception, a malformed DNG that previously aborted the process may now fail without a crash. The material does not show this handling."
     ],
     "preconditions": [
      "A crafted DNG image is processed by code that uses libdng_sdk. The commit's test (ImageDecoderTest#testInterestingDngs) points to the ImageDecoder DNG path.",
      "Processing that image must cause at least one instrumented signed or unsigned integer overflow in libdng_sdk. The specific sites are not shown.",
      "Per index search, the hits in frameworks/base/core/jni/android_hardware_camera2_DngCreator.cpp and frameworks/av/media/img_utils DngUtils are DNG writers built on TiffWriter. They do not visibly call libdng_sdk, so they are not shown to be a way to reach this code."
     ],
     "per_major": {
      "14": "same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28 (resolved by Change-Id). The sanitize/cflags change and ubsan_throwing_runtime.cpp are the same as on 17. The new file is added through a separate `srcs:` block in the cc_library hunk at line 168, and OWNERS is also changed. 3/5 lines of the patched region are present at android-security-14.0.0_r29.",
      "15": "same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (resolved by Change-Id). The Android.bp change is the same as in the 14 backport (same blob indexes), including the separate `srcs:` block, and OWNERS is also changed. 3/4 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (resolved by Change-Id). The Android.bp change is the same as in the 14/15 backports, and OWNERS is also changed. 3/5 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1. It is linked by related bug (probable, not by Change-Id). The new source is appended to the existing srcs list and OWNERS is not changed. At android-security-17.0.0_r1 the resolver finds 5/6 lines of the patched region and ubsan_throwing_runtime.cpp is present. The Android.bp context there (sanitize.misc_undefined at lines 77-82) is laid out differently from the fix diff; the material does not show why."
     },
     "backport_notes": [
      "The change touches only the build file plus one new file. It must land in the cc_library block that produces the shipped libdng_sdk: set sanitize.never, add the -fsanitize cflags and -fno-sanitize-link-runtime, and add ubsan-replacement/ubsan_throwing_runtime.cpp to srcs. Check that C++ exceptions are enabled for the library, as the commit message assumes.",
      "On the Android 17 security tag, confirm that the shipped variant still carries sanitize.never and -fno-sanitize-link-runtime. The resolver matched only 5/6 lines of the patched region there."
     ],
     "confidence": "medium",
     "unknowns": [
      "The specific overflow sites behind the linked bugs are not shown; the fix is generic.",
      "The material does not show where the thrown std::runtime_error is caught, whether inside dng_sdk or by its caller, or what result the caller sees.",
      "How xmp_toolkit_sdk (whole_static_libs) is sanitized.",
      "The Android 17 commit is linked by related bug rather than Change-Id (marked probable), and the Android.bp at android-security-17.0.0_r1 is laid out differently from the fix diff.",
      "The contents of the seven linked bugs."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0080": {
     "cve": "CVE-2026-0080",
     "summary": "Rebuilds libdng_sdk (platform/external/dng_sdk) so that UBSan integer-overflow detections throw a C++ std::runtime_error from a project-local handler runtime instead of aborting the process through the ubsan-minimal runtime.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow in DNG processing turned into a process abort by the sanitizer runtime (uncontrolled abort, DoS)",
     "root_cause": "libdng_sdk was built with UBSan signed and unsigned integer-overflow checks. The Android.bp context shows a sanitize.misc_undefined block with these two checks, and the commit title says the ubsan-minimal runtime was in use. When DNG content caused a detected integer overflow in the library, the ubsan-minimal handler aborted the hosting process. The commit message says C++ exceptions are already enabled in the library and that throwing is a better response than a hard abort. Seven bug IDs are attached. This commit does not change the arithmetic in the dng_sdk source files.",
     "fix_analysis": [
      "Android.bp, cc_library libdng_sdk: adds sanitize: { never: true } to disable the Soong-managed ubsan_minimal runtime for this module. The diff comment says Soong has no dedicated flag for this, so 'never' is combined with manual -fsanitize cflags.",
      "Android.bp, cc_library libdng_sdk cflags: keeps -DqDNGValidate=0. Adds -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which re-enables the same two checks manually, and -fno-sanitize-link-runtime, so no UBSan runtime is linked and the module itself must provide the handler symbols.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines extern \"C\", hidden-visibility __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error with a descriptive message, so a detected overflow unwinds through C++ exception handling instead of aborting.",
      "Android.bp srcs: adds ubsan-replacement/ubsan_throwing_runtime.cpp to the libdng_sdk sources. On 17 it is appended to the existing srcs list; on 14/15/16 it is added as a separate srcs block."
     ],
     "completeness": [
      "The fix changes only the failure mode: an integer overflow that libdng_sdk detects now throws instead of aborting. It does not fix any specific overflowing computation in the dng_sdk sources.",
      "Only five overflow handlers are provided (add, sub, mul, negate, divrem), matching the two checks enabled in cflags. Because -fno-sanitize-link-runtime excludes the runtime, any other UBSan check added to the cflags later would have no handler.",
      "The thrown type is std::runtime_error, not dng_exception. The material does not show whether every caller catches it. An uncaught exception, or one thrown across a noexcept frame, would still terminate the process.",
      "sanitize.never: true applies to the whole module. The material does not show whether any other sanitizer configuration existed for it."
     ],
     "behavior_changes": [
      "A detected integer overflow inside libdng_sdk now raises a C++ std::runtime_error (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of aborting the process.",
      "libdng_sdk is no longer linked against the ubsan_minimal runtime. Soong-managed sanitizers are disabled for the module (sanitize.never: true), and the two overflow checks are enabled manually through cflags."
     ],
     "preconditions": [
      "A process that links libdng_sdk must parse a DNG file (or other input libdng_sdk handles) containing values that cause a signed or unsigned integer overflow in the library's instrumented arithmetic.",
      "The commit's test (atest ImageDecoderTest#testInterestingDngs) indicates the path is reached through platform image decoding of DNG files. libdng_sdk is used by Skia's raw codec (general platform knowledge). The index search returned the frameworks/base DngCreator JNI and frameworks/av img_utils DngUtils, but those snippets are DNG writing code and do not establish a call path into libdng_sdk."
     ],
     "per_major": {
      "14": "same fix, adapted: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28 (resolved by Change-Id). Same sanitize.never/cflags change and identical ubsan_throwing_runtime.cpp. The new source is added as a separate srcs block, and OWNERS is also rewritten. 3/5 lines of the patched region present at android-security-14.0.0_r29.",
      "15": "same fix, adapted: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (Change-Id). Same content as the 14 backport (separate srcs block, OWNERS update). 3/4 lines of the patched region present at android-security-15.0.0_r17.",
      "16": "same fix, adapted: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (Change-Id). Same content as the 14/15 backports (separate srcs block, OWNERS update). 3/5 lines of the patched region present at android-security-16.0.0_r8.",
      "17": "same fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1. Linked through related bug 449728942, which commit 74a5a91d169a (the 16 fix) also mentions. ubsan_throwing_runtime.cpp is appended to the existing srcs list, with no OWNERS change. 5/6 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The fix is build configuration plus one new source file. It relies on C++ exceptions being enabled for libdng_sdk (the commit message says they are), and on sanitize.never: true together with -fno-sanitize-link-runtime so that ubsan_minimal is not linked. Check where your fork's Android.bp sets the misc_undefined overflow checks for this module."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic sites in dng_sdk overflow. The seven referenced bugs are not described in the material.",
      "Where the new std::runtime_error is caught. The material does not show catch sites in libdng_sdk or its consumers.",
      "The Android.bp excerpt shown for android-security-17.0.0_r1 has an inline sanitize.misc_undefined block and stl: c++_shared, and does not show the sanitize.never or ubsan-replacement lines. The exact file state at that tag is unclear from the excerpt.",
      "Why the resolver reports only partial presence of the patched region (3/5, 3/4, 3/5) at the latest 14/15/16 security tags despite the Change-Id matches.",
      "Whether logging on overflow changes, and how the decode failure shows up to apps."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 4,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48581": {
     "cve": "CVE-2025-48581",
     "summary": "Adds sub_dir: \"sysconfig\" to the apexd.mainline_patch_level_2 prebuilt_etc module in platform/system/apex so that com.google.android.mainline.patchlevel.2.xml is installed under etc/sysconfig instead of directly under etc, making the feature declaration it carries accessible to the device.",
     "component": "platform/system/apex, patchlevel (mainline patch level feature declaration, build rule)",
     "weakness": "Misconfiguration: build rule installed a feature/sysconfig XML at the wrong path, so the declared feature was not accessible to the device (exact flaw class not evident from the diff)",
     "root_cause": "patchlevel/Android.bp defined prebuilt_etc \"apexd.mainline_patch_level_2\" with src com.google.android.mainline.patchlevel.2.xml but no sub_dir, so the XML landed in the default etc directory rather than etc/sysconfig. Per the commit message, this made the feature declared in the XML not \"accessible by the device\". prebuilt_etc installs to <partition>/etc by default and sub_dir selects a subdirectory under it; the system reads feature declarations from etc/sysconfig and etc/permissions (general platform knowledge). The security consequence of the missing feature declaration (rated High EoP by the bulletin) is not shown in the material.",
     "fix_analysis": [
      "patchlevel/Android.bp, module apexd.mainline_patch_level_2: one line added, sub_dir: \"sysconfig\", so the prebuilt XML is installed into etc/sysconfig/com.google.android.mainline.patchlevel.2.xml (filename_from_src keeps the source filename). No code or XML content change; this is purely an install path correction.",
      "Single-commit fix; the same one-line change is carried as 117f34be6322 on Android 16 and 8b89163d61b8 on Android 17."
     ],
     "completeness": [
      "The fix covers only the install location of the one prebuilt XML in patchlevel/Android.bp; it does not change the XML content or any consumer of it.",
      "Whether other mainline patch level XMLs or similar prebuilt_etc modules exist with the same missing sub_dir is not shown in the material.",
      "The material does not show the device-side consumer, so it cannot be judged from the code whether etc/sysconfig is the only location that would make the feature effective."
     ],
     "behavior_changes": [
      "com.google.android.mainline.patchlevel.2.xml moves from <partition>/etc/ to <partition>/etc/sysconfig/ in built images.",
      "Per the commit message, the feature declared in that XML becomes accessible to the device; how it is then exposed or reported is not shown in the material."
     ],
     "preconditions": [
      "Device build includes the apexd.mainline_patch_level_2 module from platform/system/apex (Android 16 and later; patchlevel/Android.bp is absent at the latest Android 14 and 15 security tags).",
      "Any component relying on the feature declared in com.google.android.mainline.patchlevel.2.xml being present; the specific dependent checks are not shown in the material."
     ],
     "per_major": {
      "14": "not affected per bulletin; patchlevel/Android.bp absent at android-security-14.0.0_r29 (code absent)",
      "15": "not affected per bulletin; patchlevel/Android.bp absent at android-security-15.0.0_r17 (code absent)",
      "16": "same fix: platform/system/apex 117f34be6322 in android-security-16.0.0_r7 (matched by Change-Id); 3/3 patched lines present at android-security-16.0.0_r8",
      "17": "same fix: platform/system/apex 8b89163d61b8 in android-17.0.0_r1 (linked via bug 436433817, probable match); 3/3 patched lines present at android-security-17.0.0_r1"
     },
     "backport_notes": [
      "Only relevant to trees containing system/apex/patchlevel/Android.bp (Android 16+). Apply the single sub_dir line; verify the resulting image has etc/sysconfig/com.google.android.mainline.patchlevel.2.xml and that no device-specific overlay installs a conflicting copy at the old etc/ path."
     ],
     "confidence": "medium",
     "unknowns": [
      "The contents of com.google.android.mainline.patchlevel.2.xml and which feature it declares are not in the material.",
      "Which system component consumes the feature and how its absence leads to the High EoP rated by the bulletin is not shown.",
      "How the feature is exposed or reported once accessible is not shown.",
      "Whether the Android 17 commit 8b89163d61b8 is the exact fix is inferred from the shared bug number (probable match) rather than a Change-Id match."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 2,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2025-48652": {
     "cve": "CVE-2025-48652",
     "summary": "PackageInstaller now skips the unknown-sources user restriction check only when the installer holds INSTALL_PACKAGES and did not start the install via an ACTION_VIEW/ACTION_INSTALL_PACKAGE intent, or when it is a privileged app that set EXTRA_NOT_UNKNOWN_SOURCE; previously any INSTALL_PACKAGES holder bypassed the check regardless of intent action.",
     "component": "platform/frameworks/base, packages/PackageInstaller (InstallStart activity and v2 InstallRepository)",
     "weakness": "Incorrect authorization: device-policy user restriction check bypassed by an over-broad trusted-source condition",
     "root_cause": "In InstallStart (InstallStart.java, activity start-up path) and InstallRepository (InstallRepository.kt), the device-policy restriction check was gated on isTrustedSource = isPrivilegedAndKnown || isInstallPkgPermissionGranted. When isTrustedSource was true, checkDevicePolicyRestrictions / getDevicePolicyRestrictions only evaluated UserManager.DISALLOW_INSTALL_APPS and skipped the unknown-sources restrictions in the else branch. Because isInstallPkgPermissionGranted alone was enough, an installer holding INSTALL_PACKAGES that launched PackageInstaller through an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent was not subject to the unknown-sources user restrictions.",
     "fix_analysis": [
      "InstallStart.java (activity start-up path): computes isIntentInstall = (intentAction is ACTION_VIEW or ACTION_INSTALL_PACKAGE) and bypassUnknownSourceRestrictions = (!isIntentInstall && isInstallPkgPermissionGranted) || isPrivilegedAndKnown, and passes that instead of isTrustedSource to checkDevicePolicyRestrictions. The INSTALL_PACKAGES shortcut now applies only to non-intent (PackageInstaller API / session) flows; intent flows need the privileged app plus EXTRA_NOT_UNKNOWN_SOURCE path.",
      "InstallStart.java:checkDevicePolicyRestrictions: parameter renamed to bypassUnknownSourceRestrictions; semantics unchanged (true -> only DISALLOW_INSTALL_APPS checked, false -> the full list including unknown-sources restrictions).",
      "InstallRepository.kt (v2 flow): isInstallRequestFromTrustedSource is removed and inlined; isPrivilegedAndKnown and isInstallPkgPermissionGranted become locals (on 17 evaluated against originatingUid, guarded by originatingUid != Process.INVALID_UID). isTrustedSource is still isPrivilegedAndKnown || isInstallPkgPermissionGranted for other uses, but getDevicePolicyRestrictions is now called with the same bypassUnknownSourceRestrictions expression as InstallStart.",
      "InstallRepository.kt:getDevicePolicyRestrictions: parameter renamed to bypassUnknownSourceRestrictions; body unchanged.",
      "The commit is a Reapply (it reverts the revert 29962260bd04 of the original change) with fixes; the original change and the revert are not in the material."
     ],
     "completeness": [
      "On 17 (and 16) both PackageInstaller entry flows are fixed: the legacy InstallStart activity and the v2 InstallRepository model.",
      "isIntentInstall covers only ACTION_VIEW and ACTION_INSTALL_PACKAGE; any other action (session confirm actions, and on 17 ACTION_CONFIRM_DEVELOPER_VERIFICATION) is treated as a non-intent install and still bypasses the unknown-sources restriction when the originating UID holds INSTALL_PACKAGES. This matches the stated design (PackageInstaller API installs are exempt).",
      "Privileged apps that set EXTRA_NOT_UNKNOWN_SOURCE on an intent install still bypass the unknown-sources restriction (isPrivilegedAndKnown), by design.",
      "The check remains a PackageInstaller (UI) side check; the commit changes nothing in system_server or PackageManagerService.",
      "On 17, isInstallPkgPermissionGranted is evaluated on originatingUid. When the caller is the documents manager or the system downloads provider, originatingUid is taken from Intent.EXTRA_ORIGINATING_UID (per the surrounding code)."
     ],
     "behavior_changes": [
      "Installers holding INSTALL_PACKAGES that start PackageInstaller via ACTION_VIEW or ACTION_INSTALL_PACKAGE are now checked against the unknown-sources restrictions in the else list. When one applies, PackageInstaller creates the admin support intent via devicePolicyManager.createAdminSupportIntent(restriction).",
      "Session-based and other non-intent installs by INSTALL_PACKAGES holders keep the previous behaviour (only DISALLOW_INSTALL_APPS checked).",
      "On Android 16 InstallRepository.kt, isInstallPermissionGrantedOrRequested is now only evaluated when callingUid != Process.INVALID_UID (new guard added by the backport)."
     ],
     "preconditions": [
      "An unknown-sources user restriction is set for the user. The CTS tests named in the commit are the MixedProfileOwnerTest / MixedManagedProfileOwnerTest testPackageInstallUserRestrictions cases.",
      "The caller (or the originating UID derived from EXTRA_ORIGINATING_UID) holds android.permission.INSTALL_PACKAGES and launches PackageInstaller with an ACTION_VIEW or ACTION_INSTALL_PACKAGE intent rather than a PackageInstaller session.",
      "The code is reached in InstallStart (legacy flow) or in InstallRepository's intent-processing path (v2 flow). Per index search, no callers outside PackageInstaller were found."
     ],
     "per_major": {
      "14": "not affected per the bulletin; at android-security-14.0.0_r29 only 3/8 lines of the patched region are present, so the code differs and the material shows no fix for 14.",
      "15": "adapted fix: 0985478406c2 (android-security-15.0.0_r13) touches only InstallStart.java. It adds String intentAction, the bypassUnknownSourceRestrictions logic and the renamed checkDevicePolicyRestrictions parameter; that commit does not change InstallRepository.kt. 8/8 lines present at android-security-15.0.0_r17.",
      "16": "adapted fix: 4dc009cd724c (android-security-16.0.0_r4) patches InstallStart.java as on 15. It also patches InstallRepository.kt, keying the INSTALL_PACKAGES check on callingUid (not originatingUid) and adding a new callingUid != INVALID_UID guard before isInstallPermissionGrantedOrRequested. 8/8 lines present at android-security-16.0.0_r8.",
      "17": "same fix: 81f83fdb0944 in android-17.0.0_r1 (linked by related_bug, probable match score 0.82); its InstallRepository.kt change uses originatingUid. 7/8 lines of the patched region present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "This is a Reapply of a previously reverted change. In InstallStart.java the locals isPrivilegedAndKnown, isInstallPkgPermissionGranted and intentAction must be in scope at the checkDevicePolicyRestrictions call (the 15/16 backports add String intentAction).",
      "The InstallRepository.kt hunk differs by branch: on 16 the INSTALL_PACKAGES check uses callingUid and isInstallPermissionGrantedOrRequested gets a new guard; on 17 the check uses originatingUid. The 15 fix did not change InstallRepository.kt at all, so check whether the v2 flow exists and is used on the target branch."
     ],
     "confidence": "medium",
     "unknowns": [
      "Contents of the original change and its revert 29962260bd04, and what 'fix the issues' refers to.",
      "The full else-branch restriction list in checkDevicePolicyRestrictions / getDevicePolicyRestrictions is truncated in the diff (only DISALLOW_INSTALL_APPS for the bypass branch is visible), so the exact unknown-sources restriction constants are not shown.",
      "What happens after the admin support intent is created (the rest of that path is not in the diff).",
      "Why only 7/8 lines are present at android-security-17.0.0_r1, and whether Android 15 has or uses the v2 InstallRepository flow.",
      "The 17 commit linkage is probable (related_bug), not confirmed by change_id."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 5,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0067": {
     "cve": "CVE-2026-0067",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow detections throw a C++ std::runtime_error, using a hidden-visibility replacement runtime (ubsan-replacement/ubsan_throwing_runtime.cpp), instead of going through the ubsan-minimal runtime, which aborts the process.",
     "component": "platform/external/dng_sdk, libdng_sdk build configuration (Android.bp) and sanitizer runtime",
     "weakness": "Integer overflow (signed/unsigned) in DNG processing that sanitizer instrumentation detects and turns into a hard process abort (DoS). The individual overflow sites are not shown in the material.",
     "root_cause": "libdng_sdk was built with Soong sanitize { misc_undefined: [\"unsigned-integer-overflow\", \"signed-integer-overflow\"] } (visible in the Android.bp context). Per the commit comment, this uses the ubsan_minimal runtime, and the commit message describes the result as a hard abort. So an arithmetic overflow reached while dng_sdk processes DNG data ended the process instead of becoming a recoverable error. The commit lists seven bugs (470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942). Their content is not shown, and the specific arithmetic expressions are not in the diff.",
     "fix_analysis": [
      "Android.bp (cc_library libdng_sdk, the block with defaults: [\"libdng_sdk-defaults\"]): adds sanitize { never: true }. Per the commit comment, this disables the ubsan_minimal runtime so a custom one can be injected.",
      "Android.bp (same block): turns the checks back on through cflags \"-fsanitize=signed-integer-overflow,unsigned-integer-overflow\" and \"-fno-sanitize-link-runtime\", so the code is still instrumented but no sanitizer runtime is linked. The existing \"-DqDNGValidate=0\" is kept.",
      "Android.bp (srcs): adds ubsan-replacement/ubsan_throwing_runtime.cpp. Android 17 appends it to the existing srcs list. Android 14/15/16 add a new srcs: block.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp: defines extern \"C\" hidden-visibility handlers __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each one throws std::runtime_error(\"ubsan: <kind>\"). The commit message says C++ exceptions are enabled in this library, so a detected overflow now raises an exception instead of aborting.",
      "The Android 14/15/16 commits also update OWNERS (bug component 1075130, include of the frameworks/base graphics OWNERS). This change has no functional effect."
     ],
     "completeness": [
      "The fix defines five handlers (add, sub, mul, negate, divrem) for the two enabled checks, signed and unsigned integer overflow. These are expected to be the entry points those checks emit, but the material does not show the compiler's handler mapping.",
      "Only the non-abort handler names are defined. The _abort and _minimal variants are not. The material does not show whether any build setting could make the compiler emit those names.",
      "The fix does not change any dng_sdk arithmetic. The overflows still happen and are still detected; they are only converted into exceptions. The material does not show whether every instrumented site is reached under a try/catch in dng_sdk or its consumers.",
      "The replacement handlers take no parameters and ignore whatever the compiler passes them, so no diagnostic detail is kept.",
      "sanitize: never: true also removes any other sanitizer Soong would have applied to this module (general platform knowledge). The material does not show what, if anything, was applied beyond misc_undefined.",
      "The Android.bp excerpt at android-security-17.0.0_r1 (lines 75-134) shows a block with sanitize misc_undefined and stl: c++_shared after sdk_version. That is not the block the 17 commit patched (sdk_version followed by defaults: [\"libdng_sdk-defaults\"] at line 105). The excerpt does not show the never / -fno-sanitize-link-runtime lines. The runtime file is present at that tag."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error (\"ubsan: add-overflow\", \"ubsan: sub-overflow\", \"ubsan: mul-overflow\", \"ubsan: negate-overflow\" or \"ubsan: divrem-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the ubsan-minimal runtime. The overflow handlers are built into the library with hidden visibility.",
      "Soong-managed sanitizers are turned off for this module (sanitize never) and replaced by explicit -fsanitize cflags."
     ],
     "preconditions": [
      "libdng_sdk must process attacker-controlled DNG data. The commit's test (atest ImageDecoderTest#testInterestingDngs) points to DNG decoding through ImageDecoder as the exercised path.",
      "The input must cause a signed or unsigned integer overflow at an instrumented site. The specific sites are not shown.",
      "Per index search, frameworks/base android_hardware_camera2_DngCreator.cpp and frameworks/av img_utils DngUtils are DNG-related code. The material does not show them linking libdng_sdk, so their relevance is unconfirmed."
     ],
     "per_major": {
      "14": "Same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28 (resolved by change_id). The Android.bp hunk at line 168 (1978b39..3d95a7b) adds sanitize never, the -fsanitize cflags, -fno-sanitize-link-runtime and a new srcs: block. The runtime file is identical, and OWNERS is also updated. At android-security-14.0.0_r29, 3/5 lines of the patched region are present.",
      "15": "Same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16 (resolved by change_id). The Android.bp and runtime-file hunks are identical to 14 (same blob hashes), and OWNERS is updated. At android-security-15.0.0_r17, 3/4 lines of the patched region are present.",
      "16": "Same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7 (resolved by change_id). The Android.bp and runtime-file hunks are identical to 14/15, and OWNERS is updated. At android-security-16.0.0_r8, 3/5 lines of the patched region are present.",
      "17": "Adapted fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1, linked through related_bug (probable, via bug 449728942). The same sanitize/cflags change is made at Android.bp line 105, and the runtime file is appended to the existing srcs list instead of a separate srcs: block. There is no OWNERS change. At android-security-17.0.0_r1, 5/6 lines of the patched region are present and the runtime file exists."
     },
     "backport_notes": [
      "Single self-contained commit: it only touches Android.bp and adds one new file. Put sanitize { never: true }, the two extra cflags and the new source in the libdng_sdk cc_library block (the one with defaults: [\"libdng_sdk-defaults\"] and -DqDNGValidate=0 in all four branches). If a fork keeps misc_undefined in a defaults block, check that never: true in the library block actually takes effect.",
      "Check that the fork builds libdng_sdk with C++ exceptions enabled, which the commit relies on. On 14/15/16 the Android.bp pre-image (1978b39) and the runtime file are identical, so those parts apply as-is to all three. The OWNERS pre-images differ per branch."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic expressions in dng_sdk overflow for the referenced bugs, and which DNG fields trigger them.",
      "Whether every instrumented site is reached under a try/catch, so the thrown std::runtime_error is caught and does not lead to std::terminate.",
      "Whether the full Android.bp at android-security-17.0.0_r1 contains the sanitize never / -fno-sanitize-link-runtime configuration.",
      "Why only part of the patched region is reported present at the latest 14/15/16 security tags (3/5, 3/4, 3/5).",
      "Whether any build setting makes the compiler emit _abort or _minimal handler variants that the replacement runtime does not define.",
      "What the seven bugs listed in the commit describe."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 8,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-0079": {
     "cve": "CVE-2026-0079",
     "summary": "Rebuilds libdng_sdk so that UBSan integer-overflow checks call a library-private runtime that throws std::runtime_error instead of the ubsan_minimal runtime that aborts the process. Overflow-triggered crashes while parsing DNG data become catchable exceptions.",
     "component": "platform/external/dng_sdk, libdng_sdk build (Android.bp) and new ubsan-replacement runtime",
     "weakness": "Integer overflow (signed/unsigned) in DNG processing that UBSan detects and turns into a hard abort. The fix changes the failure mode, not any individual arithmetic site.",
     "root_cause": "At android-security-17.0.0_r1, Android.bp has a cc_library block whose sanitize.misc_undefined lists \"unsigned-integer-overflow\" and \"signed-integer-overflow\" (lines 77-82). The material does not show how libdng_sdk picks this up. With UBSan enabled through Soong, the ubsan_minimal runtime is used and a detected overflow aborts the hosting process (general platform knowledge). The commit message describes the previous behaviour as a hard abort. Integer overflows reachable from DNG input therefore killed the decoding process. The commit lists seven bugs: 470966846, 467994860, 467994310, 461790658, 456471487, 453649377, 449728942. The bulletin rates the issue as a High severity DoS. The commit message states that C++ exceptions are already enabled in the library and are a better response than aborting.",
     "fix_analysis": [
      "Android.bp, cc_library (the libdng_sdk target carrying -DqDNGValidate=0): adds sanitize: { never: true } so Soong stops applying its own sanitizer setup and stops linking the ubsan_minimal runtime for this module. The added comment says Soong has no dedicated flag for swapping the runtime, so 'never' plus manual cflags is used.",
      "Android.bp, same cc_library: cflags now include -fsanitize=signed-integer-overflow,unsigned-integer-overflow, which re-enables the same two checks, and -fno-sanitize-link-runtime, so no compiler-provided ubsan runtime is linked.",
      "ubsan-replacement/ubsan_throwing_runtime.cpp (new): defines five extern \"C\" hidden-visibility handlers: __ubsan_handle_add_overflow, __ubsan_handle_sub_overflow, __ubsan_handle_mul_overflow, __ubsan_handle_negate_overflow and __ubsan_handle_divrem_overflow. Each throws std::runtime_error with a message naming the check. Because the symbols are hidden, they only serve the instrumented code inside libdng_sdk itself.",
      "Android.bp: the new file is added to the library's srcs. On 17 it is appended to the existing srcs list; on 14, 15 and 16 it is added as a new srcs block. The handlers are therefore compiled into libdng_sdk and resolve the calls the compiler emits.",
      "Net effect: when instrumented arithmetic in libdng_sdk overflows, control enters a throwing handler and unwinds through C++ exceptions instead of aborting. The overflowing computation itself is not corrected; it is simply never completed."
     ],
     "completeness": [
      "Covers every signed and unsigned integer overflow site in code compiled into this cc_library, whichever specific overflow each of the seven linked bugs reported. No individual arithmetic site is changed.",
      "Only the five non-abort handler entry points (add, sub, mul, negate, divrem) are provided. This matches the two checks enabled by the new -fsanitize flag.",
      "The thrown type is std::runtime_error, not the SDK's dng_exception. The material does not show whether the SDK's internal catch sites and the Java/JNI decode callers catch it. The commit's test is ImageDecoderTest#testInterestingDngs. A throw from inside a noexcept function or a destructor would still terminate the process (general platform knowledge).",
      "xmp_toolkit_sdk is pulled in through whole_static_libs in the shown block. This diff does not touch its sanitizer setup, and the material does not show it."
     ],
     "behavior_changes": [
      "An integer overflow detected inside libdng_sdk now throws std::runtime_error(\"ubsan: <check>-overflow\") instead of aborting the process.",
      "libdng_sdk no longer links the ubsan_minimal runtime. Soong sanitizer handling for the module is disabled (sanitize.never), and the two overflow checks are re-enabled by hand through cflags.",
      "DNG inputs that previously crashed the decoding process should now produce a decode failure, provided an enclosing handler catches the exception."
     ],
     "preconditions": [
      "A DNG/TIFF-style input processed by libdng_sdk whose field values push an arithmetic expression into signed or unsigned overflow. The material does not show the exact sites.",
      "Reachable through the platform image decoding path, as the commit's test ImageDecoderTest#testInterestingDngs indicates. The index search returned the frameworks/base DngCreator JNI and frameworks/av img_utils DngUtils. These are DNG writers built on TiffWriter and do not appear to call libdng_sdk (per index search, relevance uncertain)."
     ],
     "per_major": {
      "14": "Same fix: platform/external/dng_sdk 42e65547eb33 in android-security-14.0.0_r28, matched by Change-Id. The Android.bp hunk is identical to 15 and 16 (1978b39..3d95a7b): sanitize.never, the new cflags, and a separate srcs block for ubsan-replacement/ubsan_throwing_runtime.cpp. The commit also updates OWNERS. The resolver reports 3/5 patched lines present at android-security-14.0.0_r29.",
      "15": "Same fix: platform/external/dng_sdk 1ec41401b3d4 in android-security-15.0.0_r16, matched by Change-Id. The Android.bp hunk and the new runtime file are identical to 14 and 16. The commit also updates OWNERS, from a different base blob. The resolver reports 3/4 patched lines present at android-security-15.0.0_r17.",
      "16": "Same fix: platform/external/dng_sdk 74a5a91d169a in android-security-16.0.0_r7, matched by Change-Id. The Android.bp hunk and the new runtime file are identical to 14 and 15. The commit also updates OWNERS, from a different base blob. The resolver reports 3/5 patched lines present at android-security-16.0.0_r8.",
      "17": "Adapted fix: platform/external/dng_sdk 81ded662eee0 in android-17.0.0_r1. It is linked by related bug: 74a5a91d169a mentions bug 449728942, which 81ded662eee0 also carries (probable, score 1.00). It has the same sanitize/cflags change and the same new runtime file, but the file is appended to the library's existing srcs list (Android.bp base 97ce90a) and there is no OWNERS change. The resolver reports 5/6 patched lines present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "The patch changes only the build and adds one new source file. The target library must be compiled with C++ exceptions enabled, which the commit message asserts for libdng_sdk. ubsan_throwing_runtime.cpp must be compiled into the same shared object as the instrumented code, because the handlers have hidden visibility. On 14, 15 and 16 Google added a separate srcs block to the library; on 17 the file was appended to the existing srcs list."
     ],
     "confidence": "medium",
     "unknowns": [
      "Which arithmetic expressions in dng_sdk overflow for the seven linked bugs. The diff changes no SDK source.",
      "Whether the SDK's internal exception handling and the ImageDecoder/JNI callers catch std::runtime_error, so that the throw ends in a clean decode failure rather than std::terminate.",
      "Whether setting sanitize.never drops any sanitizers that Soong applied to libdng_sdk by default, other than the two overflow checks that are re-enabled through cflags.",
      "Whether the sanitize block shown at Android.bp lines 77-82 is libdng_sdk-defaults and how libdng_sdk inherits it.",
      "Whether the 17 commit is the exact fix for this CVE. It is linked by related bug (probable), not by Change-Id.",
      "Why the resolver reports only partial presence of the patched region at the latest security tag for each major while also reporting the fix as resolved."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 6,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    },
    "CVE-2026-28577": {
     "cve": "CVE-2026-28577",
     "summary": "WindowManagerService.addWindow now rejects a TYPE_TOAST window with ADD_BAD_APP_TOKEN when addToastWindowRequiresToken is set and the WindowToken already contains a window, enforcing one toast window per toast token.",
     "component": "platform/frameworks/base, WindowManager (services/core/java/com/android/server/wm/WindowManagerService.java)",
     "weakness": "Missing validation (insufficient check on window token reuse; in the TYPE_TOAST block the one-toast constraint was only enforced per UID)",
     "root_cause": "In WindowManagerService.addWindow, the TYPE_TOAST block only enforced displayContent.canAddToastWindowForUid(callingUid), i.e. a per-UID limit on simultaneous toast windows; within that block nothing checked whether the WindowToken already held a window. As the added test shows, a second caller with a different UID could attach another TYPE_TOAST window to an existing toast token, passing the per-UID check and violating the invariant that a TYPE_TOAST WindowToken contains only one toast window.",
     "fix_analysis": [
      "WindowManagerService.java:addWindow (TYPE_TOAST block, immediately after the canAddToastWindowForUid check): adds `if (addToastWindowRequiresToken && !token.isEmpty())` which logs a WM_ERROR ProtoLog \"Adding toast window with non-empty token.\" and returns WindowManagerGlobal.ADD_BAD_APP_TOKEN, before the hide-timeout scheduling and the rest of addWindow.",
      "WindowManagerServiceTests.java: new testAddToastWindow_singleWindowPerToken adds a TYPE_TOAST token via mWm.addWindowToken, adds one toast window from uid 1234 (targetSdk O, expecting at least ADD_OKAY), then attempts a second toast window on the same token from uid 1235 and asserts ADD_BAD_APP_TOKEN."
     ],
     "completeness": [
      "Covers only the TYPE_TOAST block of WindowManagerService.addWindow; the check applies to the token used for the toast window being added.",
      "The new check is gated on addToastWindowRequiresToken; when that is false (older apps, per the surrounding comment about apps that can add toasts without a token) the token-emptiness check does not run and only the pre-existing per-UID canAddToastWindowForUid check applies.",
      "Ordering: canAddToastWindowForUid still runs first, so a same-UID duplicate continues to return ADD_DUPLICATE_ADD; the new ADD_BAD_APP_TOKEN path is reached only when the per-UID check passes.",
      "The fix does not change how toast tokens are created (addWindowToken) or who can reference an existing toast token; it only blocks attaching to a non-empty one."
     ],
     "behavior_changes": [
      "addWindow returns WindowManagerGlobal.ADD_BAD_APP_TOKEN for a TYPE_TOAST request whose token already contains a window when addToastWindowRequiresToken is true.",
      "New WM_ERROR ProtoLog message \"Adding toast window with non-empty token.\" on Android 17 and 14 (14 adds a services.core.protolog.json entry); on the 15 and 16 backports the ProtoLog line is commented out, so the rejection is not logged."
     ],
     "preconditions": [
      "Caller reaches WindowManagerService.addWindow with TYPE_TOAST LayoutParams and passes earlier checks including displayPolicy.validateAddingWindowLw and displayContent.canAddToastWindowForUid (in the test, satisfied by using a different UID than the existing toast owner).",
      "addToastWindowRequiresToken is true for the caller (the test uses targetSdkVersion O).",
      "The token refers to an existing TYPE_TOAST WindowToken that already contains a window; how a second process obtains such a token is not shown in the material."
     ],
     "per_major": {
      "14": "adapted fix (android-security-14.0.0_r28, 2f08e7d895e3): same 6-line check with active ProtoLog, plus a data/etc/services.core.protolog.json entry for the new message, a @VisibleForTesting Session(service, callback, callingPid, callingUid) constructor in Session.java, a createTestSession helper in WindowTestsBase.java (noted as a partial backport omitting process creation), and the test using the older 12-argument addWindow signature. 8/8 patched lines present at r29.",
      "15": "adapted fix (android-security-15.0.0_r16, 9af61140db79): same check but the ProtoLog.w line is commented out; test added with the older addWindow signature. 8/8 patched lines present at r17.",
      "16": "adapted fix (android-security-16.0.0_r7, d9ad94e69f4e): same check with ProtoLog.w commented out; test added with the older addWindow signature. 8/8 patched lines present at r8.",
      "17": "same fix (android-security-17.0.0_r1, a00072f03623, cherry-pick of 352e98e9ed8a): check with active ProtoLog and test using the WindowRelayoutResult addWindow signature. 8/8 patched lines present."
     },
     "backport_notes": [
      "The production change is a self-contained 6-line insert after the canAddToastWindowForUid check in addWindow (relies on WindowToken.isEmpty() and the WM_ERROR ProtoLog group already used by the adjacent line; check isEmpty() exists on your branch). On 15/16 Google commented out the ProtoLog call; on 14 it kept the log and added a services.core.protolog.json entry.",
      "The test needs a Session constructable with explicit pid/uid; on 14 this required adding a Session(service, callback, callingPid, callingUid) constructor and a WindowTestsBase.createTestSession helper, and the addWindow call signature differs per release (12-argument form on 14-16, WindowRelayoutResult form on 17)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how an unprivileged caller obtains a reference to another app's toast WindowToken, nor the concrete privilege escalation consequence beyond the bulletin's High/EoP rating.",
      "The exact condition under which addToastWindowRequiresToken is computed (its SDK threshold) is not in the provided context, only implied by the surrounding comment and the test's use of targetSdk O.",
      "How the `token` variable is resolved from the LayoutParams in addWindow is not shown in the code context.",
      "Why the ProtoLog line was commented out on 15/16 is not stated in the commits."
     ],
     "meta": {
      "generated": "2026-10-02",
      "author_model": "claude-fable-5-1",
      "review_model": "claude-opus-5-5",
      "review_corrections": 3,
      "note": "AI-generated from the bulletin, the fixing diffs and their code context, then checked claim by claim by a second model. Statements marked '(general platform knowledge)' are not taken from the material."
     }
    }
   }
  }
 },
 "bulletin": {
  "url": "https://source.android.com/docs/security/bulletin/2026/2026-06-01",
  "latest_version": "1.4",
  "latest_revision": {
   "version": "1.4",
   "date": "October 12, 2026",
   "notes": "Updated issue list"
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "June 1, 2026",
    "notes": "Bulletin Published"
   },
   {
    "version": "1.1",
    "date": "June 3, 2026",
    "notes": "Updated issue list and added AOSP links"
   },
   {
    "version": "1.2",
    "date": "August 25, 2026",
    "notes": "Updated issue list"
   },
   {
    "version": "1.3",
    "date": "September 2, 2026",
    "notes": "Updated issue list"
   },
   {
    "version": "1.4",
    "date": "October 12, 2026",
    "notes": "Updated issue list"
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [
   {
    "seen": "2026-10-13",
    "kind": "removed",
    "cve": "CVE-2026-0069",
    "bug_ids": [
     "480126173"
    ],
    "bulletin_version": "1.4"
   },
   {
    "seen": "2026-10-13",
    "kind": "removed",
    "cve": "CVE-2026-0094",
    "bug_ids": [
     "471173239"
    ],
    "bulletin_version": "1.4"
   }
  ],
  "out_of_scope_rows": {
   "Imagination Technologies": 3,
   "Kernel": 1,
   "MediaTek components": 11,
   "Qualcomm closed-source components": 19,
   "Qualcomm components": 2,
   "Unisoc components": 16
  },
  "note": "changes are only visible from `tracked_since`; earlier revisions of the bulletin cannot be reconstructed. Out-of-scope rows (vendor closed-source, kernel) have no public AOSP code to resolve."
 }
}