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:
Internal(BrowserMessageFilter * filter)25 explicit Internal(BrowserMessageFilter* filter) : filter_(filter) {}
26
27 private:
~Internal()28 virtual ~Internal() {}
29
30 // IPC::ChannelProxy::MessageFilter implementation:
OnFilterAdded(IPC::Channel * channel)31 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
32 filter_->channel_ = channel;
33 filter_->OnFilterAdded(channel);
34 }
35
OnFilterRemoved()36 virtual void OnFilterRemoved() OVERRIDE {
37 filter_->OnFilterRemoved();
38 }
39
OnChannelClosing()40 virtual void OnChannelClosing() OVERRIDE {
41 filter_->channel_ = NULL;
42 filter_->OnChannelClosing();
43 }
44
OnChannelConnected(int32 peer_pid)45 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
46 filter_->peer_pid_ = peer_pid;
47 filter_->OnChannelConnected(peer_pid);
48 }
49
OnMessageReceived(const IPC::Message & message)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.
DispatchMessage(const IPC::Message & message)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
BrowserMessageFilter()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
PeerHandle()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
OnDestruct() const121 void BrowserMessageFilter::OnDestruct() const {
122 delete this;
123 }
124
Send(IPC::Message * message)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
OverrideTaskRunnerForMessage(const IPC::Message & message)151 base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage(
152 const IPC::Message& message) {
153 return NULL;
154 }
155
CheckCanDispatchOnUI(const IPC::Message & message,IPC::Sender * sender)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
BadMessageReceived()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
~BrowserMessageFilter()190 BrowserMessageFilter::~BrowserMessageFilter() {
191 #if defined(OS_WIN)
192 if (peer_handle_ != base::kNullProcessHandle)
193 base::CloseProcessHandle(peer_handle_);
194 #endif
195 }
196
GetFilter()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