Shmem.cpp (6140B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "Shmem.h" 8 9 #include "ProtocolUtils.h" 10 #include "ShmemMessageUtils.h" 11 #include "chrome/common/ipc_message_utils.h" 12 #include "mozilla/ipc/SharedMemoryHandle.h" 13 14 namespace mozilla { 15 namespace ipc { 16 17 class ShmemCreated : public IPC::Message { 18 private: 19 typedef Shmem::id_t id_t; 20 21 public: 22 ShmemCreated(routeid_t routingId, id_t aIPDLId, 23 MutableSharedMemoryHandle&& aHandle) 24 : IPC::Message( 25 routingId, SHMEM_CREATED_MESSAGE_TYPE, 0, 26 HeaderFlags(NESTED_INSIDE_CPOW, CONTROL_PRIORITY, COMPRESSION_NONE, 27 LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) { 28 IPC::MessageWriter writer(*this); 29 IPC::WriteParam(&writer, aIPDLId); 30 IPC::WriteParam(&writer, std::move(aHandle)); 31 } 32 33 static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId, 34 MutableSharedMemoryHandle* aHandle) { 35 return IPC::ReadParam(aReader, aIPDLId) && IPC::ReadParam(aReader, aHandle); 36 } 37 38 void Log(const std::string& aPrefix, FILE* aOutf) const { 39 fputs("(special ShmemCreated msg)", aOutf); 40 } 41 }; 42 43 class ShmemDestroyed : public IPC::Message { 44 private: 45 typedef Shmem::id_t id_t; 46 47 public: 48 ShmemDestroyed(routeid_t routingId, id_t aIPDLId) 49 : IPC::Message( 50 routingId, SHMEM_DESTROYED_MESSAGE_TYPE, 0, 51 HeaderFlags(NOT_NESTED, NORMAL_PRIORITY, COMPRESSION_NONE, 52 LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) { 53 IPC::MessageWriter writer(*this); 54 IPC::WriteParam(&writer, aIPDLId); 55 } 56 }; 57 58 #if defined(DEBUG) 59 60 static void Protect(const Shmem::Segment* aSegment) { 61 MOZ_ASSERT(aSegment && *aSegment, "null segment"); 62 shared_memory::LocalProtect(aSegment->DataAs<char>(), aSegment->Size(), 63 shared_memory::AccessNone); 64 } 65 66 static void Unprotect(const Shmem::Segment* aSegment) { 67 MOZ_ASSERT(aSegment && *aSegment, "null segment"); 68 shared_memory::LocalProtect(aSegment->DataAs<char>(), aSegment->Size(), 69 shared_memory::AccessReadWrite); 70 } 71 72 void Shmem::AssertInvariants() const { 73 MOZ_ASSERT(mSegment, "null segment"); 74 MOZ_ASSERT(mData, "null data pointer"); 75 MOZ_ASSERT(mSize > 0, "invalid size"); 76 // if the segment isn't owned by the current process, these will 77 // trigger SIGSEGV 78 char checkMappingFront = *reinterpret_cast<char*>(mData); 79 char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1); 80 81 // avoid "unused" warnings for these variables: 82 (void)checkMappingFront; 83 (void)checkMappingBack; 84 } 85 86 void Shmem::RevokeRights() { 87 AssertInvariants(); 88 89 // When sending a non-unsafe shmem, remove read/write rights from the local 90 // mapping of the segment. 91 if (!mUnsafe) { 92 Protect(mSegment); 93 } 94 } 95 96 #endif // if defined(DEBUG) 97 98 Shmem::Shmem(RefPtr<Segment>&& aSegment, id_t aId, size_t aSize, bool aUnsafe) 99 : mSegment(std::move(aSegment)), 100 mData(mSegment->Address()), 101 mSize(aSize), 102 mId(aId) { 103 #ifdef DEBUG 104 mUnsafe = aUnsafe; 105 Unprotect(mSegment); 106 #endif 107 108 MOZ_RELEASE_ASSERT(mSegment->Size() >= mSize, 109 "illegal size in shared memory segment"); 110 } 111 112 Shmem::Builder::Builder(size_t aSize) : mSize(aSize) { 113 if (!aSize) { 114 return; 115 } 116 size_t pageAlignedSize = shared_memory::PageAlignedSize(aSize); 117 mHandle = shared_memory::Create(pageAlignedSize); 118 if (!mHandle) { 119 return; 120 } 121 auto mapping = mHandle.Map(); 122 if (!mapping) { 123 return; 124 } 125 mSegment = MakeAndAddRef<Segment>(std::move(mapping)); 126 } 127 128 std::tuple<UniquePtr<IPC::Message>, Shmem> Shmem::Builder::Build( 129 id_t aId, bool aUnsafe, IPC::Message::routeid_t aRoutingId) { 130 Shmem shmem(std::move(mSegment), aId, mSize, aUnsafe); 131 shmem.AssertInvariants(); 132 MOZ_ASSERT(mHandle, "null shmem handle"); 133 134 auto msg = MakeUnique<ShmemCreated>(aRoutingId, aId, std::move(mHandle)); 135 return std::make_tuple(std::move(msg), std::move(shmem)); 136 } 137 138 // static 139 already_AddRefed<Shmem::Segment> Shmem::OpenExisting( 140 const IPC::Message& aDescriptor, id_t* aId, bool /*unused*/) { 141 if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) { 142 NS_ERROR("expected 'shmem created' message"); 143 return nullptr; 144 } 145 MutableSharedMemoryHandle handle; 146 IPC::MessageReader reader(aDescriptor); 147 if (!ShmemCreated::ReadInfo(&reader, aId, &handle)) { 148 return nullptr; 149 } 150 reader.EndRead(); 151 if (!handle) { 152 return nullptr; 153 } 154 155 auto mapping = handle.Map(); 156 if (!mapping) { 157 return nullptr; 158 } 159 return MakeAndAddRef<Shmem::Segment>(std::move(mapping)); 160 } 161 162 UniquePtr<IPC::Message> Shmem::MkDestroyedMessage( 163 IPC::Message::routeid_t routingId) { 164 AssertInvariants(); 165 return MakeUnique<ShmemDestroyed>(routingId, mId); 166 } 167 168 } // namespace ipc 169 } // namespace mozilla 170 171 namespace IPC { 172 173 void ParamTraits<mozilla::ipc::Shmem>::Write(IPC::MessageWriter* aWriter, 174 paramType&& aParam) { 175 WriteParam(aWriter, aParam.mId); 176 WriteParam(aWriter, uint32_t(aParam.mSize)); 177 #ifdef DEBUG 178 WriteParam(aWriter, aParam.mUnsafe); 179 #endif 180 181 aParam.RevokeRights(); 182 aParam.forget(); 183 } 184 185 bool ParamTraits<mozilla::ipc::Shmem>::Read(IPC::MessageReader* aReader, 186 paramType* aResult) { 187 if (!aReader->GetActor()) { 188 return false; 189 } 190 191 paramType::id_t id; 192 uint32_t size; 193 if (!ReadParam(aReader, &id) || !ReadParam(aReader, &size)) { 194 return false; 195 } 196 197 bool unsafe = false; 198 #ifdef DEBUG 199 if (!ReadParam(aReader, &unsafe)) { 200 return false; 201 } 202 #endif 203 204 auto* segment = aReader->GetActor()->LookupSharedMemory(id); 205 if (segment) { 206 if (size > segment->Size()) { 207 return false; 208 } 209 210 *aResult = mozilla::ipc::Shmem(segment, id, size, unsafe); 211 return true; 212 } 213 *aResult = mozilla::ipc::Shmem(); 214 return true; 215 } 216 217 } // namespace IPC