michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "chrome/common/file_descriptor_set_posix.h" michael@0: michael@0: #include "base/eintr_wrapper.h" michael@0: #include "base/logging.h" michael@0: michael@0: #include michael@0: michael@0: FileDescriptorSet::FileDescriptorSet() michael@0: : consumed_descriptor_highwater_(0) { michael@0: } michael@0: michael@0: FileDescriptorSet::~FileDescriptorSet() { michael@0: if (consumed_descriptor_highwater_ == descriptors_.size()) michael@0: return; michael@0: michael@0: CHROMIUM_LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors"; michael@0: // We close all the descriptors where the close flag is set. If this michael@0: // message should have been transmitted, then closing those with close michael@0: // flags set mirrors the expected behaviour. michael@0: // michael@0: // If this message was received with more descriptors than expected michael@0: // (which could a DOS against the browser by a rogue renderer) then all michael@0: // the descriptors have their close flag set and we free all the extra michael@0: // kernel resources. michael@0: for (unsigned i = consumed_descriptor_highwater_; michael@0: i < descriptors_.size(); ++i) { michael@0: if (descriptors_[i].auto_close) michael@0: HANDLE_EINTR(close(descriptors_[i].fd)); michael@0: } michael@0: } michael@0: michael@0: bool FileDescriptorSet::Add(int fd) { michael@0: if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) michael@0: return false; michael@0: michael@0: struct base::FileDescriptor sd; michael@0: sd.fd = fd; michael@0: sd.auto_close = false; michael@0: descriptors_.push_back(sd); michael@0: return true; michael@0: } michael@0: michael@0: bool FileDescriptorSet::AddAndAutoClose(int fd) { michael@0: if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) michael@0: return false; michael@0: michael@0: struct base::FileDescriptor sd; michael@0: sd.fd = fd; michael@0: sd.auto_close = true; michael@0: descriptors_.push_back(sd); michael@0: DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); michael@0: return true; michael@0: } michael@0: michael@0: int FileDescriptorSet::GetDescriptorAt(unsigned index) const { michael@0: if (index >= descriptors_.size()) michael@0: return -1; michael@0: michael@0: // We should always walk the descriptors in order, so it's reasonable to michael@0: // enforce this. Consider the case where a compromised renderer sends us michael@0: // the following message: michael@0: // michael@0: // ExampleMsg: michael@0: // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} michael@0: // michael@0: // Here the renderer sent us a message which should have a descriptor, but michael@0: // actually sent two in an attempt to fill our fd table and kill us. By michael@0: // setting the index of the descriptor in the message to 1 (it should be michael@0: // 0), we would record a highwater of 1 and then consider all the michael@0: // descriptors to have been used. michael@0: // michael@0: // So we can either track of the use of each descriptor in a bitset, or we michael@0: // can enforce that we walk the indexes strictly in order. michael@0: // michael@0: // There's one more wrinkle: When logging messages, we may reparse them. So michael@0: // we have an exception: When the consumed_descriptor_highwater_ is at the michael@0: // end of the array and index 0 is requested, we reset the highwater value. michael@0: if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size()) michael@0: consumed_descriptor_highwater_ = 0; michael@0: michael@0: if (index != consumed_descriptor_highwater_) michael@0: return -1; michael@0: michael@0: consumed_descriptor_highwater_ = index + 1; michael@0: return descriptors_[index].fd; michael@0: } michael@0: michael@0: void FileDescriptorSet::GetDescriptors(int* buffer) const { michael@0: for (std::vector::const_iterator michael@0: i = descriptors_.begin(); i != descriptors_.end(); ++i) { michael@0: *(buffer++) = i->fd; michael@0: } michael@0: } michael@0: michael@0: void FileDescriptorSet::CommitAll() { michael@0: for (std::vector::iterator michael@0: i = descriptors_.begin(); i != descriptors_.end(); ++i) { michael@0: if (i->auto_close) michael@0: HANDLE_EINTR(close(i->fd)); michael@0: } michael@0: descriptors_.clear(); michael@0: consumed_descriptor_highwater_ = 0; michael@0: } michael@0: michael@0: void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) { michael@0: DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE); michael@0: DCHECK_EQ(descriptors_.size(), 0u); michael@0: DCHECK_EQ(consumed_descriptor_highwater_, 0u); michael@0: michael@0: descriptors_.reserve(count); michael@0: for (unsigned i = 0; i < count; ++i) { michael@0: struct base::FileDescriptor sd; michael@0: sd.fd = buffer[i]; michael@0: sd.auto_close = true; michael@0: descriptors_.push_back(sd); michael@0: } michael@0: }