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 (IGNORE_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 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 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 66 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 99 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 106 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 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 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 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