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
21using content::BrowserMessageFilter;
22
23namespace content {
24
25class 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
102BrowserMessageFilter::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
111BrowserMessageFilter::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
126base::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
141void BrowserMessageFilter::OnDestruct() const {
142  delete this;
143}
144
145bool 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
171base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage(
172    const IPC::Message& message) {
173  return NULL;
174}
175
176bool 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
201void 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
212BrowserMessageFilter::~BrowserMessageFilter() {
213#if defined(OS_WIN)
214  if (peer_handle_ != base::kNullProcessHandle)
215    base::CloseProcessHandle(peer_handle_);
216#endif
217}
218
219IPC::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