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/ipc_message_attachment_set.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 
11 #include "base/logging.h"
12 #include "base/posix/eintr_wrapper.h"
13 #include "build/build_config.h"
14 #include "ipc/brokerable_attachment.h"
15 #include "ipc/ipc_message_attachment.h"
16 
17 #if defined(OS_POSIX)
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include "ipc/ipc_platform_file_attachment_posix.h"
22 #endif // OS_POSIX
23 
24 namespace IPC {
25 
26 namespace {
27 
count_attachments_of_type(const std::vector<scoped_refptr<MessageAttachment>> & attachments,MessageAttachment::Type type)28 unsigned count_attachments_of_type(
29     const std::vector<scoped_refptr<MessageAttachment>>& attachments,
30     MessageAttachment::Type type) {
31   unsigned count = 0;
32   for (const scoped_refptr<MessageAttachment>& attachment : attachments) {
33     if (attachment->GetType() == type)
34       ++count;
35   }
36   return count;
37 }
38 
39 }  // namespace
40 
MessageAttachmentSet()41 MessageAttachmentSet::MessageAttachmentSet()
42     : consumed_descriptor_highwater_(0) {
43 }
44 
~MessageAttachmentSet()45 MessageAttachmentSet::~MessageAttachmentSet() {
46   if (consumed_descriptor_highwater_ == num_non_brokerable_attachments())
47     return;
48 
49   // We close all the owning descriptors. If this message should have
50   // been transmitted, then closing those with close flags set mirrors
51   // the expected behaviour.
52   //
53   // If this message was received with more descriptors than expected
54   // (which could a DOS against the browser by a rogue renderer) then all
55   // the descriptors have their close flag set and we free all the extra
56   // kernel resources.
57   LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed descriptors: "
58                << consumed_descriptor_highwater_ << "/" << num_descriptors();
59 }
60 
num_descriptors() const61 unsigned MessageAttachmentSet::num_descriptors() const {
62   return count_attachments_of_type(attachments_,
63                                    MessageAttachment::TYPE_PLATFORM_FILE);
64 }
65 
num_mojo_handles() const66 unsigned MessageAttachmentSet::num_mojo_handles() const {
67   return count_attachments_of_type(attachments_,
68                                    MessageAttachment::TYPE_MOJO_HANDLE);
69 }
70 
num_brokerable_attachments() const71 unsigned MessageAttachmentSet::num_brokerable_attachments() const {
72   return static_cast<unsigned>(brokerable_attachments_.size());
73 }
74 
num_non_brokerable_attachments() const75 unsigned MessageAttachmentSet::num_non_brokerable_attachments() const {
76   return static_cast<unsigned>(attachments_.size());
77 }
78 
size() const79 unsigned MessageAttachmentSet::size() const {
80   return static_cast<unsigned>(attachments_.size() +
81                                brokerable_attachments_.size());
82 }
83 
AddAttachment(scoped_refptr<MessageAttachment> attachment,size_t * index,bool * brokerable)84 bool MessageAttachmentSet::AddAttachment(
85     scoped_refptr<MessageAttachment> attachment,
86     size_t* index,
87     bool* brokerable) {
88 #if defined(OS_POSIX)
89   if (attachment->GetType() == MessageAttachment::TYPE_PLATFORM_FILE &&
90       num_descriptors() == kMaxDescriptorsPerMessage) {
91     DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full.";
92     return false;
93   }
94 #endif
95 
96   switch (attachment->GetType()) {
97     case MessageAttachment::TYPE_PLATFORM_FILE:
98     case MessageAttachment::TYPE_MOJO_HANDLE:
99       attachments_.push_back(attachment);
100       *index = attachments_.size() - 1;
101       *brokerable = false;
102       return true;
103     case MessageAttachment::TYPE_BROKERABLE_ATTACHMENT:
104       BrokerableAttachment* brokerable_attachment =
105           static_cast<BrokerableAttachment*>(attachment.get());
106       scoped_refptr<BrokerableAttachment> a(brokerable_attachment);
107       brokerable_attachments_.push_back(a);
108       *index = brokerable_attachments_.size() - 1;
109       *brokerable = true;
110       return true;
111   }
112   return false;
113 }
114 
AddAttachment(scoped_refptr<MessageAttachment> attachment)115 bool MessageAttachmentSet::AddAttachment(
116     scoped_refptr<MessageAttachment> attachment) {
117   bool brokerable;
118   size_t index;
119   return AddAttachment(attachment, &index, &brokerable);
120 }
121 
122 scoped_refptr<MessageAttachment>
GetNonBrokerableAttachmentAt(unsigned index)123 MessageAttachmentSet::GetNonBrokerableAttachmentAt(unsigned index) {
124   if (index >= num_non_brokerable_attachments()) {
125     DLOG(WARNING) << "Accessing out of bound index:" << index << "/"
126                   << num_non_brokerable_attachments();
127     return scoped_refptr<MessageAttachment>();
128   }
129 
130   // We should always walk the descriptors in order, so it's reasonable to
131   // enforce this. Consider the case where a compromised renderer sends us
132   // the following message:
133   //
134   //   ExampleMsg:
135   //     num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
136   //
137   // Here the renderer sent us a message which should have a descriptor, but
138   // actually sent two in an attempt to fill our fd table and kill us. By
139   // setting the index of the descriptor in the message to 1 (it should be
140   // 0), we would record a highwater of 1 and then consider all the
141   // descriptors to have been used.
142   //
143   // So we can either track of the use of each descriptor in a bitset, or we
144   // can enforce that we walk the indexes strictly in order.
145   //
146   // There's one more wrinkle: When logging messages, we may reparse them. So
147   // we have an exception: When the consumed_descriptor_highwater_ is at the
148   // end of the array and index 0 is requested, we reset the highwater value.
149   // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer
150   // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294
151   if (index == 0 &&
152       consumed_descriptor_highwater_ == num_non_brokerable_attachments()) {
153     consumed_descriptor_highwater_ = 0;
154   }
155 
156   if (index != consumed_descriptor_highwater_)
157     return scoped_refptr<MessageAttachment>();
158 
159   consumed_descriptor_highwater_ = index + 1;
160 
161   return attachments_[index];
162 }
163 
164 scoped_refptr<MessageAttachment>
GetBrokerableAttachmentAt(unsigned index)165 MessageAttachmentSet::GetBrokerableAttachmentAt(unsigned index) {
166   if (index >= num_brokerable_attachments()) {
167     DLOG(WARNING) << "Accessing out of bound index:" << index << "/"
168                   << num_brokerable_attachments();
169     return scoped_refptr<MessageAttachment>();
170   }
171 
172   scoped_refptr<BrokerableAttachment> brokerable_attachment(
173       brokerable_attachments_[index]);
174   return scoped_refptr<MessageAttachment>(brokerable_attachment.get());
175 }
176 
CommitAllDescriptors()177 void MessageAttachmentSet::CommitAllDescriptors() {
178   attachments_.clear();
179   consumed_descriptor_highwater_ = 0;
180 }
181 
182 std::vector<scoped_refptr<IPC::BrokerableAttachment>>
GetBrokerableAttachments() const183 MessageAttachmentSet::GetBrokerableAttachments() const {
184   return brokerable_attachments_;
185 }
186 
ReplacePlaceholderWithAttachment(const scoped_refptr<BrokerableAttachment> & attachment)187 void MessageAttachmentSet::ReplacePlaceholderWithAttachment(
188     const scoped_refptr<BrokerableAttachment>& attachment) {
189   DCHECK_NE(BrokerableAttachment::PLACEHOLDER, attachment->GetBrokerableType());
190   for (auto it = brokerable_attachments_.begin();
191        it != brokerable_attachments_.end(); ++it) {
192     if ((*it)->GetBrokerableType() == BrokerableAttachment::PLACEHOLDER &&
193         (*it)->GetIdentifier() == attachment->GetIdentifier()) {
194       *it = attachment;
195       return;
196     }
197   }
198 
199   // This function should only be called if there is a placeholder ready to be
200   // replaced.
201   NOTREACHED();
202 }
203 
204 #if defined(OS_POSIX)
205 
PeekDescriptors(base::PlatformFile * buffer) const206 void MessageAttachmentSet::PeekDescriptors(base::PlatformFile* buffer) const {
207   for (size_t i = 0; i != attachments_.size(); ++i)
208     buffer[i] = internal::GetPlatformFile(attachments_[i]);
209 }
210 
ContainsDirectoryDescriptor() const211 bool MessageAttachmentSet::ContainsDirectoryDescriptor() const {
212   struct stat st;
213 
214   for (auto i = attachments_.begin(); i != attachments_.end(); ++i) {
215     if (fstat(internal::GetPlatformFile(*i), &st) == 0 && S_ISDIR(st.st_mode))
216       return true;
217   }
218 
219   return false;
220 }
221 
ReleaseFDsToClose(std::vector<base::PlatformFile> * fds)222 void MessageAttachmentSet::ReleaseFDsToClose(
223     std::vector<base::PlatformFile>* fds) {
224   for (size_t i = 0; i < attachments_.size(); ++i) {
225     internal::PlatformFileAttachment* file =
226         static_cast<internal::PlatformFileAttachment*>(attachments_[i].get());
227     if (file->Owns())
228       fds->push_back(file->TakePlatformFile());
229   }
230 
231   CommitAllDescriptors();
232 }
233 
AddDescriptorsToOwn(const base::PlatformFile * buffer,unsigned count)234 void MessageAttachmentSet::AddDescriptorsToOwn(const base::PlatformFile* buffer,
235                                                unsigned count) {
236   DCHECK(count <= kMaxDescriptorsPerMessage);
237   DCHECK_EQ(num_descriptors(), 0u);
238   DCHECK_EQ(consumed_descriptor_highwater_, 0u);
239 
240   attachments_.reserve(count);
241   for (unsigned i = 0; i < count; ++i)
242     AddAttachment(
243         new internal::PlatformFileAttachment(base::ScopedFD(buffer[i])));
244 }
245 
246 #endif  // OS_POSIX
247 
248 }  // namespace IPC
249 
250 
251