Chrome · Sync
CVE-2026-17918
UAF in Sync
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/renderer/trusted_vault_encryption_keys_extension.cc |
modified |
Files Changed
chrome/renderer/trusted_vault_encryption_keys_extension.ccchrome/renderer/trusted_vault_encryption_keys_extension.h
Patch
From 52b54e757d9ddda84993f818e2a28a57680de94e Mon Sep 17 00:00:00 2001 From: Mikel Astiz <[email protected]> Date: Mon, 08 Jun 2026 01:43:38 -0700 Subject: [PATCH] [trusted vault] Prevent UAF in TrustedVaultEncryptionKeysExtension This patch modifies SetSyncEncryptionKeys to parse arguments safely and handle potential frame destruction during V8 object access. Before this patch, SetSyncEncryptionKeys parsed the encryption_keys argument using args->GetNext(&encryption_keys), which synchronously executed user-defined JS getters on the array indices. If the getter detached the frame, the extension object was destroyed, leading to a Use-After-Free when execution resumed. This patch fixes the issue by: 1. Extracting the encryption_keys argument as a raw v8::Local<v8::Value> first. 2. Parsing and converting the array elements to Mojo C++ structs in a helper function (ParseSyncEncryptionKeysMayDeleteFrame). 3. Binding the continuation (SetSyncEncryptionKeysContinue) using base::WeakPtr so it is not executed if the frame was destroyed during parsing. This approach is consistent with the existing implementation of SetClientEncryptionKeys. Fixed: 513127137 Change-Id: I99542fa1dcf40766ad4b83b8dfb37357a4a4a24d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7900081 Commit-Queue: Mikel Astiz <[email protected]> Reviewed-by: David Roger <[email protected]> Cr-Commit-Position: refs/heads/main@{#1643030} --- diff --git a/chrome/renderer/trusted_vault_encryption_keys_extension.cc b/chrome/renderer/trusted_vault_encryption_keys_extension.cc index 8cf1f0a..9296f41 100644 --- a/chrome/renderer/trusted_vault_encryption_keys_extension.cc +++ b/chrome/renderer/trusted_vault_encryption_keys_extension.cc @@ -73,6 +73,26 @@ return trusted_vault_keys; } +// Parses an array of ArrayBuffers passed to `setSyncEncryptionKeys()`. +// This method may run property callbacks during parsing, which could end up +// deleting the frame. +void ParseSyncEncryptionKeysMayDeleteFrame( + v8::Local<v8::Value> encryption_keys_value, + int32_t last_key_version, + base::OnceCallback< + void(std::optional<std::vector<chrome::mojom::TrustedVaultKeyPtr>>)> + callback) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::LocalVector<v8::ArrayBuffer> encryption_keys(isolate); + if (!gin::ConvertFromV8(isolate, encryption_keys_value, &encryption_keys)) { + DVLOG(1) << "invalid encryption key"; + std::move(callback).Run(std::nullopt); + return; + } + std::move(callback).Run( + SyncEncryptionKeysToTrustedVaultKeys(encryption_keys, last_key_version)); +} + // Parses an array of key objects passed to `setClientEncryptionKeys()`. // The members of each object are `epoch` integer and `key` ArrayBuffer. bool ParseTrustedVaultKeyArrayMayDeleteFrame( @@ -302,6 +322,10 @@ v8::HandleScope handle_scope(args->isolate()); + if (render_frame()->GetWebFrame()->MainWorldScriptContext().IsEmpty()) { + return; + } + v8::Local<v8::Function> callback; if (!args->GetNext(&callback)) { RecordCallToSetSyncEncryptionKeysToUma(ValidArgs::kInvalidArgs); @@ -318,17 +342,11 @@ return; } - v8::LocalVector<v8::ArrayBuffer> encryption_keys(args->isolate()); - if (!args->GetNext(&encryption_keys)) { + v8::Local<v8::Value> encryption_keys_value; + if (!args->GetNext(&encryption_keys_value) || + !encryption_keys_value->IsArray()) { RecordCallToSetSyncEncryptionKeysToUma(ValidArgs::kInvalidArgs); - DLOG(ERROR) << "Not array of strings"; - args->ThrowError(); - return; - } - - if (encryption_keys.empty()) { - RecordCallToSetSyncEncryptionKeysToUma(ValidArgs::kInvalidArgs); - DLOG(ERROR) << "Array of strings empty"; + DLOG(ERROR) << "Not array"; args->ThrowError(); return; } @@ -341,6 +359,34 @@ return; } + ParseSyncEncryptionKeysMayDeleteFrame( + encryption_keys_value, last_key_version, + base::BindOnce( + &TrustedVaultEncryptionKeysExtension::SetSyncEncryptionKeysContinue, + weak_ptr_factory_.GetWeakPtr(), args, std::move(callback), + std::move(gaia_id))); +} + +void TrustedVaultEncryptionKeysExtension::SetSyncEncryptionKeysContinue( + gin::Arguments* args, + v8::Local<v8::Function> callback, + std::string gaia_id, + std::optional<std::vector<chrome::mojom::TrustedVaultKeyPtr>> + encryption_keys) { + if (!encryption_keys) { + RecordCallToSetSyncEncryptionKeysToUma(ValidArgs::kInvalidArgs); + DLOG(ERROR) << "Can't parse encryption keys"; + args->ThrowError(); + return; + } + + if (encryption_keys->empty()) { + RecordCallToSetSyncEncryptionKeysToUma(ValidArgs::kInvalidArgs); + DLOG(ERROR) << "Array of strings empty"; + args->ThrowError(); + return; + } + auto global_callback = std::make_unique<v8::Global<v8::Function>>(args->isolate(), callback); @@ -353,9 +399,8 @@ std::vector< std::pair<std::string, std::vector<chrome::mojom::TrustedVaultKeyPtr>>> trusted_vault_keys; - trusted_vault_keys.emplace_back( - trusted_vault::kSyncSecurityDomainName, - SyncEncryptionKeysToTrustedVaultKeys(encryption_keys, last_key_version)); + trusted_vault_keys.emplace_back(trusted_vault::kSyncSecurityDomainName, + *std::move(encryption_keys)); remote_->SetEncryptionKeys( std::move(gaia_id), std::move(trusted_vault_keys), base::BindOnce( diff --git a/chrome/renderer/trusted_vault_encryption_keys_extension.h b/chrome/renderer/trusted_vault_encryption_keys_extension.h index 123e1d2..bb742fb 100644 --- a/chrome/renderer/trusted_vault_encryption_keys_extension.h +++ b/chrome/renderer/trusted_vault_encryption_keys_extension.h @@ -6,6 +6,7 @@ #define CHROME_RENDERER_TRUSTED_VAULT_ENCRYPTION_KEYS_EXTENSION_H_ #include <memory> +#include <vector> #include "base/memory/weak_ptr.h" #include "chrome/common/trusted_vault_encryption_keys_extension.mojom.h" @@ -45,6 +46,12 @@ void Install(); #if !BUILDFLAG(IS_ANDROID) void SetSyncEncryptionKeys(gin::Arguments* args); + void SetSyncEncryptionKeysContinue( + gin::Arguments* args, + v8::Local<v8::Function> callback, + std::string gaia_id, + std::optional<std::vector<chrome::mojom::TrustedVaultKeyPtr>> + encryption_keys); void SetClientEncryptionKeys(gin::Arguments* args); void SetClientEncryptionKeysContinue( gin::Arguments* args,
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page