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:
Internal(BrowserMessageFilter * filter)27 explicit Internal(BrowserMessageFilter* filter) : filter_(filter) {}
28
29 private:
~Internal()30 virtual ~Internal() {}
31
32 // IPC::MessageFilter implementation:
OnFilterAdded(IPC::Sender * sender)33 virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE {
34 filter_->sender_ = sender;
35 filter_->OnFilterAdded(sender);
36 }
37
OnFilterRemoved()38 virtual void OnFilterRemoved() OVERRIDE {
39 filter_->OnFilterRemoved();
40 }
41
OnChannelClosing()42 virtual void OnChannelClosing() OVERRIDE {
43 filter_->sender_ = NULL;
44 filter_->OnChannelClosing();
45 }
46
OnChannelConnected(int32 peer_pid)47 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
48 filter_->peer_pid_ = peer_pid;
49 filter_->OnChannelConnected(peer_pid);
50 }
51
OnMessageReceived(const IPC::Message & message)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_)) {
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
GetSupportedMessageClasses(std::vector<uint32> * supported_message_classes) const81 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.
DispatchMessage(const IPC::Message & message)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
BrowserMessageFilter(uint32 message_class_to_filter)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
BrowserMessageFilter(const uint32 * message_classes_to_filter,size_t num_message_classes_to_filter)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
PeerHandle()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
OnDestruct() const141 void BrowserMessageFilter::OnDestruct() const {
142 delete this;
143 }
144
Send(IPC::Message * message)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
OverrideTaskRunnerForMessage(const IPC::Message & message)171 base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage(
172 const IPC::Message& message) {
173 return NULL;
174 }
175
CheckCanDispatchOnUI(const IPC::Message & message,IPC::Sender * sender)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
BadMessageReceived()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
~BrowserMessageFilter()212 BrowserMessageFilter::~BrowserMessageFilter() {
213 #if defined(OS_WIN)
214 if (peer_handle_ != base::kNullProcessHandle)
215 base::CloseProcessHandle(peer_handle_);
216 #endif
217 }
218
GetFilter()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