Browse Source

chore: cherry-pick 176c526846cb from chromium (#36583)

chore: [20-x-y] cherry-pick 176c526846cb from chromium

Co-authored-by: electron-patch-conflict-fixer[bot] <83340002+electron-patch-conflict-fixer[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <[email protected]>
Pedro Pontes 2 years ago
parent
commit
ebe47b445f
2 changed files with 193 additions and 0 deletions
  1. 1 0
      patches/chromium/.patches
  2. 192 0
      patches/chromium/cherry-pick-176c526846cb.patch

+ 1 - 0
patches/chromium/.patches

@@ -145,6 +145,7 @@ cherry-pick-81cb17c24788.patch
 cherry-pick-9b3d0e2f1aab.patch
 cherry-pick-1894458e04a2.patch
 cherry-pick-6b4af5d82083.patch
+cherry-pick-176c526846cb.patch
 cherry-pick-65ad70274d4b.patch
 cherry-pick-f46db6aac3e9.patch
 cherry-pick-2ef09109c0ec.patch

+ 192 - 0
patches/chromium/cherry-pick-176c526846cb.patch

@@ -0,0 +1,192 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Robert Sesek <[email protected]>
+Date: Fri, 18 Nov 2022 19:31:38 +0000
+Subject: Fix a data race leading to use-after-free in mojo::ChannelMac
+ ShutDown
+
+(cherry picked from commit bd8a1e43aa93d5bb7674cb5a431e7375f7e2f192)
+
+Bug: 1378564
+Change-Id: I67041b1e2ef08dd0ee1ccbf6d534249c539b74db
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4027242
+Commit-Queue: Robert Sesek <[email protected]>
+Reviewed-by: Ken Rockot <[email protected]>
+Cr-Original-Commit-Position: refs/heads/main@{#1071700}
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4035114
+Commit-Queue: Rubber Stamper <[email protected]>
+Auto-Submit: Robert Sesek <[email protected]>
+Bot-Commit: Rubber Stamper <[email protected]>
+Cr-Commit-Position: refs/branch-heads/5359@{#881}
+Cr-Branched-From: 27d3765d341b09369006d030f83f582a29eb57ae-refs/heads/main@{#1058933}
+
+diff --git a/mojo/core/channel_mac.cc b/mojo/core/channel_mac.cc
+index 686dea5c783af06e41c290b03db251ca584d9a72..a24d5ab4ea9ae63f652acc2f903e577d45b1f0ee 100644
+--- a/mojo/core/channel_mac.cc
++++ b/mojo/core/channel_mac.cc
+@@ -25,6 +25,7 @@
+ #include "base/mac/scoped_mach_vm.h"
+ #include "base/message_loop/message_pump_for_io.h"
+ #include "base/task/current_thread.h"
++#include "base/thread_annotations.h"
+ #include "base/trace_event/typed_macros.h"
+ 
+ extern "C" {
+@@ -167,7 +168,10 @@ class ChannelMac : public Channel,
+         vm_allocate(mach_task_self(), &address, size,
+                     VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | VM_FLAGS_ANYWHERE);
+     MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_allocate";
+-    send_buffer_.reset(address, size);
++    {
++      base::AutoLock lock(write_lock_);
++      send_buffer_.reset(address, size);
++    }
+ 
+     kr = vm_allocate(mach_task_self(), &address, size,
+                      VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | VM_FLAGS_ANYWHERE);
+@@ -207,7 +211,11 @@ class ChannelMac : public Channel,
+ 
+     watch_controller_.StopWatchingMachPort();
+ 
+-    send_buffer_.reset();
++    {
++      base::AutoLock lock(write_lock_);
++      send_buffer_.reset();
++      reject_writes_ = true;
++    }
+     receive_buffer_.reset();
+     incoming_handles_.clear();
+ 
+@@ -315,7 +323,7 @@ class ChannelMac : public Channel,
+     SendPendingMessagesLocked();
+   }
+ 
+-  void SendPendingMessagesLocked() {
++  void SendPendingMessagesLocked() EXCLUSIVE_LOCKS_REQUIRED(write_lock_) {
+     // If a previous send failed due to the receiver's kernel message queue
+     // being full, attempt to send that failed message first.
+     if (send_buffer_contains_message_ && !reject_writes_) {
+@@ -342,7 +350,8 @@ class ChannelMac : public Channel,
+     }
+   }
+ 
+-  bool SendMessageLocked(MessagePtr message) {
++  bool SendMessageLocked(MessagePtr message)
++      EXCLUSIVE_LOCKS_REQUIRED(write_lock_) {
+     DCHECK(!send_buffer_contains_message_);
+     base::BufferIterator<char> buffer(
+         reinterpret_cast<char*>(send_buffer_.address()), send_buffer_.size());
+@@ -437,7 +446,8 @@ class ChannelMac : public Channel,
+     return MachMessageSendLocked(header);
+   }
+ 
+-  bool MachMessageSendLocked(mach_msg_header_t* header) {
++  bool MachMessageSendLocked(mach_msg_header_t* header)
++      EXCLUSIVE_LOCKS_REQUIRED(write_lock_) {
+     kern_return_t kr = mach_msg(header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+                                 header->msgh_size, 0, MACH_PORT_NULL,
+                                 /*timeout=*/0, MACH_PORT_NULL);
+@@ -659,7 +669,7 @@ class ChannelMac : public Channel,
+   }
+ 
+   // Marks the channel as unaccepting of new messages and shuts it down.
+-  void OnWriteErrorLocked(Error error) {
++  void OnWriteErrorLocked(Error error) EXCLUSIVE_LOCKS_REQUIRED(write_lock_) {
+     reject_writes_ = true;
+     io_task_runner_->PostTask(
+         FROM_HERE, base::BindOnce(&ChannelMac::OnError, this, error));
+@@ -701,17 +711,17 @@ class ChannelMac : public Channel,
+   // Lock that protects the following members.
+   base::Lock write_lock_;
+   // Whether writes should be rejected due to an internal error.
+-  bool reject_writes_ = false;
++  bool reject_writes_ GUARDED_BY(write_lock_) = false;
+   // IO buffer for sending Mach messages.
+-  base::mac::ScopedMachVM send_buffer_;
++  base::mac::ScopedMachVM send_buffer_ GUARDED_BY(write_lock_);
+   // If a message timed out during send in MachMessageSendLocked(), this will
+   // be true to indicate that |send_buffer_| contains a message that must
+   // be sent. If this is true, then other calls to Write() queue messages onto
+   // |pending_messages_|.
+-  bool send_buffer_contains_message_ = false;
++  bool send_buffer_contains_message_ GUARDED_BY(write_lock_) = false;
+   // When |handshake_done_| is false or |send_buffer_contains_message_| is true,
+   // calls to Write() will enqueue messages here.
+-  base::circular_deque<MessagePtr> pending_messages_;
++  base::circular_deque<MessagePtr> pending_messages_ GUARDED_BY(write_lock_);
+ };
+ 
+ }  // namespace
+diff --git a/mojo/core/channel_unittest.cc b/mojo/core/channel_unittest.cc
+index e9dee384440ee5a0500e86c522eef19c12bd8045..47422267cbaa0d3f90be6adc851a0651c1b74133 100644
+--- a/mojo/core/channel_unittest.cc
++++ b/mojo/core/channel_unittest.cc
+@@ -712,6 +712,69 @@ TEST(ChannelTest, SendToDeadMachPortName) {
+ }
+ #endif  // BUILDFLAG(IS_MAC)
+ 
++TEST(ChannelTest, ShutDownStress) {
++  base::test::SingleThreadTaskEnvironment task_environment(
++      base::test::TaskEnvironment::MainThreadType::IO);
++
++  // Create a second IO thread for Channel B.
++  base::Thread peer_thread("channel_b_io");
++  peer_thread.StartWithOptions(
++      base::Thread::Options(base::MessagePumpType::IO, 0));
++
++  // Create two channels, A and B, which run on different threads.
++  PlatformChannel platform_channel;
++
++  CallbackChannelDelegate delegate_a;
++  scoped_refptr<Channel> channel_a = Channel::Create(
++      &delegate_a, ConnectionParams(platform_channel.TakeLocalEndpoint()),
++      Channel::HandlePolicy::kRejectHandles,
++      task_environment.GetMainThreadTaskRunner());
++  channel_a->Start();
++
++  scoped_refptr<Channel> channel_b = Channel::Create(
++      nullptr, ConnectionParams(platform_channel.TakeRemoteEndpoint()),
++      Channel::HandlePolicy::kRejectHandles, peer_thread.task_runner());
++  channel_b->Start();
++
++  base::WaitableEvent go_event;
++
++  // Warm up the channel to ensure that A and B are connected, then quit.
++  channel_b->Write(Channel::Message::CreateMessage(0, 0));
++  {
++    base::RunLoop run_loop;
++    delegate_a.set_on_message(run_loop.QuitClosure());
++    run_loop.Run();
++  }
++
++  // Block the peer thread while some tasks are queued up from the test main
++  // thread.
++  peer_thread.task_runner()->PostTask(
++      FROM_HERE,
++      base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&go_event)));
++
++  // First, write some messages for Channel B.
++  for (int i = 0; i < 500; ++i) {
++    channel_b->Write(Channel::Message::CreateMessage(0, 0));
++  }
++
++  // Then shut down channel B.
++  channel_b->ShutDown();
++
++  // Un-block the peer thread.
++  go_event.Signal();
++
++  // And then flood the channel with messages. This will suss out data races
++  // during Channel B's shutdown, since Writes can happen across threads
++  // without a PostTask.
++  for (int i = 0; i < 1000; ++i) {
++    channel_b->Write(Channel::Message::CreateMessage(0, 0));
++  }
++
++  // Explicitly join the thread to wait for pending tasks, which may reference
++  // stack variables, to complete.
++  peer_thread.Stop();
++}
++
+ }  // namespace
+ }  // namespace core
+ }  // namespace mojo