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