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