April 16, 2020

GHSL-2020-037: Use after free in Chrome WebAudio

Man Yue Mo

Summary

UaF in DeferredTaskHandler::BreakConnections

Product

Chrome

CVE

CVE-2020-6428

Tested Version

Chromium version: master branch build e577636 Also tested on release google Chrome 80.3987.122 Operating System: linux 18.04

Details

When HandlePreRenderTask is called during the rendering, it will check whether any source node should be stopped[1]. If an AudioScheduleSourceNode, (e.g. ConstantSourceNode) is to be stopped at or before the frame [2], it will then call the AudioScheduledSourceHandler::Finish() method, which calls FinishWithoutOnEnded. This has 2 effects:

  1. It calls BaseAudioContext::NotifySourceNodeFinishedProcessing[3], which will add the AudioScheduledSourceHandler in the |finished_source_handlers_| vector, as raw pointer.
void BaseAudioContext::NotifySourceNodeFinishedProcessing(
    AudioHandler* handler) {
  DCHECK(IsAudioThread());

  GetDeferredTaskHandler().GetFinishedSourceHandlers()->push_back(handler);
}
  1. It sets the playback state of the AudioScheduledSourceHandler to FINISHED_STATE, allowing the AudioScheduledSourceNode to be deleted[4].

If a suspend is also scheduled at the same frame, we can then return to main thread and delete the AudioScheduleSourceNode. This is ok as the AudioScheduleSourceHandler is also being kept alive in |active_source_handlers_| when the start method of the source node is called[5], which is held in the BaseAudioContext.

void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) {
  DCHECK(IsMainThread());
  GraphAutoLocker locker(this);

  GetDeferredTaskHandler().GetActiveSourceHandlers()->insert(&node->Handler());
  node->Handler().MakeConnection();
}

When the BaseAudioContext is resumed, it will call the DeferredTaskHandler::BreakConnection method in the HandlePostRenderTasks method[6] to finish off with the AudioScheduleSourceNode:

void DeferredTaskHandler::BreakConnections() {
  ...
  wtf_size_t size = finished_source_handlers_.size();
  if (size > 0) {
    for (auto* finished : finished_source_handlers_) {
      active_source_handlers_.erase(finished);          //<-- finished is now free'd
      finished->BreakConnectionWithLock();              //<-- UaF
    }
    finished_source_handlers_.clear();
  }
}

The problem, however, is that BreakConnections first erase the handler before using it in the next line. As the handler is now only kept alive by |active_source_handlers_|, this deletes the handler and causes UaF.

  1. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/offline_audio_context.cc;l=413;drc=b892cf58e162a8f66cd76d7472f129fe0fb6a7d1;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

  2. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/constant_source_node.cc;l=117;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

  3. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc;l=242;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

  4. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc;l=243;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

  5. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc;l=199;drc=b892cf58e162a8f66cd76d7472f129fe0fb6a7d1;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

  6. https://source.chromium.org/chromium/chromium/src/+/71825939c432c440fa53ef4016372076e2c6114a:third_party/blink/renderer/modules/webaudio/offline_audio_context.cc;l=426;drc=b892cf58e162a8f66cd76d7472f129fe0fb6a7d1;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F

Impact

Use-after-free in renderer.

Coordinated Disclosure Timeline

Credit

This issue was discovered and reported by GHSL team member @m-y-mo (Man Yue Mo).

Contact

You can contact the GHSL team at securitylab@github.com, please include the GHSL-2020-037 in any communication regarding this issue.