← All issues

[7] IndexedDB Connection/Transaction Identifier Confusion

Severity: High | Component: WebKit NetworkProcess | d854553

Rated High because the diff fixes broker-side IDB registries that dereferenced renderer-supplied identifiers without rebinding to the sender; the resulting primitive is cross-renderer (effectively cross-origin) read, write, and destroy of IndexedDB records.

NetworkStorageManager's IDB IPC handlers looked up IDBDatabaseConnectionIdentifier/IDBResourceIdentifier via IDBStorageRegistry keyed only by handle. Any WebContent process could submit another renderer's identifier and have the NetworkProcess act on it.

Source/WebKit/NetworkProcess/storage/IDBStorageRegistry.cpp

+bool IDBStorageRegistry::isValidConnectionForIPC(WebCore::IDBServer::UniqueIDBDatabaseConnection& databaseConnection, IPC::Connection& ipcConnection)
+{
+ auto connectionIdentifier = databaseConnection.connectionToClient().identifier();
+ auto it = m_connectionsToClient.find(connectionIdentifier);
+ if (it == m_connectionsToClient.end())
+ return true;
+ return it->value->ipcConnection() == ipcConnection.uniqueID();
+}
+SUPPRESS_NODELETE RefPtr<WebCore::IDBServer::UniqueIDBDatabaseConnection> IDBStorageRegistry::connection(WebCore::IDBDatabaseConnectionIdentifier identifier, IPC::Connection& ipcConnection)
+{
+ RefPtr databaseConnection = m_connections.get(identifier);
+ if (!databaseConnection)
+ return nullptr;
+ MESSAGE_CHECK_WITH_RETURN_VALUE(isValidConnectionForIPC(*databaseConnection, ipcConnection), ipcConnection, nullptr);
+ return databaseConnection;
+}

connection() and transaction() now take IPC::Connection& and call isValidConnectionForIPC(), which resolves the database connection's owning client and compares ipcConnection() against the sender's uniqueID(). Every IDB IPC handler (establishTransaction, commitTransaction, putOrAdd, getRecord, openCursor, iterateCursor, etc.) is updated to thread IPC::Connection& through.

Missing sender-to-resource binding at an IPC trust boundary, where opaque identifiers minted by the broker are dereferenced without verifying that the originating IPC connection owns them.

IndexedDB splits into a renderer-side WebCore layer and a NetworkProcess broker. IDBStorageRegistry holds m_connectionsToClient (keyed by IDBConnectionIdentifier, value records the originating IPC::Connection::UniqueID), m_connections (keyed by IDBDatabaseConnectionIdentifier), and m_transactions (keyed by IDBResourceIdentifier). IDBResourceIdentifier::connectionIdentifier() links back to a client connection. MESSAGE_CHECK terminates the offending child process on failure.

Identifiers handed back to a renderer are capabilities; the broker must rebind on every call. Before the fix, WebContent A could send establishTransaction / putOrAdd / getRecord carrying WebContent B's identifier and have NetworkProcess execute against B's connection.

Read paths (getRecord, getAllRecords, getCount, openCursor, iterateCursor) exfiltrate B's records to A via the connection-to-client identifier embedded in the request; write paths (putOrAdd, deleteRecord, clearObjectStore, deleteObjectStore) corrupt B's databases; version-change handlers (didFireVersionChangeEvent, abortOpenAndUpgradeNeeded) perturb B's upgrade lifecycle. The primitive is logical, not memory-safety: the registry holds WeakPtrs and the looked-up objects are well-typed, so there's no UAF or type confusion — the impact is at the confidentiality and integrity layer. Notably, the commit pushes the check down into the lookup choke-point intentionally "so we don't forget to add such MESSAGE_CHECK when introducing new IPC."

This vulnerability weakens the IPC trust boundary that isolates one WebContent's IndexedDB state from another's — a same-origin/origin-partitioning bypass at the storage layer.