Security: Incorrect size calculation when deserializing Mojo "Event" messages leading to OOB access |
|||||||||||||
Issue descriptionVULNERABILITY DETAILS Mojo IPC allows endpoints to communicate with one another, potentially across process boundaries. Each endpoint initially receives a handle to the broker host node, using which it can request subsequent "child" channels to be created (https://cs.chromium.org/chromium/src/mojo/edk/system/broker_messages.h?l=16). Once a child node is created, the node controller can register with the broker node, create subsequent ports, and send messages to its peers. Messages transferred over Mojo IPC conform to the message (https://docs.google.com/document/d/13pv9cFh5YKuBggDBQ1-AL8VReF-IYpFOFpRfvWFrwio) and wire (https://docs.google.com/document/d/1jNcsxOdO3Al52s6lIrMOOgY7KXB7TJ8wGGWstAHiTd8) formats. Among the supported message types, "EVENT_MESSAGE" is one of the most "complex", and can be used to encapsulate one of several types of events, including "user events", "port accepted", "port merge" and others. When such a message is received by a node controller, it deserializes the received bytes in order to extract the encoded event. This is done by calling the "NodeController::DeserializeEventMessage" method (note that this method is also called when broadcasts of type BROADCAST_EVENT are received). The above method calls into several others, before arriving at "Event::Deserialize" (https://cs.chromium.org/chromium/src/mojo/edk/system/ports/event.cc?l=87). Here's a snippet from that function: 1. ScopedEvent Event::Deserialize(const void* buffer, size_t num_bytes) { 2. if (num_bytes < sizeof(SerializedHeader)) 3. return nullptr; 4. 5. const auto* header = static_cast<const SerializedHeader*>(buffer); 6. const PortName& port_name = header->port_name; 7. const size_t data_size = num_bytes - sizeof(header); 8. switch (header->type) { 9. case Type::kUserMessage: 10. return UserMessageEvent::Deserialize(port_name, header + 1, data_size); 11. case Type::kPortAccepted: 12. return PortAcceptedEvent::Deserialize(port_name, header + 1, data_size); 13. ... 14. } 15. } As we can see above, the function first checks that the received message's size is large enough to contain the "SerializedHeader" structure. Then, it extracts the message type and sends the message to additional parsing in each of the specialised parsing methods. However, the message size passed into the parsing functions ("data_size") is incorrectly calculated at line 7 -- instead of using "sizeof(*header)" (24 bytes), it uses "sizeof(header)" (4 or 8 bytes). Consequently, each of the specialised parsing methods can access data OOB if the encoded data size was small enough to begin with. In most cases, this will lead to OOB reads, which may leak information to remote nodes if the OOB-read data is subsequently written back into a Mojo message. However, I think this can also be used in order to trigger memory corruption is some cases. For example, consider "UserMessageEvent::Deserialize": 1. ScopedEvent UserMessageEvent::Deserialize(const PortName& port_name, 2. const void* buffer, 3. size_t num_bytes) { 4. if (num_bytes < sizeof(UserMessageEventData)) 5. return nullptr; 6. 7. const auto* data = static_cast<const UserMessageEventData*>(buffer); 8. ... 9. auto event = 10. base::WrapUnique(new UserMessageEvent(port_name, data->sequence_num)); 11. event->ReservePorts(data->num_ports); 12. const auto* in_descriptors = 13. reinterpret_cast<const PortDescriptor*>(data + 1); 14. std::copy(in_descriptors, in_descriptors + data->num_ports, 15. event->port_descriptors()); 16 ... 17. } Under regular circumstances, the contents of the "data" buffer does not change during the function's execution. However, if "data->num_ports" is accessed OOB, it may have one value when reserving the port array (line 11) and another, larger value, when copying the ports into the event (lines 14-15), thereby triggering a buffer overflow. Since sending Mojo IPC messages doesn't require any capabilities, any process (including the renderer) can encode such a message and send it to any peer (including the browser), thereby triggering the above OOB accesses. VERSION Chromium 64.0.3282.0 64-bit Revision dd12859a9c856c6919cedf6c35d13b8b22af94e1-refs/heads/master@{#520743} OS Linux 4.4.0-97-generic REPRODUCTION CASE I'm attaching a patch that adds code to the renderer process which modifies a serialized event message into a malformed "observe proxy" message with a small size encoded in the serialized header. Applying the patch and running Chrome will trigger an OOB access. Here's the corresponding crash output from an ASAN build: ================================================================= ==236812==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60800017b600 at pc 0x55738286be69 bp 0x7f58714290b0 sp 0x7f5871428860 READ of size 16 at 0x60800017b600 thread T24 (Chrome_IOThread) #0 0x55738286be68 in __asan_memcpy _asan_rtl_:3 #1 0x557383b50fdc in ObserveProxyEvent <PATH>/src/out/asan/../../mojo/edk/system/ports/event.cc:297:7 #2 0x557383b50fdc in make_unique<mojo::edk::ports::ObserveProxyEvent, const mojo::edk::ports::PortName &, const mojo::edk::ports::NodeName &, const mojo::edk::ports::PortName &, const mojo::edk::ports::NodeName &, const mojo::edk::ports::PortName &> <PATH>/src/out/asan/../../buildtools/third_party/libc++/trunk/include/memory:3026:0 #3 0x557383b50fdc in Deserialize <PATH>/src/out/asan/../../mojo/edk/system/ports/event.cc:315:0 #4 0x557383b50fdc in mojo::edk::ports::Event::Deserialize(void const*, unsigned long) <PATH>/src/out/asan/../../mojo/edk/system/ports/event.cc:146:0 #5 0x55738cf9bd59 in mojo::edk::(anonymous namespace)::DeserializeEventMessage(mojo::edk::ports::NodeName const&, std::__1::unique_ptr<mojo::edk::Channel::Message, std::__1::default_delete<mojo::edk::Channel::Message> >) <PATH>/src/out/asan/../../mojo/edk/system/node_controller.cc:134:16 #6 0x55738cf9b2de in mojo::edk::NodeController::OnEventMessage(mojo::edk::ports::NodeName const&, std::__1::unique_ptr<mojo::edk::Channel::Message, std::__1::default_delete<mojo::edk::Channel::Message> >) <PATH>/src/out/asan/../../mojo/edk/system/node_controller.cc:1035:16 #7 0x55738cfb3f77 in mojo::edk::NodeChannel::OnChannelMessage(void const*, unsigned long, std::__1::vector<mojo::edk::ScopedPlatformHandle, std::__1::allocator<mojo::edk::ScopedPlatformHandle> >) <PATH>/src/out/asan/../../mojo/edk/system/node_channel.cc:710:18 #8 0x55738cfae12f in mojo::edk::Channel::OnReadComplete(unsigned long, unsigned long*) <PATH>/src/out/asan/../../mojo/edk/system/channel.cc:729:18 #9 0x55738cfd4405 in mojo::edk::(anonymous namespace)::ChannelPosix::OnFileCanReadWithoutBlocking(int) <PATH>/src/out/asan/../../mojo/edk/system/channel_posix.cc:312:14 #10 0x557388c55431 in base::MessagePumpLibevent::OnLibeventNotification(int, short, void*) <PATH>/src/out/asan/../../base/message_loop/message_pump_libevent.cc:0:13 #11 0x557388e08a08 in event_process_active <PATH>/src/out/asan/../../base/third_party/libevent/event.c:381:4 #12 0x557388e08a08 in event_base_loop <PATH>/src/out/asan/../../base/third_party/libevent/event.c:521:0 #13 0x557388c55ec7 in base::MessagePumpLibevent::Run(base::MessagePump::Delegate*) <PATH>/src/out/asan/../../base/message_loop/message_pump_libevent.cc:257:9 #14 0x557388cc8321 in base::RunLoop::Run() <PATH>/src/out/asan/../../base/run_loop.cc:114:14 #15 0x557384460e2f in content::BrowserThreadImpl::IOThreadRun(base::RunLoop*) <PATH>/src/out/asan/../../content/browser/browser_thread_impl.cc:248:11 #16 0x55738446148c in content::BrowserThreadImpl::Run(base::RunLoop*) <PATH>/src/out/asan/../../content/browser/browser_thread_impl.cc:283:14 #17 0x557388d5a3e9 in base::Thread::ThreadMain() <PATH>/src/out/asan/../../base/threading/thread.cc:338:3 #18 0x557388d487f4 in base::(anonymous namespace)::ThreadFunc(void*) <PATH>/src/out/asan/../../base/threading/platform_thread_posix.cc:75:13 #19 0x7f589e4fc183 in start_thread /build/eglibc-SvCtMH/eglibc-2.19/nptl/pthread_create.c:312:0 0x60800017b600 is located 0 bytes to the right of 96-byte region [0x60800017b5a0,0x60800017b600) allocated by thread T24 (Chrome_IOThread) here: #0 0x55738286d9fe in __interceptor_posix_memalign _asan_rtl_:3 #1 0x557388c3c34f in base::AlignedAlloc(unsigned long, unsigned long) <PATH>/src/out/asan/../../base/memory/aligned_memory.cc:31:7 #2 0x55738cfab204 in Message <PATH>/src/out/asan/../../mojo/edk/system/channel.cc:121:7 #3 0x55738cfab204 in Message <PATH>/src/out/asan/../../mojo/edk/system/channel.cc:78:0 #4 0x55738cfab204 in mojo::edk::Channel::Message::Message(unsigned long, unsigned long) <PATH>/src/out/asan/../../mojo/edk/system/channel.cc:64:0 #5 0x55738cfb3d96 in mojo::edk::NodeChannel::OnChannelMessage(void const*, unsigned long, std::__1::vector<mojo::edk::ScopedPlatformHandle, std::__1::allocator<mojo::edk::ScopedPlatformHandle> >) <PATH>/src/out/asan/../../mojo/edk/system/node_channel.cc:707:15 #6 0x55738cfae12f in mojo::edk::Channel::OnReadComplete(unsigned long, unsigned long*) <PATH>/src/out/asan/../../mojo/edk/system/channel.cc:729:18 #7 0x55738cfd4405 in mojo::edk::(anonymous namespace)::ChannelPosix::OnFileCanReadWithoutBlocking(int) <PATH>/src/out/asan/../../mojo/edk/system/channel_posix.cc:312:14 #8 0x557388c55431 in base::MessagePumpLibevent::OnLibeventNotification(int, short, void*) <PATH>/src/out/asan/../../base/message_loop/message_pump_libevent.cc:0:13 #9 0x557388e08a08 in event_process_active <PATH>/src/out/asan/../../base/third_party/libevent/event.c:381:4 #10 0x557388e08a08 in event_base_loop <PATH>/src/out/asan/../../base/third_party/libevent/event.c:521:0 #11 0x557388c55ec7 in base::MessagePumpLibevent::Run(base::MessagePump::Delegate*) <PATH>/src/out/asan/../../base/message_loop/message_pump_libevent.cc:257:9 #12 0x557388cc8321 in base::RunLoop::Run() <PATH>/src/out/asan/../../base/run_loop.cc:114:14 #13 0x557384460e2f in content::BrowserThreadImpl::IOThreadRun(base::RunLoop*) <PATH>/src/out/asan/../../content/browser/browser_thread_impl.cc:248:11 #14 0x55738446148c in content::BrowserThreadImpl::Run(base::RunLoop*) <PATH>/src/out/asan/../../content/browser/browser_thread_impl.cc:283:14 #15 0x557388d5a3e9 in base::Thread::ThreadMain() <PATH>/src/out/asan/../../base/threading/thread.cc:338:3 #16 0x557388d487f4 in base::(anonymous namespace)::ThreadFunc(void*) <PATH>/src/out/asan/../../base/threading/platform_thread_posix.cc:75:13 #17 0x7f589e4fc183 in start_thread /build/eglibc-SvCtMH/eglibc-2.19/nptl/pthread_create.c:312:0 Thread T24 (Chrome_IOThread) created by T0 (chrome) here: #0 0x557382855fbd in __interceptor_pthread_create _asan_rtl_:3 #1 0x557388d47b0a in base::(anonymous namespace)::CreateThread(unsigned long, bool, base::PlatformThread::Delegate*, base::PlatformThreadHandle*, base::ThreadPriority) <PATH>/src/out/asan/../../base/threading/platform_thread_posix.cc:114:13 #2 0x557388d596b4 in base::Thread::StartWithOptions(base::Thread::Options const&) <PATH>/src/out/asan/../../base/threading/thread.cc:112:15 #3 0x557384461dad in content::BrowserThreadImpl::StartWithOptions(base::Thread::Options const&) <PATH>/src/out/asan/../../content/browser/browser_thread_impl.cc:374:25 #4 0x5573844372c6 in content::BrowserMainLoop::CreateThreads() <PATH>/src/out/asan/../../content/browser/browser_main_loop.cc:1137:49 #5 0x55738509e448 in Run <PATH>/src/out/asan/../../base/callback.h:94:12 #6 0x55738509e448 in content::StartupTaskRunner::RunAllTasksNow() <PATH>/src/out/asan/../../content/browser/startup_task_runner.cc:45:0 #7 0x557384436075 in content::BrowserMainLoop::CreateStartupTasks() <PATH>/src/out/asan/../../content/browser/browser_main_loop.cc:963:25 #8 0x557384444ef1 in content::BrowserMainRunnerImpl::Initialize(content::MainFunctionParams const&) <PATH>/src/out/asan/../../content/browser/browser_main_runner.cc:119:17 #9 0x55738442f344 in content::BrowserMain(content::MainFunctionParams const&) <PATH>/src/out/asan/../../content/browser/browser_main.cc:42:32 #10 0x5573881fe727 in content::ContentMainRunnerImpl::Run() <PATH>/src/out/asan/../../content/app/content_main_runner.cc:728:12 #11 0x557388222261 in service_manager::Main(service_manager::MainParams const&) <PATH>/src/out/asan/../../services/service_manager/embedder/main.cc:456:29 #12 0x5573881fad60 in content::ContentMain(content::ContentMainParams const&) <PATH>/src/out/asan/../../content/app/content_main.cc:19:10 #13 0x557382899cbf in ChromeMain <PATH>/src/out/asan/../../chrome/app/chrome_main.cc:130:12 #14 0x7f58975bbf44 in __libc_start_main /build/eglibc-SvCtMH/eglibc-2.19/csu/libc-start.c:287:0 SUMMARY: AddressSanitizer: heap-buffer-overflow (<PATH>/src/out/asan/chrome+0x7161e68) Shadow bytes around the buggy address: 0x0c1080027670: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c1080027680: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c1080027690: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c10800276a0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c10800276b0: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c10800276c0:[fa]fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c10800276d0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c10800276e0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c10800276f0: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00 0x0c1080027700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c1080027710: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==236812==ABORTING This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.
,
Dec 15 2017
,
Dec 18 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/5f3e8cfd216315a60b1ea9ef64496cf4d827c51a commit 5f3e8cfd216315a60b1ea9ef64496cf4d827c51a Author: Ken Rockot <rockot@chromium.org> Date: Mon Dec 18 21:08:02 2017 Fix data size calculation in mojo event messages BUG= 794969 Change-Id: Ie4b8ca96aa04cd287ee02562fe92dd38b234790a Reviewed-on: https://chromium-review.googlesource.com/832958 Reviewed-by: Jay Civelli <jcivelli@chromium.org> Commit-Queue: Ken Rockot <rockot@chromium.org> Cr-Commit-Position: refs/heads/master@{#524794} [modify] https://crrev.com/5f3e8cfd216315a60b1ea9ef64496cf4d827c51a/mojo/edk/system/ports/event.cc
,
Dec 18 2017
Thanks for the detailed report!
,
Dec 18 2017
This bug requires manual review: M64 has already been promoted to the beta branch, so this requires manual review Please contact the milestone owner if you have questions. Owners: cmasso@(Android), cmasso@(iOS), kbleicher@(ChromeOS), abdulsyed@(Desktop) For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
,
Dec 18 2017
Seems like this just landed in trunk. Let's wait for results in Canary. rockot@ can you please verify it in canary tomorrow and comment if all looks good?
,
Dec 19 2017
,
Dec 19 2017
I think it looks good. No known problems from canary.
,
Dec 20 2017
Approving merge for M62. Branch:3282
,
Dec 20 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/1e2af58da91a6659ba1f52001230d20bec2364d4 commit 1e2af58da91a6659ba1f52001230d20bec2364d4 Author: Ken Rockot <rockot@chromium.org> Date: Wed Dec 20 07:24:04 2017 Fix data size calculation in mojo event messages BUG= 794969 TBR=rockot@chromium.org (cherry picked from commit 5f3e8cfd216315a60b1ea9ef64496cf4d827c51a) Change-Id: Ie4b8ca96aa04cd287ee02562fe92dd38b234790a Reviewed-on: https://chromium-review.googlesource.com/832958 Reviewed-by: Jay Civelli <jcivelli@chromium.org> Commit-Queue: Ken Rockot <rockot@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#524794} Reviewed-on: https://chromium-review.googlesource.com/835660 Reviewed-by: Ken Rockot <rockot@chromium.org> Cr-Commit-Position: refs/branch-heads/3282@{#306} Cr-Branched-From: 5fdc0fab22ce7efd32532ee989b223fa12f8171e-refs/heads/master@{#520840} [modify] https://crrev.com/1e2af58da91a6659ba1f52001230d20bec2364d4/mojo/edk/system/ports/event.cc
,
Dec 21 2017
Requesting stable merge due to severity.
,
Jan 16 2018
We're not planning any further M63 releases. Hence, rejecting merge to M63.
,
Jan 22 2018
,
Mar 1 2018
Hi, Has this bug been fixed in M64 stable? Also, has a CVE been assigned for the issue? If not, would it be possible to assign one? Thanks! Gal.
,
Mar 1 2018
Yes it's been fixed in M64. I don't know what you're asking re CVE though.
,
Mar 2 2018
,
Mar 27 2018
,
Mar 27 2018
This bug has been closed for more than 14 weeks. Removing security view restrictions. For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot |
|||||||||||||
►
Sign in to add a comment |
|||||||||||||
Comment 1 by cthomp@chromium.org
, Dec 14 2017Labels: Security_Severity-High Security_Impact-Stable OS-Android OS-Chrome OS-Fuchsia OS-Linux OS-Mac OS-Windows Pri-1
Owner: roc...@chromium.org
Status: Assigned (was: Unconfirmed)