{
 "month": "2023-10",
 "generated": "2026-10-06T12:05+00:00",
 "majors": {
  "14": {
   "counts": {
    "announced": 11,
    "certain": 11,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [
    {
     "cve": "CVE-2023-4863",
     "osv_id": "ASB-A-299477569",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "Critical",
     "types": [
      "RCE"
     ],
     "status": "resolved",
     "brief": {
      "summary": "The fixed-size HuffmanCode buffers in the VP8L (lossless WebP) decoder are replaced with a chained HuffmanTables segment structure. VP8LBuildHuffmanTable now does a dry run of BuildHuffmanTable to compute the exact table size and allocates a new segment when the current one is too small, so Huffman tables are no longer written past the allocated buffer.",
      "confidence": "high",
      "backport_notes": [
       "The change alters the VP8LBuildHuffmanTable signature and the type of VP8LMetadata.huffman_tables_. All users must be converted together: ReadHuffmanCodeLengths, ReadHuffmanCode/ReadHuffmanCodes, ClearMetadata and VP8LDecodeImage. The new code asserts that hdr->huffman_tables_ was zero-initialized by InitMetadata.",
       "README.android also lists 'merge d64e6d7d Merge \"PaletteSortModifiedZeng: fix leak on error\" into main'. The material does not show whether that merge is a prerequisite for this fix."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/external/webp",
       "sha": "9eac4f723bf7a043b6783923cf5bf490bf6d4d7c",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/external/webp"
     ]
    },
    {
     "cve": "CVE-2023-21244",
     "osv_id": "ASB-A-276729064",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0307b18a1af586d6ba531b8b5d7ff53dd59c5d6b",
       "tag": "android-14.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-2023-21252",
     "osv_id": "ASB-A-275339978",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds an isWapi flag to WifiConfigurationUtil.validatePassword. A WAPI-PSK hex preSharedKey is then capped at 100 characters instead of going through the generic PSK_SAE_HEX_LEN rule. Per the commit message, the goal is to stop arbitrarily large passwords.",
      "confidence": "medium",
      "backport_notes": [
       "This is a single self-contained commit that touches only the private validatePassword and its call sites in WifiConfigurationUtil.java. A backport needs the SECURITY_TYPE_WAPI_PSK validate call to exist in the target, and the matching WifiConfigurationUtilTest change should be taken as well. Wifi is a mainline module, so the fix may also arrive through a module update rather than a platform tag (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/modules/Wifi",
       "sha": "1d72a8130807ba747b541007f63d38e8040cb017",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/modules/Wifi",
       "sha": "9051eef78a76550f8a59c558f37b41bf8e9be688",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/modules/Wifi"
     ]
    },
    {
     "cve": "CVE-2023-21253",
     "osv_id": "ASB-A-266580022",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "DoS"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "6f6ee8a55f37c2b8c0df041b2bd53ec928764597",
       "tag": "android-14.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-2023-21291",
     "osv_id": "ASB-A-277593270",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "method": [
      "base"
     ],
     "confidence": "certain",
     "first_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "last_fix": {
      "tag": "android-14.0.0_r1",
      "date": "2023-10-04",
      "provenance": "base_release"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "2f66b7da2ad5a019518ae7146178fa59a08c0936",
       "tag": "android-14.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-2023-40120",
     "osv_id": "ASB-A-274775190",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "SystemUI's MediaDataManager now treats blank (not only null) media titles as missing, falls through the existing fallbacks, and finally substitutes a new placeholder string \"<app name> is running\" when no title source is non-blank.",
      "confidence": "medium",
      "backport_notes": [
       "Single self-contained commit. Add the string resource together with the Kotlin change, since the code references R.string.controls_media_empty_title. The logic change is in the title fallback chain. The file path differs in newer trees (per index search, media/controls/domain/pipeline), so apply the patch by matching code content, not file path.",
       "The new tests compare against context.getString(R.string.controls_media_empty_title, APP_NAME) with APP_NAME = \"com.android.systemui.tests\". Adjust this if the test app label differs on the target branch."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "070eff919c85fd83501e380a92e30caf082e9ffc",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 1,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2023-40121",
     "osv_id": "ASB-A-224771621",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Rewrites DatabaseUtils.appendEscapedSQLString to walk every UTF-16 code unit, keep only well-formed surrogate pairs, drop unpaired (lone) high or low surrogates, and escape single quotes. Before the fix it appended the raw string verbatim when the string had no single quote.",
      "confidence": "high",
      "backport_notes": [
       "Single self-contained Java change to one static method, with no new dependencies. The material includes only the Android 14 diff base, so check that the pre-fix body (indexOf fast path plus quote-doubling loop) matches on your 11 to 13 trees before applying the hunk. DatabaseUtils is in the framework (not a mainline module), so it ships with the platform image (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "c700cdc015a8855c0a7eeebf7669dd640522e12c",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "23d54f494415b8364d0402955ed291fd43630d59",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    },
    {
     "cve": "CVE-2023-40125",
     "osv_id": "ASB-A-279902472",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Adds a user-restriction gate to ApnEditor.onCreate in Settings so the APN editor finishes immediately when the current user is not an admin or carries the DISALLOW_CONFIG_MOBILE_NETWORKS restriction.",
      "confidence": "high",
      "backport_notes": [
       "Single commit: it adds the UserManager import, a guard at the top of onCreate and the isUserRestricted() helper, using UserManager.isAdminUser/hasUserRestriction and the existing finish()/getContext() of the fragment. On older branches, check by hand that the onCreate anchor (super.onCreate followed by setLifecycleForAllControllers) matches.",
       "The new tests rely on the UserManager stub added to ApnEditorTest setUp (getSystemService(UserManager.class), isAdminUser true, no restriction by default)."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/apps/Settings",
       "sha": "97b5b908aeb8eac42b05084f7716708d090cedf3",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/apps/Settings",
       "sha": "b4e4facff4478dab07dd8d0b334ce0356396c01d",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/apps/Settings"
     ]
    },
    {
     "cve": "CVE-2023-40127",
     "osv_id": "ASB-A-262244882",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "ID"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Canonicalizes the caller-supplied file path in MediaProvider's SCAN_FILE_CALL handler and in ModernMediaScanner's public scanFile(), scanDirectory() and onDirectoryDirty() entry points, so that non-canonical paths (\"..\", symlinks) are not scanned as-is under MediaProvider's cleared calling identity.",
      "confidence": "high",
      "backport_notes": [
       "Single commit touching three files; apply the three files together. MediaProvider.java depends on the new public FileUtils.getCanonicalFile(), and the getCanonicalPath() signature change (public, throws IOException) needs the matching extractRelativePath() update. In trees where the SCAN_FILE_CALL body has been moved into MediaProvider.getResultForScanFile(), apply the canonicalization there.",
       "MediaProvider is a mainline (APEX) module, so devices that receive module updates may get the fix through the module rather than through the platform tag (general platform knowledge)."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "e7561e10a58e6064ad11c65be793e06640400dc8",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/packages/providers/MediaProvider",
       "sha": "0bbf3f4c3e75f1995d7555cc1643db049ef1bf11",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/packages/providers/MediaProvider"
     ]
    },
    {
     "cve": "CVE-2023-40131",
     "osv_id": "ASB-A-282919145",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "Converts the two detached async-initialization threads spawned in the GpuService constructor into owned std::thread members and adds a GpuService destructor that joins them, so the threads can no longer run against a destroyed GpuService object.",
      "confidence": "high",
      "backport_notes": [
       "The security-relevant change is self-contained in GpuService.h/GpuService.cpp: two unique_ptr<std::thread> members, removal of detach(), and a destructor that joins them. The header move to include/gpuservice/, the Android.bp export_include_dirs change and the new test exist for testability and can be adapted if the include layout differs.",
       "Only join thread members that the target branch's constructor actually assigns; an unconditional join() on a never-assigned unique_ptr would dereference null."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/native",
       "sha": "a2030f098afa8c34716956a766f13fd792b0e0bf",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/native",
       "sha": "e562203ac243bf9d64bd33c922fc28e781a642fb",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/native"
     ]
    },
    {
     "cve": "CVE-2023-40140",
     "osv_id": "ASB-A-274058082",
     "source": "osv",
     "announced": "2023-10-01",
     "severity": "High",
     "types": [
      "EoP"
     ],
     "status": "resolved",
     "brief": {
      "summary": "android_view_InputDevice_create now gives the Java KeyCharacterMap a copy of the native KeyCharacterMap (std::make_shared<KeyCharacterMap>(*map)) instead of the shared_ptr it shares with the native InputDeviceInfo, so Java-side reads no longer hit the same instance that native code can modify.",
      "confidence": "high",
      "backport_notes": [
       "Single-file, self-contained change in core/jni/android_view_InputDevice.cpp. It assumes deviceInfo.getKeyCharacterMap() returns a std::shared_ptr<KeyCharacterMap> and that KeyCharacterMap can be copy-constructed. Check both on the target branch."
      ],
      "meta": {
       "generated": "2026-10-04",
       "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_r1",
      "date": "2023-10-04",
      "provenance": "security_tag"
     },
     "last_fix": {
      "tag": "android-security-14.0.0_r2",
      "date": "2023-11-06",
      "provenance": "security_tag"
     },
     "public_commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "3839e41580d0ac12583a4e45d311a1222980b75a",
       "tag": "android-security-14.0.0_r1",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      },
      {
       "project": "platform/frameworks/base",
       "sha": "5ae8ed07ace782208e6cd86a39d9cd91675c173c",
       "tag": "android-security-14.0.0_r2",
       "provenance": "security_tag",
       "method": "bug_id",
       "confidence": "certain",
       "evidence": null,
       "similarity": null
      }
     ],
     "n_public_commits": 2,
     "multi_tag_fix": true,
     "projects": [
      "platform/frameworks/base"
     ]
    }
   ],
   "tags": [
    "android-security-14.0.0_r1"
   ],
   "unannounced": [
    {
     "bug_ids": [
      "231476072"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "7cf08cea84dfdc1359355333702b1c5248f07c3b",
       "subject": "Update parcling logic for Uris.",
       "tag": "android-security-14.0.0_r1",
       "merged": "2023-08-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r1"
     ],
     "tag": "android-security-14.0.0_r1"
    },
    {
     "bug_ids": [
      "286882367"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "0165d3c5920ab2c2f0e18c7070a51a584f206ec2",
       "subject": "Add permission check for RemoteTransition in ActivityOptions",
       "tag": "android-security-14.0.0_r1",
       "merged": "2023-08-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r1"
     ],
     "tag": "android-security-14.0.0_r1"
    },
    {
     "bug_ids": [
      "205837340"
     ],
     "commits": [
      {
       "project": "platform/frameworks/base",
       "sha": "8719499cbe60181440b6fc085258f174e758f932",
       "subject": "RingtoneManager: verify default ringtone is audio",
       "tag": "android-security-14.0.0_r1",
       "merged": "2023-08-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r1"
     ],
     "tag": "android-security-14.0.0_r1"
    },
    {
     "bug_ids": [
      "278556954"
     ],
     "commits": [
      {
       "project": "platform/packages/apps/Contacts",
       "sha": "1037aaa70fa7b9488f60f1758774b62ee8b0a723",
       "subject": "Don't allow importing contacts to accounts passed in by intent unless it is valid",
       "tag": "android-security-14.0.0_r1",
       "merged": "2023-08-10",
       "markers": []
      }
     ],
     "tags": [
      "android-security-14.0.0_r1"
     ],
     "tag": "android-security-14.0.0_r1"
    }
   ],
   "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-2023-4863": {
     "cve": "CVE-2023-4863",
     "summary": "The fixed-size HuffmanCode buffers in the VP8L (lossless WebP) decoder are replaced with a chained HuffmanTables segment structure. VP8LBuildHuffmanTable now does a dry run of BuildHuffmanTable to compute the exact table size and allocates a new segment when the current one is too small, so Huffman tables are no longer written past the allocated buffer.",
     "component": "platform/external/webp, libwebp VP8L lossless decoder (src/dec/vp8l_dec.c, src/utils/huffman_utils.c)",
     "weakness": "Out-of-bounds write caused by a missing size check: Huffman lookup tables were built into buffers whose size was a fixed precomputed estimate, not derived from the actual code lengths.",
     "root_cause": "ReadHuffmanCodes in src/dec/vp8l_dec.c allocated one HuffmanCode array of num_htree_groups * table_size. It passed successive slices of that array to ReadHuffmanCode/VP8LBuildHuffmanTable, advancing by the size each call returned. ReadHuffmanCodeLengths used a stack array, HuffmanCode table[1 << LENGTHS_TABLE_BITS]. BuildHuffmanTable in src/utils/huffman_utils.c writes a root table plus second-level tables. Their total size depends on the code lengths in the bitstream: unbalanced codes produce more and larger second-level tables. BuildHuffmanTable wrote into root_table without checking the buffer size. The existing dry run with a NULL root_table could not be used to size the buffer, because the second-level loop did 'if (root_table == NULL) continue;'. According to the old header comment, a NULL root_table returned 0 or something greater than 0, not the table size. The commit title describes the result as an OOB write in BuildHuffmanTable.",
     "fix_analysis": [
      "src/utils/huffman_utils.h: adds HuffmanTablesSegment {start, curr_table, next, size} and HuffmanTables {root, curr_segment}. It declares VP8LHuffmanTablesAllocate and VP8LHuffmanTablesDeallocate. VP8LBuildHuffmanTable now takes HuffmanTables* instead of HuffmanCode*. The comment saying a NULL root_table does not return the table size is removed.",
      "src/utils/huffman_utils.c:BuildHuffmanTable: the early 'if (root_table == NULL) continue;' is removed from the second-level loop. Table pointer advancement, the root_table[low] writes and ReplicateValue are each now guarded by 'if (root_table != NULL)'. table_bits, table_size, total_size, low and key are still updated in both modes, so the dry run now computes the total size including second-level tables.",
      "src/utils/huffman_utils.c:VP8LBuildHuffmanTable: first calls BuildHuffmanTable(NULL, ...) to validate the code and get total_size. It returns total_size if that is 0 or if root_table is NULL. Next it checks 'curr_segment->curr_table + total_size >= curr_segment->start + curr_segment->size'. If there is not enough room, it mallocs a new HuffmanTablesSegment of size max(total_size, current segment size), links it as curr_segment->next and makes it current. The rest of the old segment is left unused because the table must be contiguous. It then builds into curr_segment->curr_table, using a stack 'sorted' array when code_lengths_size <= SORTED_SIZE_CUTOFF and a heap array otherwise, and returns total_size.",
      "src/utils/huffman_utils.c: the new VP8LHuffmanTablesAllocate(size, tables) allocates the root segment with 'size' entries and sets curr_segment = &root. The new VP8LHuffmanTablesDeallocate frees root->start, sets start and next to NULL, then walks the chain and frees every following segment.",
      "src/dec/vp8li_dec.h: VP8LMetadata.huffman_tables_ changes from HuffmanCode* to an embedded HuffmanTables struct.",
      "src/dec/vp8l_dec.c:ReadHuffmanCodeLengths: the stack table is replaced by a HuffmanTables allocated with VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS) and built with VP8LBuildHuffmanTable. Lookups use tables.curr_segment->start[...], and VP8LHuffmanTablesDeallocate is called at End.",
      "src/dec/vp8l_dec.c:ReadHuffmanCode: the table parameter type changes to HuffmanTables*. The body is not shown in the material.",
      "src/dec/vp8l_dec.c:ReadHuffmanCodes: uses &hdr->huffman_tables_ directly and asserts it is zero-initialized. It allocates the tables with VP8LHuffmanTablesAllocate(num_htree_groups * table_size) inside the existing OOM check. After each ReadHuffmanCode it sets htrees[j] = huffman_tables->curr_segment->curr_table and advances curr_segment->curr_table by the returned size. On Error it calls VP8LHuffmanTablesDeallocate instead of WebPSafeFree.",
      "src/dec/vp8l_dec.c:ClearMetadata and VP8LDecodeImage: ClearMetadata now calls VP8LHuffmanTablesDeallocate, and VP8LDecodeImage now asserts hdr_.huffman_tables_.root.start != NULL.",
      "README.android: records the cherry-pick of 904941b4 and the merge of d64e6d7d."
     ],
     "completeness": [
      "The fix converts both callers of VP8LBuildHuffmanTable shown in the material. ReadHuffmanCodeLengths builds the code-length code table, which was formerly on the stack. ReadHuffmanCodes, through ReadHuffmanCode, builds the per-group tables in hdr->huffman_tables_.",
      "The size check sits inside VP8LBuildHuffmanTable and uses the total_size from the dry run, so it does not depend on the callers' size estimates. The original num_htree_groups * table_size allocation is kept only as the initial capacity.",
      "The check uses '>=' rather than '>'. This forces a new segment even when the table would exactly fill the remaining space, which is conservative.",
      "The fix does not tighten the initial capacity estimate. Valid unbalanced streams now succeed by allocating extra heap segments.",
      "Correctness relies on the returned total_size matching the number of entries written. The dry run and the real build share one code path.",
      "The commit touches only decoder and huffman_utils files, so encoder-side code is not changed by this commit."
     ],
     "behavior_changes": [
      "Valid bitstreams with unbalanced Huffman codes whose tables exceed the initial capacity are now decoded. According to the commit message, this is intended to keep such streams decodable.",
      "There are more heap allocations. ReadHuffmanCodeLengths now mallocs a 1 << LENGTHS_TABLE_BITS table instead of using the stack, and extra HuffmanTablesSegment chunks are allocated on demand.",
      "If allocating a new segment fails, VP8LBuildHuffmanTable returns 0. In ReadHuffmanCodeLengths this ends in VP8_STATUS_BITSTREAM_ERROR.",
      "Each table build now runs BuildHuffmanTable twice, a dry run plus the real build.",
      "The VP8LMetadata layout changes because huffman_tables_ becomes an embedded struct. VP8LMetadata is defined in an internal decoder header."
     ],
     "preconditions": [
      "A VP8L (lossless) WebP bitstream must be decoded by libwebp from external/webp. The Huffman code lengths come from the bitstream and determine the size of the tables that BuildHuffmanTable builds.",
      "The vulnerable code is reached through ReadHuffmanCodes/ReadHuffmanCode and ReadHuffmanCodeLengths in src/dec/vp8l_dec.c.",
      "The bulletin rates it Critical RCE. The material does not show which Android processes link external/webp."
     ],
     "per_major": {
      "14": "Same fix. Google cherry-picked it as 9eac4f723bf7 into android-security-14.0.0_r2. README.android records 'cherry-pick 904941b4' on top of libwebp 1.3.0. At android-security-14.0.0_r29, 6/8 lines of the patched region are present and the HuffmanTables code is in place.",
      "15": "Not affected per the bulletin. At android-security-15.0.0_r17, 6/8 lines of the patched region are present.",
      "16": "Not affected per the bulletin. At android-security-16.0.0_r8, 6/8 lines of the patched region are present.",
      "17": "Not affected per the bulletin. At android-security-17.0.0_r1, 6/8 lines of the patched region are present."
     },
     "backport_notes": [
      "The change alters the VP8LBuildHuffmanTable signature and the type of VP8LMetadata.huffman_tables_. All users must be converted together: ReadHuffmanCodeLengths, ReadHuffmanCode/ReadHuffmanCodes, ClearMetadata and VP8LDecodeImage. The new code asserts that hdr->huffman_tables_ was zero-initialized by InitMetadata.",
      "README.android also lists 'merge d64e6d7d Merge \"PaletteSortModifiedZeng: fix leak on error\" into main'. The material does not show whether that merge is a prerequisite for this fix."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the body of ReadHuffmanCode or how table_size is computed, so the original capacity estimate is not visible.",
      "Whether the replaced stack table in ReadHuffmanCodeLengths could itself overflow is not shown.",
      "Which Android processes or apps link the external/webp decoder is not shown.",
      "Whether 902bc919 and 2af26267 are upstream libwebp hashes or Android-internal ones is not shown.",
      "Why only 6/8 lines of the patched region match in later tags is not shown."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-21252": {
     "cve": "CVE-2023-21252",
     "summary": "Adds an isWapi flag to WifiConfigurationUtil.validatePassword. A WAPI-PSK hex preSharedKey is then capped at 100 characters instead of going through the generic PSK_SAE_HEX_LEN rule. Per the commit message, the goal is to stop arbitrarily large passwords.",
     "component": "platform/packages/modules/Wifi, Wi-Fi service (WifiConfigurationUtil, WifiConfiguration validation)",
     "weakness": "missing/insufficient input length bound on a network credential string (DoS per bulletin)",
     "root_cause": "WifiConfigurationUtil.validatePassword had no notion of the WAPI security type. Its hex-PSK branch applied one rule (length == PSK_SAE_HEX_LEN) to every caller, and the SECURITY_TYPE_WAPI_PSK call site in validate() passed the same arguments (isAdd, isSae=false) as WPA-PSK. The commit message says the goal is to stop arbitrarily large WAPI passwords, and the diff adds an explicit WAPI-specific upper bound of 100 characters on the hex string. The material does not show a code state in which WAPI passwords were unbounded: in the shown pre-image, WAPI already went through the exact PSK_SAE_HEX_LEN check.",
     "fix_analysis": [
      "service/java/com/android/server/wifi/WifiConfigurationUtil.java:validatePassword: the signature is extended to validatePassword(String password, boolean isAdd, boolean isSae, boolean isWapi). In the hex-PSK branch (password does not start with a double quote), when isWapi is true the password is rejected if password.length() > 100, with the log 'validatePassword failed: WAPI hex string too long'. Otherwise the existing exact check password.length() != PSK_SAE_HEX_LEN still applies.",
      "service/java/com/android/server/wifi/WifiConfigurationUtil.java:validate (isAdd-aware path, hunk at new line 733): the SECURITY_TYPE_PSK and SECURITY_TYPE_SAE calls pass isWapi=false, and the SECURITY_TYPE_WAPI_PSK call now passes isWapi=true.",
      "service/java/com/android/server/wifi/WifiConfigurationUtil.java: a second validation path (hunk at new line 905, PSK and SAE only) is updated to pass isWapi=false. No WAPI call is added there.",
      "service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java: the negative test key changes from a 40-character string to a 101-character digit string, which is expected to fail validate(..., VALIDATE_FOR_ADD)."
     ],
     "completeness": [
      "The fix covers only the hex (unquoted) preSharedKey path for SECURITY_TYPE_WAPI_PSK in WifiConfigurationUtil.validate. The bound is a hard-coded literal (100), not a named constant.",
      "This diff leaves the quoted ASCII path unchanged. The visible context shows only a minimum-length check (targetMinLength) for ASCII strings; any maximum check for ASCII is not shown.",
      "The second validation routine (hunk at new line 905) validates only PSK and SAE passwords and gets no WAPI check. Whether WAPI configurations can reach that routine is not shown.",
      "PSK and SAE keep the existing exact PSK_SAE_HEX_LEN check. DPP and other security types are untouched.",
      "In the shown pre-image, WAPI hex keys were subject to the exact PSK_SAE_HEX_LEN check. After the fix they only fail the length check when longer than 100 characters, which relaxes the length rule for shorter keys."
     ],
     "behavior_changes": [
      "A WAPI-PSK hex preSharedKey longer than 100 characters is rejected by WifiConfigurationUtil.validate, with Log.e 'validatePassword failed: WAPI hex string too long: <len>'.",
      "A non-empty WAPI-PSK hex preSharedKey of 100 characters or fewer no longer has to equal PSK_SAE_HEX_LEN in length. Whether later checks in the branch still apply is not shown.",
      "PSK and SAE validation is unchanged. The private validatePassword signature gains a fourth boolean parameter (internal API only)."
     ],
     "preconditions": [
      "A WifiConfiguration with SECURITY_TYPE_WAPI_PSK and a preSharedKey that does not start with a double quote (hex form) must reach WifiConfigurationUtil.validate for an add or update."
     ],
     "per_major": {
      "14": "same fix: commit 1d72a8130807 (cherry-pick of 38707fb4ff1405663cc24affc95244f4cc830499, Change-Id I15f3aff373af56c253a50c308d886a7acf661e59) in android-security-14.0.0_r1, also present as 9051eef78a76 in android-security-14.0.0_r2. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.",
      "15": "not affected (not listed by the bulletin). At android-security-15.0.0_r17, 7/8 lines of the patched region are present.",
      "16": "not affected (not listed by the bulletin). At android-security-16.0.0_r8, 7/8 lines of the patched region are present.",
      "17": "not affected (not listed by the bulletin). At android-security-17.0.0_r1, 7/8 lines of the patched region are present."
     },
     "backport_notes": [
      "This is a single self-contained commit that touches only the private validatePassword and its call sites in WifiConfigurationUtil.java. A backport needs the SECURITY_TYPE_WAPI_PSK validate call to exist in the target, and the matching WifiConfigurationUtilTest change should be taken as well. Wifi is a mainline module, so the fix may also arrive through a module update rather than a platform tag (general platform knowledge)."
     ],
     "confidence": "medium",
     "unknowns": [
      "The pre-fix code state in which WAPI passwords were unbounded. In the pre-image shown here, the WAPI call already went through the exact PSK_SAE_HEX_LEN check, so the material does not show how 'arbitrarily large' passwords were accepted before this commit.",
      "Which downstream consumer of the WAPI preSharedKey was affected by large values, and the concrete DoS effect. The bulletin gives only the DoS type and High severity.",
      "Which entry points call WifiConfigurationUtil.validate, and which security type the modified negative test configures.",
      "The value of PSK_SAE_HEX_LEN, why 100 was chosen as the bound, and whether the quoted ASCII WAPI path has its own maximum."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40120": {
     "cve": "CVE-2023-40120",
     "summary": "SystemUI's MediaDataManager now treats blank (not only null) media titles as missing, falls through the existing fallbacks, and finally substitutes a new placeholder string \"<app name> is running\" when no title source is non-blank.",
     "component": "platform/frameworks/base, SystemUI media controls (MediaDataManager)",
     "weakness": "Improper input validation: a null-only check let an empty or whitespace-only title bypass the title fallback chain, producing a media control with no identifying title (the bulletin classifies this as EoP)",
     "root_cause": "In packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt, the song title was taken from MediaMetadata.METADATA_KEY_DISPLAY_TITLE, then METADATA_KEY_TITLE, then HybridGroupManager.resolveTitle(notif), but each fallback was gated on `song == null`. An app posting a media session whose title fields were empty strings or whitespace passed the null checks, so no fallback ran and the resulting MediaData.song was blank. There was also no final fallback when every source was absent, so the media control could be shown without a title identifying the app.",
     "fix_analysis": [
      "packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt (MediaDataManager, title resolution): the two fallback conditions change from `song == null` to `song.isNullOrBlank()`. A blank METADATA_KEY_DISPLAY_TITLE now falls through to METADATA_KEY_TITLE, and a blank METADATA_KEY_TITLE falls through to HybridGroupManager.resolveTitle(notif).",
      "Same function: a third branch is added. If the title is still null or blank after all sources, `song = context.getString(R.string.controls_media_empty_title, app)` supplies a placeholder built from the app name.",
      "packages/SystemUI/res/values/strings.xml: new string resource controls_media_empty_title = \"%1$s is running\" (app_name placeholder, CHAR_LIMIT=NONE).",
      "packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt: adds tests for an empty title and a blank title (placeholder expected), and for empty metadata with a non-blank notification title (notification title expected). The APP_NAME constant changes to \"com.android.systemui.tests\" and a MediaData ArgumentCaptor is added."
     ],
     "completeness": [
      "Covers the title (song) field only. A null or blank value at each source moves on to the next one, and the placeholder is used when all three sources are null or blank.",
      "The artist field directly below (`metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)`) is not changed by this commit. The diff shows its declaration but no blank handling for it.",
      "The placeholder depends on the `app` value being meaningful. The diff does not show how `app` is derived or whether it can itself be blank.",
      "Only the notification-driven media data path visible in the diff is changed. Other MediaData construction paths (e.g. resumption or smartspace) are not in the diff and their handling is unknown."
     ],
     "behavior_changes": [
      "Media controls whose title sources are all empty or whitespace now display \"<app name> is running\" instead of a blank title.",
      "Empty-string metadata titles now fall back to the notification's content title, where previously the empty string was used as-is.",
      "New translatable string resource controls_media_empty_title added to SystemUI."
     ],
     "preconditions": [
      "An app posts a MediaStyle notification with an attached media session (handled by MediaDataManager.onNotificationAdded, per the tests) whose METADATA_KEY_DISPLAY_TITLE / METADATA_KEY_TITLE and notification title are null, empty or whitespace."
     ],
     "per_major": {
      "14": "same fix: resolved via commit 070eff919c85 in android-security-14.0.0_r2 (the bulletin lists 14-next). At android-security-14.0.0_r29 the controls_media_empty_title string is present and 5/8 lines of the patched region are present. The Kotlin file is not at its original path at r29. Per index search, a MediaDataManager.kt exists under media/controls/domain/pipeline in android-14.0.0_r75, but the material does not show the fixed title chain there.",
      "15": "not affected per the bulletin; android-security-15.0.0_r17 has 5/8 lines of the patched region present. Whether the full fix is present is not shown.",
      "16": "not affected per the bulletin; android-security-16.0.0_r8 has 4/8 lines of the patched region present. Whether the full fix is present is not shown.",
      "17": "not affected per the bulletin; android-security-17.0.0_r1 has 4/8 lines of the patched region present. Whether the full fix is present is not shown."
     },
     "backport_notes": [
      "Single self-contained commit. Add the string resource together with the Kotlin change, since the code references R.string.controls_media_empty_title. The logic change is in the title fallback chain. The file path differs in newer trees (per index search, media/controls/domain/pipeline), so apply the patch by matching code content, not file path.",
      "The new tests compare against context.getString(R.string.controls_media_empty_title, APP_NAME) with APP_NAME = \"com.android.systemui.tests\". Adjust this if the test app label differs on the target branch."
     ],
     "confidence": "medium",
     "unknowns": [
      "The material does not explain the bulletin's EoP classification. The concrete impact of a blank-title media control is not shown.",
      "How the `app` name is derived in this function, and whether it can be blank, is not shown in the diff.",
      "Whether Android 11 to 13 received the same or an adapted patch is not shown. The material only shows the Android 14 resolution.",
      "Whether the relocated MediaDataManager in later trees contains the full fixed title chain is not shown. Only partial line counts are available."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40121": {
     "cve": "CVE-2023-40121",
     "summary": "Rewrites DatabaseUtils.appendEscapedSQLString to walk every UTF-16 code unit, keep only well-formed surrogate pairs, drop unpaired (lone) high or low surrogates, and escape single quotes. Before the fix it appended the raw string verbatim when the string had no single quote.",
     "component": "platform/frameworks/base, android.database (DatabaseUtils SQL string escaping)",
     "weakness": "Improper input validation / improper neutralization in SQL string escaping (malformed UTF-16 surrogates passed through unchecked)",
     "root_cause": "In core/java/android/database/DatabaseUtils.java, appendEscapedSQLString only handled the single-quote character. If the input had no '\\'' it appended the whole string unchanged (fast path); otherwise it only doubled quotes. It never checked surrogate code units, so strings with malformed UTF-16 (unpaired high or low surrogates) went into the quoted SQL literal as-is. The commit message states the goal is to \"detect malformed UTF-16 strings\". The bulletin classifies the issue as information disclosure (ID). The material does not show the downstream mechanism by which the lone surrogates led to disclosure.",
     "fix_analysis": [
      "core/java/android/database/DatabaseUtils.java:appendEscapedSQLString: removes the indexOf('\\'') fast path, so every input string is now processed character by character.",
      "For each char c: if Character.isHighSurrogate(c) and it is the last char, it is skipped. If the next char is a low surrogate, both are appended together and the loop continues; the next iteration re-reads the low surrogate and skips it through the isLowSurrogate branch. Otherwise the lone high surrogate is skipped.",
      "If Character.isLowSurrogate(c) (either a lone low surrogate or the second half of a pair already appended), the char is skipped.",
      "Remaining chars: a '\\'' is doubled, then the char is appended. Opening and closing quotes are still added around the result.",
      "Net effect: the output literal contains only well-formed UTF-16 plus correctly escaped quotes. Malformed surrogates are silently removed, not rejected."
     ],
     "completeness": [
      "Only appendEscapedSQLString is changed. Callers that go through it are covered transitively: SQLiteQueryBuilder.appendWhereEscapeString in frameworks/base, plus ContactsProvider LegacyApiSupport.appendRawContactsAccount / appendGroupAccount and ContactsProvider2.appendSnippetFunction (per index search).",
      "Lone surrogates are dropped, not rejected. No exception or error is raised, so callers cannot tell a sanitized string from the original.",
      "A trailing high surrogate is dropped and a surrogate pair is kept intact. Non-surrogate code units pass through unchanged apart from quote doubling; no other characters are validated.",
      "SQL that callers build by direct string concatenation, without appendEscapedSQLString, is not affected by this change. The material shows no other escaping routine in DatabaseUtils being changed."
     ],
     "behavior_changes": [
      "Strings containing unpaired surrogate code units are silently altered: the lone surrogates are left out of the generated SQL literal.",
      "The fast path that appended the string without iterating when it had no single quote is gone. Every call now iterates over all characters, with no functional difference for well-formed input.",
      "Well-formed strings (with or without single quotes) produce the same output as before."
     ],
     "preconditions": [
      "A caller passes a string containing malformed UTF-16 (an unpaired high or low surrogate) to DatabaseUtils.appendEscapedSQLString, directly or through wrappers that call it.",
      "Per index search, reachable from SQLiteQueryBuilder.appendWhereEscapeString (frameworks/base) and from ContactsProvider code paths (LegacyApiSupport.appendRawContactsAccount, appendGroupAccount, ContactsProvider2.appendSnippetFunction). The material does not show which of these take externally controlled input."
     ],
     "per_major": {
      "14": "same fix: commit c700cdc015a8 in android-security-14.0.0_r1, with matching commit 23d54f494415 in android-security-14.0.0_r2 (same Change-Id Ide65205b83063801971c5778af3154bcf3f0e530). The patched loop is present at android-security-14.0.0_r29 (7/8 lines of the patched region); the code shown there is the fixed version.",
      "15": "not affected per bulletin. The fixed appendEscapedSQLString is present at android-security-15.0.0_r17 (7/8 lines of the patched region).",
      "16": "not affected per bulletin. Fixed code present at android-security-16.0.0_r8 (7/8 lines of the patched region).",
      "17": "not affected per bulletin. Fixed code present at android-security-17.0.0_r1 (7/8 lines of the patched region)."
     },
     "backport_notes": [
      "Single self-contained Java change to one static method, with no new dependencies. The material includes only the Android 14 diff base, so check that the pre-fix body (indexOf fast path plus quote-doubling loop) matches on your 11 to 13 trees before applying the hunk. DatabaseUtils is in the framework (not a mainline module), so it ships with the platform image (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show how unpaired surrogates in the SQL literal led to information disclosure (for example, what the downstream consumer did with the malformed UTF-16).",
      "The POC referenced in bug 224771621 and the atest test are not included.",
      "The material does not show whether the Android 11, 12, 12L and 13 branches got the same cherry-pick, or whether their pre-fix code matches. Only the Android 14 commits are provided.",
      "How the fixed code came to be present in the Android 15 to 17 tags is not shown."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40125": {
     "cve": "CVE-2023-40125",
     "summary": "Adds a user-restriction gate to ApnEditor.onCreate in Settings so the APN editor finishes immediately when the current user is not an admin or carries the DISALLOW_CONFIG_MOBILE_NETWORKS restriction.",
     "component": "platform/packages/apps/Settings, network/apn (ApnEditor fragment)",
     "weakness": "missing authorization check (user restriction / admin-user check not enforced)",
     "root_cause": "ApnEditor (src/com/android/settings/network/apn/ApnEditor.java) did not consult UserManager in onCreate, so neither the admin-user status nor the DISALLOW_CONFIG_MOBILE_NETWORKS user restriction was enforced before the APN editing UI and the rest of fragment initialization ran. A non-admin or restricted user could therefore reach APN configuration that the restriction is meant to block.",
     "fix_analysis": [
      "ApnEditor.java:onCreate: right after super.onCreate(icicle), calls the new isUserRestricted(); if true, logs an error, calls finish() and returns before setLifecycleForAllControllers() and the rest of the fragment initialization runs.",
      "ApnEditor.java:isUserRestricted (new, @VisibleForTesting): obtains UserManager via getContext().getSystemService(UserManager.class); returns false if it is null; returns true if !userManager.isAdminUser(); returns true if userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); otherwise false.",
      "ApnEditorTest.java: adds a mocked UserManager (admin user, no restriction by default) to setUp, plus onCreate_notAdminUser_shouldFinish and onCreate_hasUserRestriction_shouldFinish verifying finish() is called."
     ],
     "completeness": [
      "The gate is applied only at onCreate of ApnEditor; the diff adds no independent check to the fragment's later entry points (menu actions, save/delete paths, dialogs), which rely on onCreate having finished the fragment.",
      "The check is fail-open when UserManager cannot be obtained (userManager == null returns false, so the editor proceeds).",
      "Only the ApnEditor fragment is changed; the material does not show whether the APN list screen or other APN-related entry points in Settings perform equivalent checks.",
      "The check runs after super.onCreate, so base fragment creation completes before finish() is invoked; this is a UI-level finish, not a throw."
     ],
     "behavior_changes": [
      "ApnEditor now closes immediately (finish()) for non-admin users and for users with DISALLOW_CONFIG_MOBILE_NETWORKS, logging 'This setting isn't available due to user restriction.' plus a reason line at error level.",
      "Non-admin users lose access to the APN editor UI entirely, even without an explicit DISALLOW_CONFIG_MOBILE_NETWORKS restriction."
     ],
     "preconditions": [
      "The ApnEditor fragment in the Settings app is launched in a user that is either not an admin user or has the DISALLOW_CONFIG_MOBILE_NETWORKS restriction set.",
      "A UserManager system service is available to the fragment's context (otherwise the new check does not block)."
     ],
     "per_major": {
      "14": "same fix: commit 97b5b908aeb8 in android-security-14.0.0_r1 and an identical commit b4e4facff447 (same Change-Id Iecdbbff7e21dfb11e3ba385858747a220cfd3e04) in android-security-14.0.0_r2; at android-security-14.0.0_r29 all 8/8 lines of the patched region are present.",
      "15": "not affected per the bulletin; patched region fully present (8/8 lines) at android-security-15.0.0_r17.",
      "16": "not affected per the bulletin; patched region fully present (8/8 lines) at android-security-16.0.0_r8.",
      "17": "not affected per the bulletin; patched region fully present (8/8 lines) at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single commit: it adds the UserManager import, a guard at the top of onCreate and the isUserRestricted() helper, using UserManager.isAdminUser/hasUserRestriction and the existing finish()/getContext() of the fragment. On older branches, check by hand that the onCreate anchor (super.onCreate followed by setLifecycleForAllControllers) matches.",
      "The new tests rely on the UserManager stub added to ApnEditorTest setUp (getSystemService(UserManager.class), isAdminUser true, no restriction by default)."
     ],
     "confidence": "high",
     "unknowns": [
      "The material does not show the fix commits for Android 11, 12, 12L and 13, which the bulletin lists as affected.",
      "The material does not show how ApnEditor is reached (which intents or parent screens launch it) nor whether those callers already enforce the same restriction.",
      "The material does not show how the patched code came to be present in the 15, 16 and 17 tags.",
      "The bulletin does not describe the concrete impact beyond rating it High EoP."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40127": {
     "cve": "CVE-2023-40127",
     "summary": "Canonicalizes the caller-supplied file path in MediaProvider's SCAN_FILE_CALL handler and in ModernMediaScanner's public scanFile(), scanDirectory() and onDirectoryDirty() entry points, so that non-canonical paths (\"..\", symlinks) are not scanned as-is under MediaProvider's cleared calling identity.",
     "component": "platform/packages/providers/MediaProvider: MediaProvider.call() SCAN_FILE_CALL handling, plus ModernMediaScanner / FileUtils path handling",
     "weakness": "Path traversal: a caller-controlled file path was not canonicalized before being scanned with a cleared (elevated) calling identity",
     "root_cause": "In MediaProvider.call(), the SCAN_FILE_CALL branch ran `new File(arg)` on the raw string supplied by the caller. It passed the result straight to scanFile(file, REASON_DEMAND) after clearLocalCallingIdentity()/clearCallingIdentity(), so the scan ran with MediaProvider's own identity on whatever path the caller named. The resulting Uri was returned to the caller in Intent.EXTRA_STREAM. Neither the provider nor ModernMediaScanner.scanFile()/scanDirectory()/onDirectoryDirty() canonicalized the path, so paths containing \"..\" segments or symlinks reached the scanner in non-canonical form. The bulletin classifies the issue as information disclosure (ID), High.",
     "fix_analysis": [
      "src/com/android/providers/media/MediaProvider.java:call(), case MediaStore.SCAN_FILE_CALL: split out of the block it shared with SCAN_VOLUME_CALL into its own block. The arg string goes to FileUtils.getCanonicalFile(filePath). If that throws IOException, file is set to null and no scan happens (uri = null). Only the canonical File is passed to scanFile(file, REASON_DEMAND). The identity is still cleared before the scan and restored after it. A TODO (b/262244882) says enforceCallingPermissionInternal(uri, ...) might still be added. The commit message says the permission check was added, but the diff does not implement it.",
      "src/com/android/providers/media/MediaProvider.java:call(), case MediaStore.SCAN_VOLUME_CALL: rewritten as a standalone block with the same logic (findVolume, then MediaService.onScanVolume). It now returns Bundle.EMPTY instead of a new empty Bundle().",
      "src/com/android/providers/media/scan/ModernMediaScanner.java:scanDirectory(): after requireNonNull, replaces `file` with file.getCanonicalFile(). On IOException it logs and returns without scanning. The redundant `return` in the FileNotFoundException catch is removed.",
      "src/com/android/providers/media/scan/ModernMediaScanner.java:scanFile(): same canonicalization. On IOException it logs and returns null.",
      "src/com/android/providers/media/scan/ModernMediaScanner.java:onDirectoryDirty(): the parameter is annotated @NonNull, requireNonNull(dir) is added and dir is replaced by dir.getCanonicalFile(). On IOException it logs and returns. As a result, mPendingCleanDirectories.remove() and FileUtils.setDirectoryDirty() operate on the canonical path.",
      "src/com/android/providers/media/util/FileUtils.java: the private getCanonicalPath(String), which accepted null and swallowed exceptions, is replaced by two public @NonNull helpers that throw IOException: getCanonicalFile(String) (new, used by MediaProvider) and getCanonicalPath(String). extractRelativePath() already canonicalized before the fix. It now calls the new getCanonicalPath inside its own try/catch, so it still returns null on failure and keeps the original `data` unchanged for logging."
     ],
     "completeness": [
      "Covers the SCAN_FILE_CALL method and the three public ModernMediaScanner entry points (scanFile, scanDirectory, onDirectoryDirty). Internal callers such as MediaProvider.scanFileAsMediaProvider()/scanRenamedDirectoryForFuse() (per index search) still build `new File(path)` and call MediaProvider.scanFile(). They benefit from the scanner-level canonicalization only if MediaProvider.scanFile() delegates to ModernMediaScanner.scanFile(), which the material does not show.",
      "The permission check named in the commit message (enforceCallingPermissionInternal() in SCAN_FILE_CALL) is not in the diff; only a TODO comment referencing the same bug is added. The handler still returns the scanned file's Uri to the caller without an access check after the scan.",
      "FileUtils.extractVolumePath() and extractVolumeName() (shown in the r29 context) still run PATTERN_RELATIVE_PATH / PATTERN_VOLUME_NAME on the raw string without canonicalizing it. Of the three, only extractRelativePath() canonicalizes."
     ],
     "behavior_changes": [
      "If the supplied path cannot be canonicalized (IOException), SCAN_FILE_CALL now returns a Bundle with a null Intent.EXTRA_STREAM instead of attempting the scan on the raw path.",
      "SCAN_VOLUME_CALL returns Bundle.EMPTY instead of a newly allocated empty Bundle.",
      "When getCanonicalFile() fails, ModernMediaScanner.scanDirectory()/onDirectoryDirty() return silently (with an error log) and scanFile() returns null. Before the fix, the raw File was processed.",
      "onDirectoryDirty() now throws NullPointerException on a null argument (requireNonNull) and records the dirty state under the canonical directory path.",
      "FileUtils.getCanonicalPath() becomes public and @NonNull and throws IOException. FileUtils.getCanonicalFile(String) is a new public helper."
     ],
     "preconditions": [
      "The caller invokes ContentResolver.call() on the MediaStore authority with method MediaStore.SCAN_FILE_CALL and a path string as arg. The public wrapper MediaStore.scanFile(ContentResolver, File) is @SystemApi (per index search, apex/framework/java/android/provider/MediaStore.java). The material does not show what permission checks, if any, run before the switch in MediaProvider.call().",
      "Per index search, frameworks/base MtpDatabase.updateMediaStore and FileSystemProvider.updateMediaStore call MediaStore.scanFile.",
      "The scan runs after clearLocalCallingIdentity()/clearCallingIdentity(), so the path is accessed with MediaProvider's own identity rather than the caller's."
     ],
     "per_major": {
      "14": "same fix: commits e7561e10a58e (android-security-14.0.0_r1) and 0bbf3f4c3e75 (android-security-14.0.0_r2) are the same cherry-pick (Change-Id I61e77d69ae857984b819fa0ea27bec5c26a34842) and differ only in the FileUtils hunk offsets/base. At android-security-14.0.0_r29, 8/8 patched lines are present. Per index search, in android-14.0.0_r75 the SCAN_FILE_CALL body is in the helper MediaProvider.getResultForScanFile() with the same logic.",
      "15": "not affected (not listed by the bulletin); the patched code is present at android-security-15.0.0_r17 (8/8 lines).",
      "16": "not affected (not listed by the bulletin); the patched code is present at android-security-16.0.0_r8 (8/8 lines).",
      "17": "not affected (not listed by the bulletin); the patched code is present at android-security-17.0.0_r1 (8/8 lines)."
     },
     "backport_notes": [
      "Single commit touching three files; apply the three files together. MediaProvider.java depends on the new public FileUtils.getCanonicalFile(), and the getCanonicalPath() signature change (public, throws IOException) needs the matching extractRelativePath() update. In trees where the SCAN_FILE_CALL body has been moved into MediaProvider.getResultForScanFile(), apply the canonicalization there.",
      "MediaProvider is a mainline (APEX) module, so devices that receive module updates may get the fix through the module rather than through the platform tag (general platform knowledge)."
     ],
     "confidence": "high",
     "unknowns": [
      "What permission or identity checks, if any, run before the method switch in MediaProvider.call() for SCAN_FILE_CALL (the material shows only the case body).",
      "Which path-based checks inside ModernMediaScanner.Scan or downstream were bypassed by non-canonical paths, and exactly what information could be disclosed; Scan internals are not in the material.",
      "Whether MediaProvider.scanFile() delegates to ModernMediaScanner.scanFile(), which would determine whether internal callers get canonicalized.",
      "Whether a follow-up change under b/262244882 ever added the enforceCallingPermissionInternal() check described in the commit message."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40131": {
     "cve": "CVE-2023-40131",
     "summary": "Converts the two detached async-initialization threads spawned in the GpuService constructor into owned std::thread members and adds a GpuService destructor that joins them, so the threads can no longer run against a destroyed GpuService object.",
     "component": "platform/frameworks/native, services/gpuservice (GpuService)",
     "weakness": "heap use-after-free (detached thread outlives the object whose `this` it captured)",
     "root_cause": "In services/gpuservice/GpuService.cpp, GpuService::GpuService() created two std::thread objects (gpuMemAsyncInitThread and gpuWorkAsyncInitThread) whose lambdas captured `this` and dereferenced members (mGpuMem->initialize(), mGpuMemTracer->initialize(mGpuMem), mGpuWork->initialize()), then immediately called detach() on both. Nothing tied the lifetime of those threads to the GpuService object, so if the GpuService was destroyed (last sp<> reference dropped) before the initializers finished, the threads could access freed memory. The commit message states this heap-use-after-free was reported by libfuzzer.",
     "fix_analysis": [
      "services/gpuservice/include/gpuservice/GpuService.h (renamed from services/gpuservice/GpuService.h): adds `#include <thread>`, declares `~GpuService();`, and adds two private members `std::unique_ptr<std::thread> mGpuMemAsyncInitThread` and `std::unique_ptr<std::thread> mGpuWorkAsyncInitThread`.",
      "services/gpuservice/GpuService.cpp GpuService::GpuService(): replaces the local std::thread objects with `mGpuMemAsyncInitThread = std::make_unique<std::thread>(...)` and `mGpuWorkAsyncInitThread = std::make_unique<std::thread>(...)`; the detach() calls are removed so the threads stay joinable and owned by the object. Lambda bodies are unchanged.",
      "services/gpuservice/GpuService.cpp GpuService::~GpuService(): new destructor that calls mGpuWorkAsyncInitThread->join() then mGpuMemAsyncInitThread->join(). Because the destructor body runs before member destruction, mGpuMem, mGpuMemTracer and mGpuWork are still valid while the init threads finish, and the object memory is not released until both have joined.",
      "services/gpuservice/Android.bp: adds `export_include_dirs: [\"include\"]` to libgpuservice so the header is reachable from tests; services/gpuservice/main_gpuservice.cpp and GpuService.cpp update the include to \"gpuservice/GpuService.h\".",
      "services/gpuservice/tests/unittests/Android.bp and GpuServiceTest.cpp: new test onInitializeShouldNotCauseUseAfterFree creates a GpuService, clears the sp<> immediately, sleeps 3 seconds and passes if no crash; the test links libgpuservice."
     ],
     "completeness": [
      "Covers both detached threads created in the constructor (GpuMem/GpuMemTracer init and GpuWork init); no other detached threads appear in the shown GpuService code.",
      "Both thread members are unconditionally assigned in the constructor, so the unconditional join() calls in the destructor do not dereference a null unique_ptr in the shown code paths.",
      "The join is unconditional and has no timeout, so if an initialize() call never returns, GpuService destruction blocks indefinitely rather than freeing the object.",
      "The fix does not change what the initializer lambdas do or add any synchronization between the init threads and other GpuService methods that may run concurrently via binder while initialization is in progress; the material does not show whether such concurrent access exists."
     ],
     "behavior_changes": [
      "GpuService destruction now blocks until both asynchronous initialization threads have completed.",
      "The init threads are no longer detached; their std::thread objects live as members for the lifetime of the GpuService.",
      "GpuService.h moved to services/gpuservice/include/gpuservice/GpuService.h and is exported from libgpuservice via export_include_dirs; includers must use \"gpuservice/GpuService.h\".",
      "gpuservice_unittest gains a new test and a new shared_libs dependency on libgpuservice."
     ],
     "preconditions": [
      "A GpuService object must be destroyed while one of its constructor-spawned initialization threads (GpuMem::initialize, GpuMemTracer::initialize, GpuWork::initialize) is still running.",
      "In the production binary, main_gpuservice.cpp holds `sp<GpuService> gpuservice` for the lifetime of main() and registers it with servicemanager; the material does not show a production path that drops that reference early. The commit message states the bug was found by libfuzzer, and the new unit test exercises it by clearing the sp<> immediately after construction."
     ],
     "per_major": {
      "14": "same fix: commit a2030f098afa in android-security-14.0.0_r1 (and identical commit e562203ac243 in android-security-14.0.0_r2, same Change-Id I4d1d2d4658b575bf2c8f425f91f68f03114ad029), cherry-picked from 3c00cbc0f119. At android-security-14.0.0_r29 the shown context has the moved header, the ~GpuService() declaration, the <thread>/<memory> includes, export_include_dirs and the new test; 7/8 patched lines matched.",
      "15": "not affected per the bulletin; the patched region is present at android-security-15.0.0_r17 (7/8 lines matched).",
      "16": "not affected per the bulletin; the patched region is present at android-security-16.0.0_r8 (7/8 lines matched).",
      "17": "not affected per the bulletin; the patched region is present at android-security-17.0.0_r1 (7/8 lines matched)."
     },
     "backport_notes": [
      "The security-relevant change is self-contained in GpuService.h/GpuService.cpp: two unique_ptr<std::thread> members, removal of detach(), and a destructor that joins them. The header move to include/gpuservice/, the Android.bp export_include_dirs change and the new test exist for testability and can be adapted if the include layout differs.",
      "Only join thread members that the target branch's constructor actually assigns; an unconditional join() on a never-assigned unique_ptr would dereference null."
     ],
     "confidence": "high",
     "unknowns": [
      "How the libfuzzer harness caused the GpuService object to be destroyed, and whether any production path in the gpuservice process can destroy the service while initialization is in progress.",
      "Which single line of the 8-line patched region differs at the later security tags (14.0.0_r29, 15.0.0_r17, 16.0.0_r8, 17.0.0_r1); the shown context excerpts do not include the destructor body or the thread member declarations.",
      "Whether the originating AOSP commit 3c00cbc0f119 landed in 13 and 12/12L branches, which the bulletin lists as affected; the material only shows the 14 branch commits."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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-2023-40140": {
     "cve": "CVE-2023-40140",
     "summary": "android_view_InputDevice_create now gives the Java KeyCharacterMap a copy of the native KeyCharacterMap (std::make_shared<KeyCharacterMap>(*map)) instead of the shared_ptr it shares with the native InputDeviceInfo, so Java-side reads no longer hit the same instance that native code can modify.",
     "component": "platform/frameworks/base, core JNI for android.view.InputDevice / KeyCharacterMap (input)",
     "weakness": "race condition / concurrent access to a shared native object (read while being modified)",
     "root_cause": "In core/jni/android_view_InputDevice.cpp, android_view_InputDevice_create passed deviceInfo.getKeyCharacterMap() (a std::shared_ptr<KeyCharacterMap>) directly to android_view_KeyCharacterMap_create. As a result, the Java KeyCharacterMap object and the native key mappings shared one instance. The commit message says a read could be attempted while the mappings were being modified.",
     "fix_analysis": [
      "core/jni/android_view_InputDevice.cpp:android_view_InputDevice_create: obtain std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap(); if it is non-null, replace it with std::make_shared<KeyCharacterMap>(*map), a separate copy made with the KeyCharacterMap copy constructor.",
      "The call android_view_KeyCharacterMap_create(env, deviceInfo.getId(), ...) now receives map (the copy) instead of deviceInfo.getKeyCharacterMap(), so the Java-visible KeyCharacterMap no longer points to the native instance. A null map is still passed through as nullptr.",
      "Both commits (3839e41580d0 in android-security-14.0.0_r1 and 5ae8ed07ace7 in android-security-14.0.0_r2) have the same Change-Id I745008a0a8ea30830660c45dcebee917b3913d13 and an identical diff. This is one change appearing in two release tags, not a two-step fix."
     ],
     "completeness": [
      "Covers the single sharing point in android_view_InputDevice_create: every Java InputDevice created through this JNI path gets its own KeyCharacterMap copy.",
      "Adds no locking or synchronization on the native KeyCharacterMap itself; it only separates the Java object from the native one. Other native consumers that may read the shared map while it is being modified are not addressed (none are shown in the material).",
      "Relies on the KeyCharacterMap copy constructor producing an independent copy of all mapping state. The copy constructor is not in the material.",
      "Any other call sites of android_view_KeyCharacterMap_create that pass a shared native map are not touched by this diff. The material does not show whether such call sites exist."
     ],
     "behavior_changes": [
      "One extra KeyCharacterMap allocation and copy each time a Java InputDevice object is created.",
      "The Java KeyCharacterMap is now a copy taken at creation time, so later changes to the native instance are not reflected in an InputDevice object that already exists.",
      "No new error paths; null maps are still passed through as before."
     ],
     "preconditions": [
      "A Java android.view.InputDevice object must be created from a native InputDeviceInfo via android_view_InputDevice_create (declared in core/jni/android_view_InputDevice.h). Per index search, the concrete callers did not come up.",
      "The device must have a non-null native KeyCharacterMap, and the native side must modify that map while the Java side reads it. What triggers the native modification is not shown in the material."
     ],
     "per_major": {
      "14": "same fix: commit 3839e41580d0 in android-security-14.0.0_r1 and the identical commit 5ae8ed07ace7 in android-security-14.0.0_r2 (same Change-Id). At android-security-14.0.0_r29 the resolver matches 6/7 lines of the patched region, and the code context shows the full copy logic in place.",
      "15": "not affected (not listed by the bulletin); 6/7 lines of the patched region are present at android-security-15.0.0_r17.",
      "16": "not affected (not listed by the bulletin); 6/7 lines of the patched region are present at android-security-16.0.0_r8.",
      "17": "not affected (not listed by the bulletin); 6/7 lines of the patched region are present at android-security-17.0.0_r1."
     },
     "backport_notes": [
      "Single-file, self-contained change in core/jni/android_view_InputDevice.cpp. It assumes deviceInfo.getKeyCharacterMap() returns a std::shared_ptr<KeyCharacterMap> and that KeyCharacterMap can be copy-constructed. Check both on the target branch."
     ],
     "confidence": "high",
     "unknowns": [
      "Which native code path modifies the KeyCharacterMap concurrently, and what triggers it.",
      "The concrete callers of android_view_InputDevice_create, and whether android_view_KeyCharacterMap_create is called elsewhere with a shared native map.",
      "Whether the KeyCharacterMap copy constructor fully copies all internal state.",
      "How the fix was applied on Android 11, 12, 12L and 13, which the bulletin lists as affected. No commits for those branches are in the material, and neither is the type returned by getKeyCharacterMap() there.",
      "Why the resolver reports 6/7 lines on all branches when the code shown contains the full patched region."
     ],
     "meta": {
      "generated": "2026-10-04",
      "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."
     }
    }
   }
  },
  "15": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "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": {}
  },
  "16": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "tags": [],
   "unannounced": [],
   "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": {}
  },
  "17": {
   "counts": {
    "announced": 0,
    "certain": 0,
    "probable": 0,
    "not_published": 0,
    "not_covered": 0,
    "awaiting": 0
   },
   "advisories": [],
   "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": {}
  }
 },
 "bulletin": {
  "url": "https://source.android.com/docs/security/bulletin/2023-10-01",
  "latest_version": "1.0",
  "latest_revision": {
   "version": "1.0",
   "date": "October 2, 2023",
   "notes": "Bulletin published"
  },
  "revisions": [
   {
    "version": "1.0",
    "date": "October 2, 2023",
    "notes": "Bulletin published"
   }
  ],
  "tracked_since": "2026-09-30",
  "changes_since_tracking": [],
  "out_of_scope_rows": {
   "Arm components": 5,
   "MediaTek components": 3,
   "Qualcomm closed-source components": 14,
   "Qualcomm components": 3,
   "Unisoc components": 1
  },
  "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."
 }
}