1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/public/browser/browser_message_filter.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/command_line.h" 10 #include "base/logging.h" 11 #include "base/process/kill.h" 12 #include "base/process/process_handle.h" 13 #include "base/task_runner.h" 14 #include "content/public/browser/user_metrics.h" 15 #include "content/public/common/content_switches.h" 16 #include "content/public/common/result_codes.h" 17 #include "ipc/ipc_sync_message.h" 18 19 using content::BrowserMessageFilter; 20 21 namespace content { 22 23 class BrowserMessageFilter::Internal : public IPC::ChannelProxy::MessageFilter { 24 public: 25 explicit Internal(BrowserMessageFilter* filter) : filter_(filter) {} 26 27 private: 28 virtual ~Internal() {} 29 30 // IPC::ChannelProxy::MessageFilter implementation: 31 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE { 32 filter_->channel_ = channel; 33 filter_->OnFilterAdded(channel); 34 } 35 36 virtual void OnFilterRemoved() OVERRIDE { 37 filter_->OnFilterRemoved(); 38 } 39 40 virtual void OnChannelClosing() OVERRIDE { 41 filter_->channel_ = NULL; 42 filter_->OnChannelClosing(); 43 } 44 45 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { 46 filter_->peer_pid_ = peer_pid; 47 filter_->OnChannelConnected(peer_pid); 48 } 49 50 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 51 BrowserThread::ID thread = BrowserThread::IO; 52 filter_->OverrideThreadForMessage(message, &thread); 53 54 if (thread == BrowserThread::IO) { 55 scoped_refptr<base::TaskRunner> runner = 56 filter_->OverrideTaskRunnerForMessage(message); 57 if (runner.get()) { 58 runner->PostTask( 59 FROM_HERE, 60 base::Bind( 61 base::IgnoreResult(&Internal::DispatchMessage), this, message)); 62 return true; 63 } 64 return DispatchMessage(message); 65 } 66 67 if (thread == BrowserThread::UI && 68 !BrowserMessageFilter::CheckCanDispatchOnUI(message, filter_)) { 69 return true; 70 } 71 72 BrowserThread::PostTask( 73 thread, FROM_HERE, 74 base::Bind( 75 base::IgnoreResult(&Internal::DispatchMessage), this, message)); 76 return true; 77 } 78 79 // Dispatches a message to the derived class. 80 bool DispatchMessage(const IPC::Message& message) { 81 bool message_was_ok = true; 82 bool rv = filter_->OnMessageReceived(message, &message_was_ok); 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || rv) << 84 "Must handle messages that were dispatched to another thread!"; 85 if (!message_was_ok) { 86 content::RecordAction(UserMetricsAction("BadMessageTerminate_BMF")); 87 filter_->BadMessageReceived(); 88 } 89 90 return rv; 91 } 92 93 scoped_refptr<BrowserMessageFilter> filter_; 94 95 DISALLOW_COPY_AND_ASSIGN(Internal); 96 }; 97 98 BrowserMessageFilter::BrowserMessageFilter() 99 : internal_(NULL), channel_(NULL), 100 #if defined(OS_WIN) 101 peer_handle_(base::kNullProcessHandle), 102 #endif 103 peer_pid_(base::kNullProcessId) { 104 } 105 106 base::ProcessHandle BrowserMessageFilter::PeerHandle() { 107 #if defined(OS_WIN) 108 base::AutoLock lock(peer_handle_lock_); 109 if (peer_handle_ == base::kNullProcessHandle) 110 base::OpenPrivilegedProcessHandle(peer_pid_, &peer_handle_); 111 112 return peer_handle_; 113 #else 114 base::ProcessHandle result = base::kNullProcessHandle; 115 base::OpenPrivilegedProcessHandle(peer_pid_, &result); 116 return result; 117 #endif 118 } 119 120 121 void BrowserMessageFilter::OnDestruct() const { 122 delete this; 123 } 124 125 bool BrowserMessageFilter::Send(IPC::Message* message) { 126 if (message->is_sync()) { 127 // We don't support sending synchronous messages from the browser. If we 128 // really needed it, we can make this class derive from SyncMessageFilter 129 // but it seems better to not allow sending synchronous messages from the 130 // browser, since it might allow a corrupt/malicious renderer to hang us. 131 NOTREACHED() << "Can't send sync message through BrowserMessageFilter!"; 132 return false; 133 } 134 135 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 136 BrowserThread::PostTask( 137 BrowserThread::IO, 138 FROM_HERE, 139 base::Bind(base::IgnoreResult(&BrowserMessageFilter::Send), this, 140 message)); 141 return true; 142 } 143 144 if (channel_) 145 return channel_->Send(message); 146 147 delete message; 148 return false; 149 } 150 151 base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage( 152 const IPC::Message& message) { 153 return NULL; 154 } 155 156 bool BrowserMessageFilter::CheckCanDispatchOnUI(const IPC::Message& message, 157 IPC::Sender* sender) { 158 #if defined(OS_WIN) 159 // On Windows there's a potential deadlock with sync messsages going in 160 // a circle from browser -> plugin -> renderer -> browser. 161 // On Linux we can avoid this by avoiding sync messages from browser->plugin. 162 // On Mac we avoid this by not supporting windowed plugins. 163 if (message.is_sync() && !message.is_caller_pumping_messages()) { 164 // NOTE: IF YOU HIT THIS ASSERT, THE SOLUTION IS ALMOST NEVER TO RUN A 165 // NESTED MESSAGE LOOP IN THE RENDERER!!! 166 // That introduces reentrancy which causes hard to track bugs. You should 167 // find a way to either turn this into an asynchronous message, or one 168 // that can be answered on the IO thread. 169 NOTREACHED() << "Can't send sync messages to UI thread without pumping " 170 "messages in the renderer or else deadlocks can occur if the page " 171 "has windowed plugins! (message type " << message.type() << ")"; 172 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); 173 reply->set_reply_error(); 174 sender->Send(reply); 175 return false; 176 } 177 #endif 178 return true; 179 } 180 181 void BrowserMessageFilter::BadMessageReceived() { 182 CommandLine* command_line = CommandLine::ForCurrentProcess(); 183 184 if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { 185 base::KillProcess(PeerHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE, 186 false); 187 } 188 } 189 190 BrowserMessageFilter::~BrowserMessageFilter() { 191 #if defined(OS_WIN) 192 if (peer_handle_ != base::kNullProcessHandle) 193 base::CloseProcessHandle(peer_handle_); 194 #endif 195 } 196 197 IPC::ChannelProxy::MessageFilter* BrowserMessageFilter::GetFilter() { 198 // We create this on demand so that if a filter is used in a unit test but 199 // never attached to a channel, we don't leak Internal and this; 200 DCHECK(!internal_) << "Should only be called once."; 201 internal_ = new Internal(this); 202 return internal_; 203 } 204 205 } // namespace content 206