file_descriptor_set_posix.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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
14FileDescriptorSet::FileDescriptorSet()
15    : consumed_descriptor_highwater_(0) {
16}
17
18FileDescriptorSet::~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
39bool 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
52bool 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
66int 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
99void 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
106bool 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
118void FileDescriptorSet::CommitAll() {
119  for (std::vector<base::FileDescriptor>::iterator
120       i = descriptors_.begin(); i != descriptors_.end(); ++i) {
121    if (i->auto_close)
122      if (HANDLE_EINTR(close(i->fd)) < 0)
123        PLOG(ERROR) << "close";
124  }
125  descriptors_.clear();
126  consumed_descriptor_highwater_ = 0;
127}
128
129void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
130  DCHECK(count <= kMaxDescriptorsPerMessage);
131  DCHECK_EQ(descriptors_.size(), 0u);
132  DCHECK_EQ(consumed_descriptor_highwater_, 0u);
133
134  descriptors_.reserve(count);
135  for (unsigned i = 0; i < count; ++i) {
136    struct base::FileDescriptor sd;
137    sd.fd = buffer[i];
138    sd.auto_close = true;
139    descriptors_.push_back(sd);
140  }
141}
142