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