• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FileDescriptorSet()14 FileDescriptorSet::FileDescriptorSet()
15     : consumed_descriptor_highwater_(0) {
16 }
17 
~FileDescriptorSet()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 
AddToBorrow(base::PlatformFile fd)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 
AddToOwn(base::ScopedFD fd)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 
TakeDescriptorAt(unsigned index)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 
PeekDescriptors(base::PlatformFile * buffer) const116 void FileDescriptorSet::PeekDescriptors(base::PlatformFile* buffer) const {
117   std::copy(descriptors_.begin(), descriptors_.end(), buffer);
118 }
119 
ContainsDirectoryDescriptor() const120 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 
CommitAll()133 void FileDescriptorSet::CommitAll() {
134   descriptors_.clear();
135   owned_descriptors_.clear();
136   consumed_descriptor_highwater_ = 0;
137 }
138 
ReleaseFDsToClose(std::vector<base::PlatformFile> * fds)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 
AddDescriptorsToOwn(const base::PlatformFile * buffer,unsigned count)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