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/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:
     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 
    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 
    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 
    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 
    141 void BrowserMessageFilter::OnDestruct() const {
    142   delete this;
    143 }
    144 
    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 
    171 base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage(
    172     const IPC::Message& message) {
    173   return NULL;
    174 }
    175 
    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 
    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 
    212 BrowserMessageFilter::~BrowserMessageFilter() {
    213 #if defined(OS_WIN)
    214   if (peer_handle_ != base::kNullProcessHandle)
    215     base::CloseProcessHandle(peer_handle_);
    216 #endif
    217 }
    218 
    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