| 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 |