[5] NetworkProcess null-deref on a BroadcastChannel message with a null name
Severity: Low | Component: WebKit NetworkProcess IPC surface | e85a1ea
Rated Low because the diff fixes a reliably reachable null-StringImpl dereference in the NetworkProcess driven by a forged IPC message; the faulting address carries no attacker-controlled data, so the observable effect is a controlled crash (availability) with no path to memory corruption, and reaching it already requires a compromised WebProcess.
NetworkBroadcastChannelRegistry uses the IPC-supplied channel name as a HashMap<String, ...> key in registerChannel() (via ensure()) and in unregisterChannel() / postMessage() (via find()). A compromised or malformed WebProcess could send a null String for the name; looking up a null String key dereferences a null StringImpl while hashing the key (StringHash::hash() calls key.impl()->hash()), crashing the network process before HashTable::validateKey() ever runs. The fix rejects a null name with a MESSAGE_CHECK in all three endpoints, matching the existing origin validation.
Source/WebKit/NetworkProcess/NetworkBroadcastChannelRegistry.cpp
void NetworkBroadcastChannelRegistry::registerChannel(IPC::Connection& connection, const WebCore::ClientOrigin& origin, const String& name)
{
MESSAGE_CHECK(isValidClientOrigin(origin), connection);
+ MESSAGE_CHECK(!name.isNull(), connection);
auto& channelsForOrigin = m_broadcastChannels.ensure(origin, [] { return NameToConnectionIdentifiersMap { }; }).iterator->value;
auto& connectionIdentifiersForName = channelsForOrigin.ensure(name, [] { return Vector<IPC::Connection::UniqueID> { }; }).iterator->value;
...
void NetworkBroadcastChannelRegistry::unregisterChannel(...) {
MESSAGE_CHECK(isValidClientOrigin(origin), connection);
+ MESSAGE_CHECK(!name.isNull(), connection);
...
void NetworkBroadcastChannelRegistry::postMessage(...) {
MESSAGE_CHECK_COMPLETION(isValidClientOrigin(origin), connection, completionHandler());
+ MESSAGE_CHECK_COMPLETION(!name.isNull(), connection, completionHandler());
LayoutTests/ipc/coreipc.js
case 'String':
+ if (argument === null)
+ return {value: null, type: 'String'};
Patch Details
registerChannel() and unregisterChannel() gain MESSAGE_CHECK(!name.isNull(), connection), and postMessage() gains MESSAGE_CHECK_COMPLETION(!name.isNull(), connection, completionHandler()), each placed immediately after the existing isValidClientOrigin(origin) check and before the String name is used as a HashMap key. The remaining diff is collateral: a new layout test driving the bug through the IPC testing API, and a one-line change to coreipc.js's ArgumentSerializer to allow serializing a null value for a String argument.
Missing null-validation of attacker-controlled IPC input before it is used as a hash-map key that dereferences its backing StringImpl during hashing.
Background
BroadcastChannel is a Web API letting same-origin browsing contexts message each other; the NetworkProcess maintains the central registry so messages route across WebProcesses. MESSAGE_CHECK is a WebKit IPC macro that validates an assertion on an incoming message and, on failure, terminates the offending connection rather than continuing. A WTF String wraps a refcounted StringImpl; a null String has a null impl(), and StringHash::hash() computes a key's hash by calling key.impl()->hash(). HashMap insertion/lookup hashes the key, and only later runs HashTable::validateKey(). The IPCTestingAPI is a test-only facility that lets a layout test synthesize raw IPC messages to a target process, used here to reproduce a message a compromised WebProcess could send.
Analysis
This is a null pointer dereference from unvalidated IPC input. Before the fix, NetworkBroadcastChannelRegistry trusted the IPC-supplied channel name and used it directly as a HashMap<String, ...> key without validating it was non-null. The endpoints validated the ClientOrigin via isValidClientOrigin but applied no equivalent check to name. When a null String is used as a key, the hash table must compute the key's hash before it can reach HashTable::validateKey(): StringHash::hash() calls key.impl()->hash(), and for a null String impl() returns nullptr, dereferencing a null StringImpl.
Aaa Aaaaaaaaa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaa Aaa Aaa Aaaaaa Aaaaaa Aaa Aaaa Aaaaaaaaaaaa Aaaa a Aaaaaaaaaaa Aaaaaaaaaa Aaa Aaa Aaa Aaaaaaaaaaaaaaa Aa Aaa Aaaaaa Aa Aaaaaaaa Aaaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa a Aaaaa Aaaaaaaaaaaaaa Aaa Aaaaa a Aaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaa Aaaaaa Aaaaaa Aaa Aaaa Aaaaaaa Aaa Aaaaaaaa Aaaaa Aa Aa Aaaaaaaaaaaaaaaaaaa Aaaa Aa Aaa Aaaaaaaa Aaaaaaaa Aa Aaaa Aaaa Aaa Aaaaaaaa Aaaaaa a Aaaaa Aaaaaaa Aa Aaaaaaaaa Aaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaa Aaaa Aaa Aaa Aaaaaaaa Aaaa a Aaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aa a Aaaaaaaa Aaaaaaaaa a Aaaaaaaaaaa Aaaaaaaaaa Aaaaaaa a Aaaa Aaaa Aaaaaaaa Aaaaa a Aaaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaa Aaa Aaa Aaaaaaaa Aaaaaaaa a a Aaaaaa Aa Aaaaaaa Aaaaaaaaa Aaaaaaaaaaaaa Aa Aaaa Aaa Aaaaa a Aaaaaaaaaaaaa Aa Aaaaaa Aaaaaaaaa
Aaa Aaaaaaaa Aaaa Aa Aaaaaa Aaaaaaa Aaa Aaaaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa a Aa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaaa Aaaaaa Aaa Aaaa Aaaaa Aa Aaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaa a Aaaaaaaaa Aaaa Aaaaaaaaaa Aaaaaaaaaa Aaaa Aaaaaa Aa Aaa Aaa Aaaaaaaaa Aaa Aa Aaaaaaaaa Aa Aaa Aaaaaaaaaa
Aaaaaaaaa Aaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aa a Aaaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaa Aaaaaaa Aa Aaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaa Aaaa Aa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaaa Aaaaaaa Aa Aaa Aaaaa Aaa Aaaaaaa Aaaaaaaaaa Aaa Aaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aa Aaa Aaaaaa
🔒Where exactly does the null-name crash occur, and why does the hash table's own validation never get a chance to catch it? The cross-process impact is assessed in depth.
Subscribe to read more
Audit directions
a Aaaaa Aaaaaaaa Aaaa Aaaaaaaa Aaa Aaaaa Aa a Aaaaaa Aaa Aaa a Aaaaaaa Aaaaa Aa a Aaaaaaa Aaa Aaaaaaa Aaaaaaaaaaaaa Aaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaa Aaaa Aa Aaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaa Aaaaa Aaa Aaaaaaaa Aaaa Aaaaa Aaaaaa Aaa Aaa Aaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaa Aaa Aa Aa Aaaaaaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaaaa a Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa
a Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaaa Aa a Aaaaaaa Aaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aa Aaa Aaaaaa Aaaa Aa Aaa Aaaaaaaa Aaaaaaaa Aaa Aaa Aaaaa Aaaaaa Aaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaa Aaa Aaaaa Aaaaaa Aaaaaa Aaaa Aaaa Aaaaaaaaaaa
a Aaaaaaaaaa Aa Aaaaaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaa Aaa Aaa Aaaaaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaa a Aaaaaa Aaaaa Aaaa Aaaaa Aaaaaa Aaaaaaaaa Aaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaaaaaaa Aaaaa Aaa Aaaaa Aaa Aa Aaaaaaa Aaaaa Aaaaaaaa
🔒Several reusable audit directions for IPC-keyed map handlers across the NetworkProcess, with concrete grep targets for finding sibling-field validation gaps.
Subscribe to read more