Home | History | Annotate | Download | only in ipc
      1 // Copyright (c) 2011 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 "ipc/file_descriptor_set_posix.h"
      6 
      7 #include <sys/types.h>
      8 #include <sys/stat.h>
      9 #include <unistd.h>
     10 
     11 #include "base/logging.h"
     12 #include "base/posix/eintr_wrapper.h"
     13 
     14 FileDescriptorSet::FileDescriptorSet()
     15     : consumed_descriptor_highwater_(0) {
     16 }
     17 
     18 FileDescriptorSet::~FileDescriptorSet() {
     19   if (consumed_descriptor_highwater_ == descriptors_.size())
     20     return;
     21 
     22   LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors";
     23   // We close all the descriptors where the close flag is set. If this
     24   // message should have been transmitted, then closing those with close
     25   // flags set mirrors the expected behaviour.
     26   //
     27   // If this message was received with more descriptors than expected
     28   // (which could a DOS against the browser by a rogue renderer) then all
     29   // the descriptors have their close flag set and we free all the extra
     30   // kernel resources.
     31   for (unsigned i = consumed_descriptor_highwater_;
     32        i < descriptors_.size(); ++i) {
     33     if (descriptors_[i].auto_close)
     34       if (HANDLE_EINTR(close(descriptors_[i].fd)) < 0)
     35         PLOG(ERROR) << "close";
     36   }
     37 }
     38 
     39 bool FileDescriptorSet::Add(int fd) {
     40   if (descriptors_.size() == kMaxDescriptorsPerMessage)
     41     return false;
     42 
     43   struct base::FileDescriptor sd;
     44   sd.fd = fd;
     45   sd.auto_close = false;
     46   descriptors_.push_back(sd);
     47   return true;
     48 }
     49 
     50 bool FileDescriptorSet::AddAndAutoClose(int fd) {
     51   if (descriptors_.size() == kMaxDescriptorsPerMessage)
     52     return false;
     53 
     54   struct base::FileDescriptor sd;
     55   sd.fd = fd;
     56   sd.auto_close = true;
     57   descriptors_.push_back(sd);
     58   DCHECK(descriptors_.size() <= kMaxDescriptorsPerMessage);
     59   return true;
     60 }
     61 
     62 int FileDescriptorSet::GetDescriptorAt(unsigned index) const {
     63   if (index >= descriptors_.size())
     64     return -1;
     65 
     66   // We should always walk the descriptors in order, so it's reasonable to
     67   // enforce this. Consider the case where a compromised renderer sends us
     68   // the following message:
     69   //
     70   //   ExampleMsg:
     71   //     num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
     72   //
     73   // Here the renderer sent us a message which should have a descriptor, but
     74   // actually sent two in an attempt to fill our fd table and kill us. By
     75   // setting the index of the descriptor in the message to 1 (it should be
     76   // 0), we would record a highwater of 1 and then consider all the
     77   // descriptors to have been used.
     78   //
     79   // So we can either track of the use of each descriptor in a bitset, or we
     80   // can enforce that we walk the indexes strictly in order.
     81   //
     82   // There's one more wrinkle: When logging messages, we may reparse them. So
     83   // we have an exception: When the consumed_descriptor_highwater_ is at the
     84   // end of the array and index 0 is requested, we reset the highwater value.
     85   if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size())
     86     consumed_descriptor_highwater_ = 0;
     87 
     88   if (index != consumed_descriptor_highwater_)
     89     return -1;
     90 
     91   consumed_descriptor_highwater_ = index + 1;
     92   return descriptors_[index].fd;
     93 }
     94 
     95 void FileDescriptorSet::GetDescriptors(int* buffer) const {
     96   for (std::vector<base::FileDescriptor>::const_iterator
     97        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
     98     *(buffer++) = i->fd;
     99   }
    100 }
    101 
    102 bool FileDescriptorSet::ContainsDirectoryDescriptor() const {
    103   struct stat st;
    104 
    105   for (std::vector<base::FileDescriptor>::const_iterator
    106        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
    107     if (fstat(i->fd, &st) == 0 && S_ISDIR(st.st_mode))
    108       return true;
    109   }
    110 
    111   return false;
    112 }
    113 
    114 void FileDescriptorSet::CommitAll() {
    115   for (std::vector<base::FileDescriptor>::iterator
    116        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
    117     if (i->auto_close)
    118       if (HANDLE_EINTR(close(i->fd)) < 0)
    119         PLOG(ERROR) << "close";
    120   }
    121   descriptors_.clear();
    122   consumed_descriptor_highwater_ = 0;
    123 }
    124 
    125 void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
    126   DCHECK(count <= kMaxDescriptorsPerMessage);
    127   DCHECK_EQ(descriptors_.size(), 0u);
    128   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
    129 
    130   descriptors_.reserve(count);
    131   for (unsigned i = 0; i < count; ++i) {
    132     struct base::FileDescriptor sd;
    133     sd.fd = buffer[i];
    134     sd.auto_close = true;
    135     descriptors_.push_back(sd);
    136   }
    137 }
    138