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_ == size())
     20     return;
     21 
     22   // We close all the owning descriptors. If this message should have
     23   // been transmitted, then closing those with close flags set mirrors
     24   // the expected behaviour.
     25   //
     26   // If this message was received with more descriptors than expected
     27   // (which could a DOS against the browser by a rogue renderer) then all
     28   // the descriptors have their close flag set and we free all the extra
     29   // kernel resources.
     30   LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors: "
     31                << consumed_descriptor_highwater_ << "/" << size();
     32 }
     33 
     34 bool FileDescriptorSet::AddToBorrow(base::PlatformFile fd) {
     35   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
     36 
     37   if (size() == kMaxDescriptorsPerMessage) {
     38     DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
     39     return false;
     40   }
     41 
     42   descriptors_.push_back(fd);
     43   return true;
     44 }
     45 
     46 bool FileDescriptorSet::AddToOwn(base::ScopedFD fd) {
     47   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
     48 
     49   if (size() == kMaxDescriptorsPerMessage) {
     50     DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
     51     return false;
     52   }
     53 
     54   descriptors_.push_back(fd.get());
     55   owned_descriptors_.push_back(new base::ScopedFD(fd.Pass()));
     56   DCHECK(size() <= kMaxDescriptorsPerMessage);
     57   return true;
     58 }
     59 
     60 base::PlatformFile FileDescriptorSet::TakeDescriptorAt(unsigned index) {
     61   if (index >= size()) {
     62     DLOG(WARNING) << "Accessing out of bound index:"
     63                   << index << "/" << size();
     64     return -1;
     65   }
     66 
     67 
     68   // We should always walk the descriptors in order, so it's reasonable to
     69   // enforce this. Consider the case where a compromised renderer sends us
     70   // the following message:
     71   //
     72   //   ExampleMsg:
     73   //     num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
     74   //
     75   // Here the renderer sent us a message which should have a descriptor, but
     76   // actually sent two in an attempt to fill our fd table and kill us. By
     77   // setting the index of the descriptor in the message to 1 (it should be
     78   // 0), we would record a highwater of 1 and then consider all the
     79   // descriptors to have been used.
     80   //
     81   // So we can either track of the use of each descriptor in a bitset, or we
     82   // can enforce that we walk the indexes strictly in order.
     83   //
     84   // There's one more wrinkle: When logging messages, we may reparse them. So
     85   // we have an exception: When the consumed_descriptor_highwater_ is at the
     86   // end of the array and index 0 is requested, we reset the highwater value.
     87   // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer
     88   // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294
     89   if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size())
     90     consumed_descriptor_highwater_ = 0;
     91 
     92   if (index != consumed_descriptor_highwater_)
     93     return -1;
     94 
     95   consumed_descriptor_highwater_ = index + 1;
     96 
     97   base::PlatformFile file = descriptors_[index];
     98 
     99   // TODO(morrita): In production, descriptors_.size() should be same as
    100   // owned_descriptors_.size() as all read descriptors are owned by Message.
    101   // We have to do this because unit test breaks this assumption. It should be
    102   // changed to exercise with own-able descriptors.
    103   for (ScopedVector<base::ScopedFD>::const_iterator i =
    104            owned_descriptors_.begin();
    105        i != owned_descriptors_.end();
    106        ++i) {
    107     if ((*i)->get() == file) {
    108       ignore_result((*i)->release());
    109       break;
    110     }
    111   }
    112 
    113   return file;
    114 }
    115 
    116 void FileDescriptorSet::PeekDescriptors(base::PlatformFile* buffer) const {
    117   std::copy(descriptors_.begin(), descriptors_.end(), buffer);
    118 }
    119 
    120 bool FileDescriptorSet::ContainsDirectoryDescriptor() const {
    121   struct stat st;
    122 
    123   for (std::vector<base::PlatformFile>::const_iterator i = descriptors_.begin();
    124        i != descriptors_.end();
    125        ++i) {
    126     if (fstat(*i, &st) == 0 && S_ISDIR(st.st_mode))
    127       return true;
    128   }
    129 
    130   return false;
    131 }
    132 
    133 void FileDescriptorSet::CommitAll() {
    134   descriptors_.clear();
    135   owned_descriptors_.clear();
    136   consumed_descriptor_highwater_ = 0;
    137 }
    138 
    139 void FileDescriptorSet::ReleaseFDsToClose(
    140     std::vector<base::PlatformFile>* fds) {
    141   for (ScopedVector<base::ScopedFD>::iterator i = owned_descriptors_.begin();
    142        i != owned_descriptors_.end();
    143        ++i) {
    144     fds->push_back((*i)->release());
    145   }
    146 
    147   CommitAll();
    148 }
    149 
    150 void FileDescriptorSet::AddDescriptorsToOwn(const base::PlatformFile* buffer,
    151                                             unsigned count) {
    152   DCHECK(count <= kMaxDescriptorsPerMessage);
    153   DCHECK_EQ(size(), 0u);
    154   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
    155 
    156   descriptors_.reserve(count);
    157   owned_descriptors_.reserve(count);
    158   for (unsigned i = 0; i < count; ++i) {
    159     descriptors_.push_back(buffer[i]);
    160     owned_descriptors_.push_back(new base::ScopedFD(buffer[i]));
    161   }
    162 }
    163