OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ipc/ipc_message_attachment_set.h" | 5 #include "ipc/ipc_message_attachment_set.h" |
6 | 6 |
| 7 #include <algorithm> |
7 #include "base/logging.h" | 8 #include "base/logging.h" |
8 #include "base/posix/eintr_wrapper.h" | 9 #include "base/posix/eintr_wrapper.h" |
| 10 #include "ipc/ipc_message_attachment.h" |
| 11 #include "ipc/ipc_platform_file_attachment.h" |
9 | 12 |
10 #if defined(OS_POSIX) | 13 #if defined(OS_POSIX) |
11 #include <sys/types.h> | 14 #include <sys/types.h> |
12 #include <sys/stat.h> | 15 #include <sys/stat.h> |
13 #include <unistd.h> | 16 #include <unistd.h> |
14 #endif // OS_POSIX | 17 #endif // OS_POSIX |
15 | 18 |
16 namespace IPC { | 19 namespace IPC { |
17 | 20 |
18 MessageAttachmentSet::MessageAttachmentSet() | 21 MessageAttachmentSet::MessageAttachmentSet() |
19 : consumed_descriptor_highwater_(0) { | 22 : consumed_descriptor_highwater_(0) { |
20 } | 23 } |
21 | 24 |
22 MessageAttachmentSet::~MessageAttachmentSet() { | 25 MessageAttachmentSet::~MessageAttachmentSet() { |
23 if (consumed_descriptor_highwater_ == size()) | 26 if (consumed_descriptor_highwater_ == size()) |
24 return; | 27 return; |
25 | 28 |
26 // We close all the owning descriptors. If this message should have | 29 // We close all the owning descriptors. If this message should have |
27 // been transmitted, then closing those with close flags set mirrors | 30 // been transmitted, then closing those with close flags set mirrors |
28 // the expected behaviour. | 31 // the expected behaviour. |
29 // | 32 // |
30 // If this message was received with more descriptors than expected | 33 // If this message was received with more descriptors than expected |
31 // (which could a DOS against the browser by a rogue renderer) then all | 34 // (which could a DOS against the browser by a rogue renderer) then all |
32 // the descriptors have their close flag set and we free all the extra | 35 // the descriptors have their close flag set and we free all the extra |
33 // kernel resources. | 36 // kernel resources. |
34 LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed descriptors: " | 37 LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed descriptors: " |
35 << consumed_descriptor_highwater_ << "/" << size(); | 38 << consumed_descriptor_highwater_ << "/" << size(); |
36 } | 39 } |
37 | 40 |
38 unsigned MessageAttachmentSet::size() const { | 41 unsigned MessageAttachmentSet::file_size() const { |
39 #if defined(OS_POSIX) | 42 return std::count_if(attachments_.begin(), attachments_.end(), |
40 return descriptors_.size(); | 43 [](scoped_refptr<MessageAttachment> i) { |
41 #else | 44 return i->GetType() == MessageAttachment::TYPE_PLATFORM_FILE; |
42 return 0; | 45 }); |
43 #endif | |
44 } | 46 } |
45 | 47 |
46 #if defined(OS_POSIX) | 48 unsigned MessageAttachmentSet::size() const { |
47 | 49 return attachments_.size(); |
48 bool MessageAttachmentSet::AddToBorrow(base::PlatformFile fd) { | |
49 DCHECK_EQ(consumed_descriptor_highwater_, 0u); | |
50 | |
51 if (size() == kMaxDescriptorsPerMessage) { | |
52 DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full."; | |
53 return false; | |
54 } | |
55 | |
56 descriptors_.push_back(fd); | |
57 return true; | |
58 } | 50 } |
59 | 51 |
60 bool MessageAttachmentSet::AddToOwn(base::ScopedFD fd) { | 52 void MessageAttachmentSet::AddAttachment( |
61 DCHECK_EQ(consumed_descriptor_highwater_, 0u); | 53 scoped_refptr<MessageAttachment> attachment) { |
62 | 54 attachments_.push_back(attachment); |
63 if (size() == kMaxDescriptorsPerMessage) { | |
64 DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full."; | |
65 return false; | |
66 } | |
67 | |
68 descriptors_.push_back(fd.get()); | |
69 owned_descriptors_.push_back(new base::ScopedFD(fd.Pass())); | |
70 DCHECK(size() <= kMaxDescriptorsPerMessage); | |
71 return true; | |
72 } | 55 } |
73 | 56 |
74 base::PlatformFile MessageAttachmentSet::TakeDescriptorAt(unsigned index) { | 57 scoped_refptr<MessageAttachment> MessageAttachmentSet::TakeAttachmentAt( |
| 58 unsigned index) { |
75 if (index >= size()) { | 59 if (index >= size()) { |
76 DLOG(WARNING) << "Accessing out of bound index:" << index << "/" << size(); | 60 DLOG(WARNING) << "Accessing out of bound index:" << index << "/" << size(); |
77 return -1; | 61 return scoped_refptr<MessageAttachment>(); |
78 } | 62 } |
79 | 63 |
80 // We should always walk the descriptors in order, so it's reasonable to | 64 // We should always walk the descriptors in order, so it's reasonable to |
81 // enforce this. Consider the case where a compromised renderer sends us | 65 // enforce this. Consider the case where a compromised renderer sends us |
82 // the following message: | 66 // the following message: |
83 // | 67 // |
84 // ExampleMsg: | 68 // ExampleMsg: |
85 // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} | 69 // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} |
86 // | 70 // |
87 // Here the renderer sent us a message which should have a descriptor, but | 71 // Here the renderer sent us a message which should have a descriptor, but |
88 // actually sent two in an attempt to fill our fd table and kill us. By | 72 // actually sent two in an attempt to fill our fd table and kill us. By |
89 // setting the index of the descriptor in the message to 1 (it should be | 73 // setting the index of the descriptor in the message to 1 (it should be |
90 // 0), we would record a highwater of 1 and then consider all the | 74 // 0), we would record a highwater of 1 and then consider all the |
91 // descriptors to have been used. | 75 // descriptors to have been used. |
92 // | 76 // |
93 // So we can either track of the use of each descriptor in a bitset, or we | 77 // So we can either track of the use of each descriptor in a bitset, or we |
94 // can enforce that we walk the indexes strictly in order. | 78 // can enforce that we walk the indexes strictly in order. |
95 // | 79 // |
96 // There's one more wrinkle: When logging messages, we may reparse them. So | 80 // There's one more wrinkle: When logging messages, we may reparse them. So |
97 // we have an exception: When the consumed_descriptor_highwater_ is at the | 81 // we have an exception: When the consumed_descriptor_highwater_ is at the |
98 // end of the array and index 0 is requested, we reset the highwater value. | 82 // end of the array and index 0 is requested, we reset the highwater value. |
99 // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer | 83 // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer |
100 // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294 | 84 // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294 |
101 if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size()) | 85 if (index == 0 && consumed_descriptor_highwater_ == size()) |
102 consumed_descriptor_highwater_ = 0; | 86 consumed_descriptor_highwater_ = 0; |
103 | 87 |
104 if (index != consumed_descriptor_highwater_) | 88 if (index != consumed_descriptor_highwater_) |
105 return -1; | 89 return scoped_refptr<MessageAttachment>(); |
106 | 90 |
107 consumed_descriptor_highwater_ = index + 1; | 91 consumed_descriptor_highwater_ = index + 1; |
108 | 92 |
109 base::PlatformFile file = descriptors_[index]; | 93 return attachments_[index]; |
| 94 } |
110 | 95 |
111 // TODO(morrita): In production, descriptors_.size() should be same as | 96 #if defined(OS_POSIX) |
| 97 |
| 98 bool MessageAttachmentSet::AddToBorrow(base::PlatformFile fd) { |
| 99 DCHECK_EQ(consumed_descriptor_highwater_, 0u); |
| 100 |
| 101 if (file_size() == kMaxDescriptorsPerMessage) { |
| 102 DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full."; |
| 103 return false; |
| 104 } |
| 105 |
| 106 AddAttachment(new internal::PlatformFileAttachment(fd)); |
| 107 return true; |
| 108 } |
| 109 |
| 110 bool MessageAttachmentSet::AddToOwn(base::ScopedFD fd) { |
| 111 DCHECK_EQ(consumed_descriptor_highwater_, 0u); |
| 112 |
| 113 if (file_size() == kMaxDescriptorsPerMessage) { |
| 114 DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full."; |
| 115 return false; |
| 116 } |
| 117 |
| 118 AddAttachment(new internal::PlatformFileAttachment(fd.get())); |
| 119 owned_descriptors_.push_back(new base::ScopedFD(fd.Pass())); |
| 120 DCHECK(file_size() <= kMaxDescriptorsPerMessage); |
| 121 return true; |
| 122 } |
| 123 |
| 124 base::PlatformFile MessageAttachmentSet::TakeDescriptorAt(unsigned index) { |
| 125 scoped_refptr<MessageAttachment> taken = TakeAttachmentAt(index); |
| 126 if (!taken) |
| 127 return -1; |
| 128 |
| 129 base::PlatformFile file = internal::GetPlatformFile(taken); |
| 130 |
| 131 // TODO(morrita): In production, attachments_.size() should be same as |
112 // owned_descriptors_.size() as all read descriptors are owned by Message. | 132 // owned_descriptors_.size() as all read descriptors are owned by Message. |
113 // We have to do this because unit test breaks this assumption. It should be | 133 // We have to do this because unit test breaks this assumption. It should be |
114 // changed to exercise with own-able descriptors. | 134 // changed to exercise with own-able descriptors. |
115 for (ScopedVector<base::ScopedFD>::const_iterator i = | 135 for (ScopedVector<base::ScopedFD>::const_iterator i = |
116 owned_descriptors_.begin(); | 136 owned_descriptors_.begin(); |
117 i != owned_descriptors_.end(); ++i) { | 137 i != owned_descriptors_.end(); ++i) { |
118 if ((*i)->get() == file) { | 138 if ((*i)->get() == file) { |
119 ignore_result((*i)->release()); | 139 ignore_result((*i)->release()); |
120 break; | 140 break; |
121 } | 141 } |
122 } | 142 } |
123 | 143 |
124 return file; | 144 return file; |
125 } | 145 } |
126 | 146 |
127 void MessageAttachmentSet::PeekDescriptors(base::PlatformFile* buffer) const { | 147 void MessageAttachmentSet::PeekDescriptors(base::PlatformFile* buffer) const { |
128 std::copy(descriptors_.begin(), descriptors_.end(), buffer); | 148 for (size_t i = 0; i != attachments_.size(); ++i) |
| 149 buffer[i] = internal::GetPlatformFile(attachments_[i]); |
129 } | 150 } |
130 | 151 |
131 bool MessageAttachmentSet::ContainsDirectoryDescriptor() const { | 152 bool MessageAttachmentSet::ContainsDirectoryDescriptor() const { |
132 struct stat st; | 153 struct stat st; |
133 | 154 |
134 for (std::vector<base::PlatformFile>::const_iterator i = descriptors_.begin(); | 155 for (auto i = attachments_.begin(); i != attachments_.end(); ++i) { |
135 i != descriptors_.end(); ++i) { | 156 if (fstat(internal::GetPlatformFile(*i), &st) == 0 && S_ISDIR(st.st_mode)) |
136 if (fstat(*i, &st) == 0 && S_ISDIR(st.st_mode)) | |
137 return true; | 157 return true; |
138 } | 158 } |
139 | 159 |
140 return false; | 160 return false; |
141 } | 161 } |
142 | 162 |
143 void MessageAttachmentSet::CommitAll() { | 163 void MessageAttachmentSet::CommitAll() { |
144 descriptors_.clear(); | 164 attachments_.clear(); |
145 owned_descriptors_.clear(); | 165 owned_descriptors_.clear(); |
146 consumed_descriptor_highwater_ = 0; | 166 consumed_descriptor_highwater_ = 0; |
147 } | 167 } |
148 | 168 |
149 void MessageAttachmentSet::ReleaseFDsToClose( | 169 void MessageAttachmentSet::ReleaseFDsToClose( |
150 std::vector<base::PlatformFile>* fds) { | 170 std::vector<base::PlatformFile>* fds) { |
151 for (ScopedVector<base::ScopedFD>::iterator i = owned_descriptors_.begin(); | 171 for (ScopedVector<base::ScopedFD>::iterator i = owned_descriptors_.begin(); |
152 i != owned_descriptors_.end(); ++i) { | 172 i != owned_descriptors_.end(); ++i) { |
153 fds->push_back((*i)->release()); | 173 fds->push_back((*i)->release()); |
154 } | 174 } |
155 | 175 |
156 CommitAll(); | 176 CommitAll(); |
157 } | 177 } |
158 | 178 |
159 void MessageAttachmentSet::AddDescriptorsToOwn(const base::PlatformFile* buffer, | 179 void MessageAttachmentSet::AddDescriptorsToOwn(const base::PlatformFile* buffer, |
160 unsigned count) { | 180 unsigned count) { |
161 DCHECK(count <= kMaxDescriptorsPerMessage); | 181 DCHECK(count <= kMaxDescriptorsPerMessage); |
162 DCHECK_EQ(size(), 0u); | 182 DCHECK_EQ(file_size(), 0u); |
163 DCHECK_EQ(consumed_descriptor_highwater_, 0u); | 183 DCHECK_EQ(consumed_descriptor_highwater_, 0u); |
164 | 184 |
165 descriptors_.reserve(count); | 185 attachments_.reserve(count); |
166 owned_descriptors_.reserve(count); | 186 owned_descriptors_.reserve(count); |
167 for (unsigned i = 0; i < count; ++i) { | 187 for (unsigned i = 0; i < count; ++i) { |
168 descriptors_.push_back(buffer[i]); | 188 AddAttachment(new internal::PlatformFileAttachment(buffer[i])); |
169 owned_descriptors_.push_back(new base::ScopedFD(buffer[i])); | 189 owned_descriptors_.push_back(new base::ScopedFD(buffer[i])); |
170 } | 190 } |
171 } | 191 } |
172 | 192 |
173 #endif // OS_POSIX | 193 #endif // OS_POSIX |
174 | 194 |
175 } // namespace IPC | 195 } // namespace IPC |
176 | 196 |
177 | 197 |
OLD | NEW |