Skip to content

Commit

Permalink
[CoreIPC] heap-use-after-free in WebCore::MockMediaSourcePrivate::mar…
Browse files Browse the repository at this point in the history
…kEndOfStream

rdar://115982856

Reviewed by Jean-Yves Avenard and Eric Carlson.

Error only hit in internal testing. Object was referenced after deletion. Updated `MockMediaPlayer` to use weak pointer for `m_player` instead of reference and added checks to methods to check that `m_player` exists before trying to read/write

* Source/WebCore/platform/mock/mediasource/MockMediaSourcePrivate.cpp: added check that `m_player` exists before accessing
* Source/WebCore/platform/mock/mediasource/MockMediaSourcePrivate.h: changed `m_player` to weak pointer instead of a reference
* Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp:
(WebCore::MockSourceBufferPrivate::readyState const):
(WebCore::MockSourceBufferPrivate::setReadyState):

Canonical link: https://commits.webkit.org/267815.570@safari-7617-branch
  • Loading branch information
NKRosario authored and cdumez committed Nov 16, 2023
1 parent 5ebdf97 commit fc6f620
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 14 deletions.
196 changes: 196 additions & 0 deletions LayoutTests/ipc/argumentParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
let knownSizes = {
"unsigned": 4,
"int": 4,
"uint8_t": 1,
"int8_t": 1,
"uint64_t": 8,
"int64_t": 8,
"uint32_t": 4,
"int32_t": 4,
"uint16_t": 2,
"int16_t": 2,
"double": 8,
"float": 4,
"bool": 1,
"size_t": 8,
"unsigned short": 2,
"long long": 8,
"UUID": 16
};


// manual additions
$F.fuzzerTypeInfo = {};
$F.fuzzerTypeInfo['WebCore::DOMCacheEngine::CacheIdentifierOrError'] = [{'type': 'Expected<WebCore::DOMCacheEngine::CacheIdentifierOperationResult, Error>'}];

let ArgumentParser = {};
function align(pos, granularity) {
if(pos%granularity) {
pos = pos + granularity-(pos%granularity);
}
return pos;
}

ArgumentParser.parseMessage = function(msg, proc) {
let buffer = new Uint8Array(msg.buffer);
window.bada = buffer;
let name = msg.description;
let args = IPCmessages[name].arguments;
let results = {};
try {
ArgumentParser.parse(buffer, args, 0x10 /*skip message header*/, results);
if(proc) results[proc + "Connection"] = [ msg.destinationID ];
let clz = msg.description.substring(0, msg.description.indexOf("_"));
if(proc) results["clz_" + clz + "Connection"] = [ msg.destinationID ];
//$F.exec.log("//blam: " + msg.destinationID);
if($F.rand && $F.rand.chance(16)) {
//$F.exec.log("// focus on " + clz);
$F.focusClass = clz;
}
} catch (e) {
//$F.exec.log("//Bug: " + e);
}
return results;
}

ArgumentParser.parseMessageFromReply = function(args, buffer) {
buffer = new Uint8Array(buffer);
let results = {};
try {
ArgumentParser.parse(buffer, args, 0x10/*skip message header*/, results);
} catch (e) {
//$F.exec.log("//Bug: " + e);
}
return results;
}

function addResult(results, t, id) {
if(t in results) {
results[t].push(id);
} else {
results[t] = [ id ];
}
}

ArgumentParser.parse = function(buf, args, pos, results) {
if(args == null) return -1;
if(args.length == 0) return pos;
let arg = args.shift();
let t = arg['type'];
//$F.exec.log("// next " + t + " -> " + arg['optional']);
if(arg['optional']) {
let haz = !!buf[pos];
pos += 1
if(!haz) {
//$F.exec.log("// skipping optional argument")
return ArgumentParser.parse(buf, args, pos, results);
}
}
if (processQualified.includes(t)) {
pos = align(pos, 8);
if(pos + 16 > buf.length) {
//$F.exec.log("// Message too small to contain identifier")
return -1;
}
let view = new DataView(buf.buffer, pos);
let id1 = view.getBigUint64(0, true);
let id2 = view.getBigUint64(8, true);
addResult(results, t, id1);
addResult(results, "WebCore::ProcessIdentifier", id2);
return ArgumentParser.parse(buf, args, pos + 16, results);
} else if ( IPCobjectIdentifiers.includes(t) ) {
pos = align(pos, 8);
if(pos + 8 > buf.length) {
//$F.exec.log("// Message too small to contain identifier")
return -1;
}
let view = new DataView(buf.buffer, pos);
let id = view.getBigUint64(0, true);
//$F.exec.log("// Got Identifier '" + t + "': " + id);
//$F.objstore.addObj(t, id);
addResult(results, t, id);
return ArgumentParser.parse(buf, args, pos + 8, results);
} else if ( knownSizes[t] ) {
pos = align(pos, knownSizes[t]);
pos += knownSizes[t];
return ArgumentParser.parse(buf, args, pos, results);
} else if ( IPCserializedEnumInfo[t] ) {
let enumInfo = IPCserializedEnumInfo[t];
pos = align(pos, enumInfo.size);
pos += enumInfo.size;
return ArgumentParser.parse(buf, args, pos, results);
} else if ( $F.fuzzerTypeInfo[t] ) {
pos = ArgumentParser.parse(buf, $F.fuzzerTypeInfo[t], pos, results);
if(pos==-1) return -1;
return ArgumentParser.parse(buf, args, pos, results);
} else if ( IPCserializedTypeInfo[t] ) {
let typeInfo = IPCserializedTypeInfo[t];
let subArgs = [];
for (const element of typeInfo) {
subArgs.push(element);
}
pos = ArgumentParser.parse(buf, subArgs, pos, results);
if(pos==-1) return -1;
return ArgumentParser.parse(buf, args, pos, results);
} else if ( t == "String" || t == "URL" ) {
pos = align(pos, 4);
let view = new DataView(buf.buffer, pos);
let strLen = view.getUint32(0, true);
pos += 4;
if ( buf[pos++] == 0x01 ) {
let str = "";
for(let i=0; i<strLen; i++) {
str += String.fromCharCode(buf[pos++]);
}
return ArgumentParser.parse(buf, args, pos, results);
} else {
// Other encodings are not supported
return -1;
}
} else if ( t.indexOf("std::pair<") == 0) {
let types = t.substr(10).split(", ");
for(const nextt of types) {
let contentArg = {'type': nextt};
pos = ArgumentParser.parse(buf, [ contentArg ], pos, results);
if(pos==-1) return -1;
}
return ArgumentParser.parse(buf, args, pos, results);
} else if ( t.indexOf("Expected<") == 0) {
let haz = !!buf[pos];
pos += 1
if(haz) {
let expected_type = t.substr(9).split(",")[0];
let contentArg = {'type': expected_type};
pos = ArgumentParser.parse(buf, [ contentArg ], pos, results);
if (pos == -1) {
return -1;
}
return ArgumentParser.parse(buf, args, pos, results);
} else {
return -1;
}
} else if ( t.indexOf("Vector<") == 0 ) {
pos = align(pos, 8);
if(pos + 8 > buf.length) {
//$F.exec.log("// Message too small to contain identifier")
return -1;
}
let view = new DataView(buf.buffer, pos);
let id = view.getBigUint64(0, true);
pos += 8;
if(id > 0x1000) {
return -1;
}
let contentArg = {'type': t.substring(7, t.length-1)};
for(let x = 0; x<id; x++) {
pos = ArgumentParser.parse(buf, [ contentArg ], pos, results);
if (pos == -1) {
return -1
}
}
return ArgumentParser.parse(buf, args, pos, results);
} else {
//$F.exec.log("//[x] could not parse '"+t+"'");
}
return -1;
}
148 changes: 148 additions & 0 deletions LayoutTests/ipc/fuzz_tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
if(!window.$F) {
$F={};
$F.enableListener= true;
}

if(window.testRunner) {
testRunner.waitUntilDone();
}

if (window.IPC) {
// Cache to avoid context switches from JavaScript to Native
const IPCmessages = JSON.parse(JSON.stringify(IPC.messages));
const IPCobjectIdentifiers = JSON.parse(JSON.stringify(IPC.objectIdentifiers));
IPCobjectIdentifiers.push("PAL::SessionID");
IPCobjectIdentifiers.push("WebCore::ProcessIdentifier");
IPCobjectIdentifiers.push("WebKit::RemoteMediaSourceIdentifier");
IPCobjectIdentifiers.push("WebCore::MediaPlayerIdentifier");
IPCobjectIdentifiers.push("IPC::AsyncReplyID");
IPCobjectIdentifiers.push("ActivityStateChangeID");
IPCobjectIdentifiers.push("WebKit::EditorStateIdentifier");
IPCobjectIdentifiers.push("WebCore::PageOverlay::PageOverlayID");
IPCobjectIdentifiers.push("WebKit::DisplayLinkObserverID");
IPCobjectIdentifiers.push("WebKit::TextCheckerRequestID");
IPCobjectIdentifiers.push("WebKit::RemoteCDMIdentifier");
IPCobjectIdentifiers.push("WebKit::RemoteCDMInstanceIdentifier");
IPCobjectIdentifiers.push("WebKit::RemoteCDMInstanceSessionIdentifier");
IPCobjectIdentifiers.push("WebKit::RemoteSourceBufferIdentifier");

const IPCserializedTypeInfo = JSON.parse(JSON.stringify(IPC.serializedTypeInfo));
const IPCserializedEnumInfo = JSON.parse(JSON.stringify(IPC.serializedEnumInfo));

const processQualified = ["WebCore::FrameIdentifier","WebCore::ScriptExecutionContextIdentifier","WebCore::PolicyCheckIdentifier","WebCore::WebLockIdentifier","WebCore::PlatformLayerIdentifier","WebCore::BackForwardItemIdentifier","WebCore::SharedWorkerObjectIdentifier"];

$F.GPUOutgoingHandler = {};
$F.UIOutgoingHandler = {};
$F.UIIncomingHandler = {};
$F.GPUIncomingHandler = {};
$F.NetworkingOutgoingHandler = {};
$F.NetworkingIncomingHandler = {};

function shouldDiscard(args) {
for(let a of args) {
if(Array.isArray(a)) {
if(shouldDiscard(a)) return true;
} else {
if(a['type']) {
if(a['type'] == 'FrameID') {
if(!Array.isArray(a['value'])) return true;
} else if(a['type'] == 'Vector') {
if(shouldDiscard(a['value'])) return true;
}
}
}
}
return false;
}

$F.sendMessage = (process, connId, name, args) => {
if(shouldDiscard(args)) return;
if(window.$F) $F.enableListener = false;
try{
return IPC.sendMessage(process, connId, name, args);
}catch(e) {
$vm.print("[-] send exception: " + e);
}
finally {
$F.enableListener = true;
}
}
$F.sendSyncMessage = (process, connId, name, timeout, args) => {
if(shouldDiscard(args)) return;
if(window.$F) $F.enableListener = false;
try{
return IPC.sendSyncMessage(process, connId, name, timeout, args);
}catch(e) {
$vm.print("[-] sync send exception: " + e);
}
finally {
$F.enableListener = true;
}
}

IPC.addOutgoingMessageListener("GPU", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.GPUOutgoingHandler) {
let func = $F.GPUOutgoingHandler[name];
delete $F.GPUOutgoingHandler[name];
func(msg);
}
}
});

IPC.addOutgoingMessageListener("UI", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.UIOutgoingHandler) {
let func = $F.UIOutgoingHandler[name];
delete $F.UIOutgoingHandler[name];
func(msg);
}
}
});

IPC.addIncomingMessageListener("GPU", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.GPUIncomingHandler) {
let func = $F.GPUIncomingHandler[name];
delete $F.GPUIncomingHandler[name];
func(msg);
}
}
});

IPC.addIncomingMessageListener("UI", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.UIIncomingHandler) {
let func = $F.UIIncomingHandler[name];
delete $F.UIIncomingHandler[name];
func(msg);
}
}
});

IPC.addOutgoingMessageListener("Networking", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.NetworkingOutgoingHandler) {
let func = $F.NetworkingOutgoingHandler[name];
delete $F.NetworkingOutgoingHandler[name];
func(msg);
}
}
});

IPC.addIncomingMessageListener("Networking", (msg) => {
if(window.$F && $F.enableListener) {
let name = msg.name;
if (name in $F.NetworkingIncomingHandler) {
let func = $F.NetworkingIncomingHandler[name];
delete $F.NetworkingIncomingHandler[name];
func(msg);
}
}
});
}
1 change: 1 addition & 0 deletions LayoutTests/ipc/media-player-invalid-test-expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This test passes if it does not crash.
40 changes: 40 additions & 0 deletions LayoutTests/ipc/media-player-invalid-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html><!-- webkit-test-runner [ IPCTestingAPIEnabled=true ] -->
<html><head>
<script src="fuzz_tools.js"></script>
<script src="argumentParser.js"></script>
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
setTimeout(() => {
testRunner.notifyDone();
}, 2000);
testRunner.dumpAsText();
}

function fuzz() {
if (window.IPC && IPC) {
$F.GPUOutgoingHandler[IPC.messages.RemoteMediaPlayerManagerProxy_CreateMediaPlayer.name]=parseMessage;
video=document.createElement('video');
video.src='abc';
}
}
function parseMessage(msg) {
let parseResults=ArgumentParser.parseMessage(msg,'GPU');
o17=parseResults['WebCore::MediaPlayerIdentifier'][0];
o19=parseResults['GPUConnection'][0];
o20=parseResults['clz_RemoteMediaPlayerManagerProxyConnection'][0];
IPC.sendMessage('GPU',o19,IPCmessages.GPUConnectionToWebProcess_EnableMockMediaSource.name,[]);
window.setTimeout(timeout_159,200);
}
function timeout_159() {
IPC.sendMessage('GPU',o20,IPCmessages.RemoteMediaPlayerManagerProxy_CreateMediaPlayer.name,[{type: 'uint64_t',value: o17},{type: 'uint8_t',value: 8},{type: 'String',value: ''},{type: 'String',value: ''},{type: 'String',value: ''},{type: 'String',value: ''},{type: 'Vector',value: [[{type: 'String',value: "pageUp:"}],[{type: 'String',value: ''}]]},{type: 'bool',value: 1},{type: 'Vector',value: [[{type: 'String',value: ''}],[{type: 'String',value: ''}],[{type: 'String',value: ''}],[{type: 'String',value: "video/mpeg"}],[{type: 'String',value: ''}],[{type: 'String',value: "http"}],[{type: 'String',value: "a"}],[{type: 'String',value: ''}],[{type: 'String',value: ''}]]},{type: 'bool',value: 0},{type: 'bool',value: 1},{type: 'Vector',value: []},{type: 'bool',value: 1},{type: 'Vector',value: [[{type: 'uint32_t',value: 476}],[{type: 'uint32_t',value: 555}]]},{type: 'bool',value: 0},{type: 'uint32_t',value: 697},{type: 'uint32_t',value: 644},{type: 'uint32_t',value: 543},{type: 'uint32_t',value: 460},{type: 'Vector',value: [[{type: 'String',value: "page-6"}],[{type: 'String',value: ''}]]},{type: 'Vector',value: []},{type: 'uint8_t',value: 0},{type: 'String',value: "file:///tmp/ipcfuzz"},{type: 'String',value: "file:///etc/passwd"},{type: 'bool',value: 1},{type: 'uint16_t',value: 47385},{type: 'uint32_t',value: 174},{type: 'uint32_t',value: 632},{type: 'float',value: 341},{type: 'float',value: 1316.9076250987036},{type: 'uint64_t',value: 1009},{type: 'bool',value: 1},{type: 'bool',value: 1},{type: 'bool',value: 0},{type: 'bool',value: 0},{type: 'bool',value: 1}]);
o112=2395;
IPC.sendMessage("GPU",o17,IPC.messages.RemoteMediaPlayerProxy_LoadMediaSource.name, [{type:"String", value: ''},{type:"String", value: ''},{type: "bool", value: 0},{type: "uint64_t", value: o112}]);
IPC.sendMessage('GPU',o20,IPC.messages.RemoteMediaPlayerManagerProxy_DeleteMediaPlayer.name,[{type: 'uint64_t',value: o17}]);
IPC.sendMessage('GPU',o112,IPCmessages.RemoteMediaSourceProxy_MarkEndOfStream.name,[{type: 'bool',value: 0}]);
}
</script></head>
<body onload='fuzz()'>
This test passes if it does not crash.
</body>
</html>
Loading

0 comments on commit fc6f620

Please sign in to comment.