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 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 41 MessageAttachmentSet::MessageAttachmentSet() 42 : consumed_descriptor_highwater_(0) { 43 } 44 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 61 unsigned MessageAttachmentSet::num_descriptors() const { 62 return count_attachments_of_type(attachments_, 63 MessageAttachment::TYPE_PLATFORM_FILE); 64 } 65 66 unsigned MessageAttachmentSet::num_mojo_handles() const { 67 return count_attachments_of_type(attachments_, 68 MessageAttachment::TYPE_MOJO_HANDLE); 69 } 70 71 unsigned MessageAttachmentSet::num_brokerable_attachments() const { 72 return static_cast<unsigned>(brokerable_attachments_.size()); 73 } 74 75 unsigned MessageAttachmentSet::num_non_brokerable_attachments() const { 76 return static_cast<unsigned>(attachments_.size()); 77 } 78 79 unsigned MessageAttachmentSet::size() const { 80 return static_cast<unsigned>(attachments_.size() + 81 brokerable_attachments_.size()); 82 } 83 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 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> 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> 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 177 void MessageAttachmentSet::CommitAllDescriptors() { 178 attachments_.clear(); 179 consumed_descriptor_highwater_ = 0; 180 } 181 182 std::vector<scoped_refptr<IPC::BrokerableAttachment>> 183 MessageAttachmentSet::GetBrokerableAttachments() const { 184 return brokerable_attachments_; 185 } 186 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 206 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 211 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 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 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