|
1 // Copyright (c) 2006-2008 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 "chrome/common/file_descriptor_set_posix.h" |
|
6 |
|
7 #include "base/eintr_wrapper.h" |
|
8 #include "base/logging.h" |
|
9 |
|
10 #include <unistd.h> |
|
11 |
|
12 FileDescriptorSet::FileDescriptorSet() |
|
13 : consumed_descriptor_highwater_(0) { |
|
14 } |
|
15 |
|
16 FileDescriptorSet::~FileDescriptorSet() { |
|
17 if (consumed_descriptor_highwater_ == descriptors_.size()) |
|
18 return; |
|
19 |
|
20 CHROMIUM_LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors"; |
|
21 // We close all the descriptors where the close flag is set. If this |
|
22 // message should have been transmitted, then closing those with close |
|
23 // flags set mirrors the expected behaviour. |
|
24 // |
|
25 // If this message was received with more descriptors than expected |
|
26 // (which could a DOS against the browser by a rogue renderer) then all |
|
27 // the descriptors have their close flag set and we free all the extra |
|
28 // kernel resources. |
|
29 for (unsigned i = consumed_descriptor_highwater_; |
|
30 i < descriptors_.size(); ++i) { |
|
31 if (descriptors_[i].auto_close) |
|
32 HANDLE_EINTR(close(descriptors_[i].fd)); |
|
33 } |
|
34 } |
|
35 |
|
36 bool FileDescriptorSet::Add(int fd) { |
|
37 if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) |
|
38 return false; |
|
39 |
|
40 struct base::FileDescriptor sd; |
|
41 sd.fd = fd; |
|
42 sd.auto_close = false; |
|
43 descriptors_.push_back(sd); |
|
44 return true; |
|
45 } |
|
46 |
|
47 bool FileDescriptorSet::AddAndAutoClose(int fd) { |
|
48 if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) |
|
49 return false; |
|
50 |
|
51 struct base::FileDescriptor sd; |
|
52 sd.fd = fd; |
|
53 sd.auto_close = true; |
|
54 descriptors_.push_back(sd); |
|
55 DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); |
|
56 return true; |
|
57 } |
|
58 |
|
59 int FileDescriptorSet::GetDescriptorAt(unsigned index) const { |
|
60 if (index >= descriptors_.size()) |
|
61 return -1; |
|
62 |
|
63 // We should always walk the descriptors in order, so it's reasonable to |
|
64 // enforce this. Consider the case where a compromised renderer sends us |
|
65 // the following message: |
|
66 // |
|
67 // ExampleMsg: |
|
68 // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} |
|
69 // |
|
70 // Here the renderer sent us a message which should have a descriptor, but |
|
71 // actually sent two in an attempt to fill our fd table and kill us. By |
|
72 // setting the index of the descriptor in the message to 1 (it should be |
|
73 // 0), we would record a highwater of 1 and then consider all the |
|
74 // descriptors to have been used. |
|
75 // |
|
76 // So we can either track of the use of each descriptor in a bitset, or we |
|
77 // can enforce that we walk the indexes strictly in order. |
|
78 // |
|
79 // There's one more wrinkle: When logging messages, we may reparse them. So |
|
80 // we have an exception: When the consumed_descriptor_highwater_ is at the |
|
81 // end of the array and index 0 is requested, we reset the highwater value. |
|
82 if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size()) |
|
83 consumed_descriptor_highwater_ = 0; |
|
84 |
|
85 if (index != consumed_descriptor_highwater_) |
|
86 return -1; |
|
87 |
|
88 consumed_descriptor_highwater_ = index + 1; |
|
89 return descriptors_[index].fd; |
|
90 } |
|
91 |
|
92 void FileDescriptorSet::GetDescriptors(int* buffer) const { |
|
93 for (std::vector<base::FileDescriptor>::const_iterator |
|
94 i = descriptors_.begin(); i != descriptors_.end(); ++i) { |
|
95 *(buffer++) = i->fd; |
|
96 } |
|
97 } |
|
98 |
|
99 void FileDescriptorSet::CommitAll() { |
|
100 for (std::vector<base::FileDescriptor>::iterator |
|
101 i = descriptors_.begin(); i != descriptors_.end(); ++i) { |
|
102 if (i->auto_close) |
|
103 HANDLE_EINTR(close(i->fd)); |
|
104 } |
|
105 descriptors_.clear(); |
|
106 consumed_descriptor_highwater_ = 0; |
|
107 } |
|
108 |
|
109 void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) { |
|
110 DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE); |
|
111 DCHECK_EQ(descriptors_.size(), 0u); |
|
112 DCHECK_EQ(consumed_descriptor_highwater_, 0u); |
|
113 |
|
114 descriptors_.reserve(count); |
|
115 for (unsigned i = 0; i < count; ++i) { |
|
116 struct base::FileDescriptor sd; |
|
117 sd.fd = buffer[i]; |
|
118 sd.auto_close = true; |
|
119 descriptors_.push_back(sd); |
|
120 } |
|
121 } |