Bug 1885602 - Part 4: Implement navigating to the settings from the menu header for...
[gecko.git] / ipc / glue / Shmem.cpp
blob442c6fb73c02554c8d93b7404a1024e43d6ea9ad
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/. */
7 #include "Shmem.h"
9 #include "ProtocolUtils.h"
10 #include "SharedMemoryBasic.h"
11 #include "ShmemMessageUtils.h"
12 #include "chrome/common/ipc_message_utils.h"
13 #include "mozilla/Unused.h"
15 namespace mozilla {
16 namespace ipc {
18 class ShmemCreated : public IPC::Message {
19 private:
20 typedef Shmem::id_t id_t;
22 public:
23 ShmemCreated(int32_t routingId, id_t aIPDLId, size_t aSize)
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 MOZ_RELEASE_ASSERT(aSize < std::numeric_limits<uint32_t>::max(),
29 "Tried to create Shmem with size larger than 4GB");
30 IPC::MessageWriter writer(*this);
31 IPC::WriteParam(&writer, aIPDLId);
32 IPC::WriteParam(&writer, uint32_t(aSize));
35 static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId,
36 size_t* aSize) {
37 uint32_t size = 0;
38 if (!IPC::ReadParam(aReader, aIPDLId) || !IPC::ReadParam(aReader, &size)) {
39 return false;
41 *aSize = size;
42 return true;
45 void Log(const std::string& aPrefix, FILE* aOutf) const {
46 fputs("(special ShmemCreated msg)", aOutf);
50 class ShmemDestroyed : public IPC::Message {
51 private:
52 typedef Shmem::id_t id_t;
54 public:
55 ShmemDestroyed(int32_t routingId, id_t aIPDLId)
56 : IPC::Message(
57 routingId, SHMEM_DESTROYED_MESSAGE_TYPE, 0,
58 HeaderFlags(NOT_NESTED, NORMAL_PRIORITY, COMPRESSION_NONE,
59 LAZY_SEND, NOT_CONSTRUCTOR, ASYNC, NOT_REPLY)) {
60 IPC::MessageWriter writer(*this);
61 IPC::WriteParam(&writer, aIPDLId);
65 static already_AddRefed<SharedMemory> NewSegment() {
66 return MakeAndAddRef<SharedMemoryBasic>();
69 static already_AddRefed<SharedMemory> CreateSegment(size_t aNBytes) {
70 if (!aNBytes) {
71 return nullptr;
73 RefPtr<SharedMemory> segment = NewSegment();
74 if (!segment) {
75 return nullptr;
77 size_t size = SharedMemory::PageAlignedSize(aNBytes);
78 if (!segment->Create(size) || !segment->Map(size)) {
79 return nullptr;
81 return segment.forget();
84 static already_AddRefed<SharedMemory> ReadSegment(
85 const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes) {
86 if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
87 NS_ERROR("expected 'shmem created' message");
88 return nullptr;
90 IPC::MessageReader reader(aDescriptor);
91 if (!ShmemCreated::ReadInfo(&reader, aId, aNBytes)) {
92 return nullptr;
94 RefPtr<SharedMemory> segment = NewSegment();
95 if (!segment) {
96 return nullptr;
98 if (!segment->ReadHandle(&reader)) {
99 NS_ERROR("trying to open invalid handle");
100 return nullptr;
102 reader.EndRead();
103 if (!*aNBytes) {
104 return nullptr;
106 size_t size = SharedMemory::PageAlignedSize(*aNBytes);
107 if (!segment->Map(size)) {
108 return nullptr;
110 // close the handle to the segment after it is mapped
111 segment->CloseHandle();
112 return segment.forget();
115 #if defined(DEBUG)
117 static void Protect(SharedMemory* aSegment) {
118 MOZ_ASSERT(aSegment, "null segment");
119 aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
120 aSegment->Size(), RightsNone);
123 static void Unprotect(SharedMemory* aSegment) {
124 MOZ_ASSERT(aSegment, "null segment");
125 aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
126 aSegment->Size(), RightsRead | RightsWrite);
129 void Shmem::AssertInvariants() const {
130 MOZ_ASSERT(mSegment, "null segment");
131 MOZ_ASSERT(mData, "null data pointer");
132 MOZ_ASSERT(mSize > 0, "invalid size");
133 // if the segment isn't owned by the current process, these will
134 // trigger SIGSEGV
135 char checkMappingFront = *reinterpret_cast<char*>(mData);
136 char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
138 // avoid "unused" warnings for these variables:
139 Unused << checkMappingFront;
140 Unused << checkMappingBack;
143 void Shmem::RevokeRights() {
144 AssertInvariants();
146 // When sending a non-unsafe shmem, remove read/write rights from the local
147 // mapping of the segment.
148 if (!mUnsafe) {
149 Protect(mSegment);
153 #endif // if defined(DEBUG)
155 Shmem::Shmem(SharedMemory* aSegment, id_t aId, size_t aSize, bool aUnsafe)
156 : mSegment(aSegment), mData(aSegment->memory()), mSize(aSize), mId(aId) {
157 #ifdef DEBUG
158 mUnsafe = aUnsafe;
159 Unprotect(mSegment);
160 #endif
162 MOZ_RELEASE_ASSERT(mSegment->Size() >= mSize,
163 "illegal size in shared memory segment");
166 // static
167 already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(size_t aNBytes) {
168 RefPtr<SharedMemory> segment = CreateSegment(aNBytes);
169 if (!segment) {
170 return nullptr;
173 return segment.forget();
176 // static
177 already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(
178 const IPC::Message& aDescriptor, id_t* aId, bool /*unused*/) {
179 size_t size;
180 RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size);
181 if (!segment) {
182 return nullptr;
185 return segment.forget();
188 UniquePtr<IPC::Message> Shmem::MkCreatedMessage(int32_t routingId) {
189 AssertInvariants();
191 auto msg = MakeUnique<ShmemCreated>(routingId, mId, mSize);
192 IPC::MessageWriter writer(*msg);
193 if (!mSegment->WriteHandle(&writer)) {
194 return nullptr;
196 // close the handle to the segment after it is shared
197 mSegment->CloseHandle();
198 return msg;
201 UniquePtr<IPC::Message> Shmem::MkDestroyedMessage(int32_t routingId) {
202 AssertInvariants();
203 return MakeUnique<ShmemDestroyed>(routingId, mId);
206 void IPDLParamTraits<Shmem>::Write(IPC::MessageWriter* aWriter,
207 IProtocol* aActor, Shmem&& aParam) {
208 WriteIPDLParam(aWriter, aActor, aParam.mId);
209 WriteIPDLParam(aWriter, aActor, uint32_t(aParam.mSize));
210 #ifdef DEBUG
211 WriteIPDLParam(aWriter, aActor, aParam.mUnsafe);
212 #endif
214 aParam.RevokeRights();
215 aParam.forget();
218 bool IPDLParamTraits<Shmem>::Read(IPC::MessageReader* aReader,
219 IProtocol* aActor, paramType* aResult) {
220 paramType::id_t id;
221 uint32_t size;
222 if (!ReadIPDLParam(aReader, aActor, &id) ||
223 !ReadIPDLParam(aReader, aActor, &size)) {
224 return false;
227 bool unsafe = false;
228 #ifdef DEBUG
229 if (!ReadIPDLParam(aReader, aActor, &unsafe)) {
230 return false;
232 #endif
234 Shmem::SharedMemory* rawmem = aActor->LookupSharedMemory(id);
235 if (rawmem) {
236 if (size > rawmem->Size()) {
237 return false;
240 *aResult = Shmem(rawmem, id, size, unsafe);
241 return true;
243 *aResult = Shmem();
244 return true;
247 } // namespace ipc
248 } // namespace mozilla