• 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_ == 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 (IGNORE_EINTR(close(descriptors_[i].fd)) < 0)
35         PLOG(ERROR) << "close";
36   }
37 }
38 
Add(int fd)39 bool FileDescriptorSet::Add(int fd) {
40   if (descriptors_.size() == kMaxDescriptorsPerMessage) {
41     DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
42     return false;
43   }
44 
45   struct base::FileDescriptor sd;
46   sd.fd = fd;
47   sd.auto_close = false;
48   descriptors_.push_back(sd);
49   return true;
50 }
51 
AddAndAutoClose(int fd)52 bool FileDescriptorSet::AddAndAutoClose(int fd) {
53   if (descriptors_.size() == kMaxDescriptorsPerMessage) {
54     DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
55     return false;
56   }
57 
58   struct base::FileDescriptor sd;
59   sd.fd = fd;
60   sd.auto_close = true;
61   descriptors_.push_back(sd);
62   DCHECK(descriptors_.size() <= kMaxDescriptorsPerMessage);
63   return true;
64 }
65 
GetDescriptorAt(unsigned index) const66 int FileDescriptorSet::GetDescriptorAt(unsigned index) const {
67   if (index >= descriptors_.size())
68     return -1;
69 
70   // We should always walk the descriptors in order, so it's reasonable to
71   // enforce this. Consider the case where a compromised renderer sends us
72   // the following message:
73   //
74   //   ExampleMsg:
75   //     num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
76   //
77   // Here the renderer sent us a message which should have a descriptor, but
78   // actually sent two in an attempt to fill our fd table and kill us. By
79   // setting the index of the descriptor in the message to 1 (it should be
80   // 0), we would record a highwater of 1 and then consider all the
81   // descriptors to have been used.
82   //
83   // So we can either track of the use of each descriptor in a bitset, or we
84   // can enforce that we walk the indexes strictly in order.
85   //
86   // There's one more wrinkle: When logging messages, we may reparse them. So
87   // we have an exception: When the consumed_descriptor_highwater_ is at the
88   // end of the array and index 0 is requested, we reset the highwater value.
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   return descriptors_[index].fd;
97 }
98 
GetDescriptors(int * buffer) const99 void FileDescriptorSet::GetDescriptors(int* buffer) const {
100   for (std::vector<base::FileDescriptor>::const_iterator
101        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
102     *(buffer++) = i->fd;
103   }
104 }
105 
ContainsDirectoryDescriptor() const106 bool FileDescriptorSet::ContainsDirectoryDescriptor() const {
107   struct stat st;
108 
109   for (std::vector<base::FileDescriptor>::const_iterator
110        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
111     if (fstat(i->fd, &st) == 0 && S_ISDIR(st.st_mode))
112       return true;
113   }
114 
115   return false;
116 }
117 
CommitAll()118 void FileDescriptorSet::CommitAll() {
119   for (std::vector<base::FileDescriptor>::iterator
120        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
121     if (i->auto_close)
122       if (IGNORE_EINTR(close(i->fd)) < 0)
123         PLOG(ERROR) << "close";
124   }
125   descriptors_.clear();
126   consumed_descriptor_highwater_ = 0;
127 }
128 
ReleaseFDsToClose(std::vector<int> * fds)129 void FileDescriptorSet::ReleaseFDsToClose(std::vector<int>* fds) {
130   for (std::vector<base::FileDescriptor>::iterator
131        i = descriptors_.begin(); i != descriptors_.end(); ++i) {
132     if (i->auto_close)
133       fds->push_back(i->fd);
134   }
135   descriptors_.clear();
136   consumed_descriptor_highwater_ = 0;
137 }
138 
SetDescriptors(const int * buffer,unsigned count)139 void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
140   DCHECK(count <= kMaxDescriptorsPerMessage);
141   DCHECK_EQ(descriptors_.size(), 0u);
142   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
143 
144   descriptors_.reserve(count);
145   for (unsigned i = 0; i < count; ++i) {
146     struct base::FileDescriptor sd;
147     sd.fd = buffer[i];
148     sd.auto_close = true;
149     descriptors_.push_back(sd);
150   }
151 }
152