• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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