ipc/glue/Shmem.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:12831c908a20
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "Shmem.h"
9
10 #include "ProtocolUtils.h"
11 #include "SharedMemoryBasic.h"
12 #include "SharedMemorySysV.h"
13
14 #include "nsAutoPtr.h"
15 #include "mozilla/unused.h"
16
17
18 namespace mozilla {
19 namespace ipc {
20
21 class ShmemCreated : public IPC::Message
22 {
23 private:
24 typedef Shmem::id_t id_t;
25
26 public:
27 ShmemCreated(int32_t routingId,
28 const id_t& aIPDLId,
29 const size_t& aSize,
30 const SharedMemoryBasic::Handle& aHandle) :
31 IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, PRIORITY_NORMAL)
32 {
33 IPC::WriteParam(this, aIPDLId);
34 IPC::WriteParam(this, aSize);
35 IPC::WriteParam(this, int32_t(SharedMemory::TYPE_BASIC)),
36 IPC::WriteParam(this, aHandle);
37 }
38
39 // Instead of a single Read() function, we have ReadInfo() and
40 // ReadHandle(). The reason is that the handle type is specific to
41 // the shmem type. These functions should only be called in the
42 // order ReadInfo(); ReadHandle();, and only once each.
43
44 static bool
45 ReadInfo(const Message* msg, void** iter,
46 id_t* aIPDLId,
47 size_t* aSize,
48 SharedMemory::SharedMemoryType* aType)
49 {
50 if (!IPC::ReadParam(msg, iter, aIPDLId) ||
51 !IPC::ReadParam(msg, iter, aSize) ||
52 !IPC::ReadParam(msg, iter, reinterpret_cast<int32_t*>(aType)))
53 return false;
54 return true;
55 }
56
57 static bool
58 ReadHandle(const Message* msg, void** iter,
59 SharedMemoryBasic::Handle* aHandle)
60 {
61 if (!IPC::ReadParam(msg, iter, aHandle))
62 return false;
63 msg->EndRead(*iter);
64 return true;
65 }
66
67 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
68 ShmemCreated(int32_t routingId,
69 const id_t& aIPDLId,
70 const size_t& aSize,
71 const SharedMemorySysV::Handle& aHandle) :
72 IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, PRIORITY_NORMAL)
73 {
74 IPC::WriteParam(this, aIPDLId);
75 IPC::WriteParam(this, aSize);
76 IPC::WriteParam(this, int32_t(SharedMemory::TYPE_SYSV)),
77 IPC::WriteParam(this, aHandle);
78 }
79
80 static bool
81 ReadHandle(const Message* msg, void** iter,
82 SharedMemorySysV::Handle* aHandle)
83 {
84 if (!IPC::ReadParam(msg, iter, aHandle))
85 return false;
86 msg->EndRead(*iter);
87 return true;
88 }
89 #endif
90
91 void Log(const std::string& aPrefix,
92 FILE* aOutf) const
93 {
94 fputs("(special ShmemCreated msg)", aOutf);
95 }
96 };
97
98 class ShmemDestroyed : public IPC::Message
99 {
100 private:
101 typedef Shmem::id_t id_t;
102
103 public:
104 ShmemDestroyed(int32_t routingId,
105 const id_t& aIPDLId) :
106 IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE, PRIORITY_NORMAL)
107 {
108 IPC::WriteParam(this, aIPDLId);
109 }
110 };
111
112
113 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
114 static Shmem::SharedMemory*
115 CreateSegment(size_t aNBytes, SharedMemorySysV::Handle aHandle)
116 {
117 nsAutoPtr<SharedMemory> segment;
118
119 if (SharedMemorySysV::IsHandleValid(aHandle)) {
120 segment = new SharedMemorySysV(aHandle);
121 }
122 else {
123 segment = new SharedMemorySysV();
124
125 if (!segment->Create(aNBytes))
126 return 0;
127 }
128 if (!segment->Map(aNBytes))
129 return 0;
130
131 segment->AddRef();
132 return segment.forget();
133 }
134 #endif
135
136 static Shmem::SharedMemory*
137 CreateSegment(size_t aNBytes, SharedMemoryBasic::Handle aHandle)
138 {
139 nsAutoPtr<SharedMemory> segment;
140
141 if (SharedMemoryBasic::IsHandleValid(aHandle)) {
142 segment = new SharedMemoryBasic(aHandle);
143 }
144 else {
145 segment = new SharedMemoryBasic();
146
147 if (!segment->Create(aNBytes))
148 return 0;
149 }
150 if (!segment->Map(aNBytes))
151 return 0;
152
153 segment->AddRef();
154 return segment.forget();
155 }
156
157 static void
158 DestroySegment(SharedMemory* aSegment)
159 {
160 // the SharedMemory dtor closes and unmaps the actual OS shmem segment
161 if (aSegment)
162 aSegment->Release();
163 }
164
165
166 #if defined(DEBUG)
167
168 static const char sMagic[] =
169 "This little piggy went to market.\n"
170 "This little piggy stayed at home.\n"
171 "This little piggy has roast beef,\n"
172 "This little piggy had none.\n"
173 "And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
174
175
176 struct Header {
177 // Don't use size_t or bool here because their size depends on the
178 // architecture.
179 uint32_t mSize;
180 uint32_t mUnsafe;
181 char mMagic[sizeof(sMagic)];
182 };
183
184 static void
185 GetSections(Shmem::SharedMemory* aSegment,
186 Header** aHeader,
187 char** aFrontSentinel,
188 char** aData,
189 char** aBackSentinel)
190 {
191 NS_ABORT_IF_FALSE(aSegment && aFrontSentinel && aData && aBackSentinel,
192 "NULL param(s)");
193
194 *aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
195 NS_ABORT_IF_FALSE(*aFrontSentinel, "NULL memory()");
196
197 *aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
198
199 size_t pageSize = Shmem::SharedMemory::SystemPageSize();
200 *aData = *aFrontSentinel + pageSize;
201
202 *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
203 }
204
205 static Header*
206 GetHeader(Shmem::SharedMemory* aSegment)
207 {
208 Header* header;
209 char* dontcare;
210 GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
211 return header;
212 }
213
214 static void
215 Protect(SharedMemory* aSegment)
216 {
217 NS_ABORT_IF_FALSE(aSegment, "NULL segment");
218 aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
219 aSegment->Size(),
220 RightsNone);
221 }
222
223 static void
224 Unprotect(SharedMemory* aSegment)
225 {
226 NS_ABORT_IF_FALSE(aSegment, "NULL segment");
227 aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
228 aSegment->Size(),
229 RightsRead | RightsWrite);
230 }
231
232 //
233 // In debug builds, we specially allocate shmem segments. The layout
234 // is as follows
235 //
236 // Page 0: "front sentinel"
237 // size of mapping
238 // magic bytes
239 // Page 1 through n-1:
240 // user data
241 // Page n: "back sentinel"
242 // [nothing]
243 //
244 // The mapping can be in one of the following states, wrt to the
245 // current process.
246 //
247 // State "unmapped": all pages are mapped with no access rights.
248 //
249 // State "mapping": all pages are mapped with read/write access.
250 //
251 // State "mapped": the front and back sentinels are mapped with no
252 // access rights, and all the other pages are mapped with
253 // read/write access.
254 //
255 // When a SharedMemory segment is first allocated, it starts out in
256 // the "mapping" state for the process that allocates the segment, and
257 // in the "unmapped" state for the other process. The allocating
258 // process will then create a Shmem, which takes the segment into the
259 // "mapped" state, where it can be accessed by clients.
260 //
261 // When a Shmem is sent to another process in an IPDL message, the
262 // segment transitions into the "unmapped" state for the sending
263 // process, and into the "mapping" state for the receiving process.
264 // The receiving process will then create a Shmem from the underlying
265 // segment, and take the segment into the "mapped" state.
266 //
267 // In the "mapping" state, we use the front sentinel to verify the
268 // integrity of the shmem segment. If valid, it has a size_t
269 // containing the number of bytes the user allocated followed by the
270 // magic bytes above.
271 //
272 // In the "mapped" state, the front and back sentinels have no access
273 // rights. They act as guards against buffer overflows and underflows
274 // in client code; if clients touch a sentinel, they die with SIGSEGV.
275 //
276 // The "unmapped" state is used to enforce single-owner semantics of
277 // the shmem segment. If a process other than the current owner tries
278 // to touch the segment, it dies with SIGSEGV.
279 //
280
281 Shmem::Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
282 SharedMemory* aSegment, id_t aId) :
283 mSegment(aSegment),
284 mData(0),
285 mSize(0)
286 {
287 NS_ABORT_IF_FALSE(mSegment, "NULL segment");
288 NS_ABORT_IF_FALSE(aId != 0, "invalid ID");
289
290 Unprotect(mSegment);
291
292 Header* header;
293 char* frontSentinel;
294 char* data;
295 char* backSentinel;
296 GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
297
298 // do a quick validity check to avoid weird-looking crashes in libc
299 char check = *frontSentinel;
300 (void)check;
301
302 NS_ABORT_IF_FALSE(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
303 "invalid segment");
304 mSize = static_cast<size_t>(header->mSize);
305
306 size_t pageSize = SharedMemory::SystemPageSize();
307 // transition into the "mapped" state by protecting the front and
308 // back sentinels (which guard against buffer under/overflows)
309 mSegment->Protect(frontSentinel, pageSize, RightsNone);
310 mSegment->Protect(backSentinel, pageSize, RightsNone);
311
312 // don't set these until we know they're valid
313 mData = data;
314 mId = aId;
315 }
316
317 void
318 Shmem::AssertInvariants() const
319 {
320 NS_ABORT_IF_FALSE(mSegment, "NULL segment");
321 NS_ABORT_IF_FALSE(mData, "NULL data pointer");
322 NS_ABORT_IF_FALSE(mSize > 0, "invalid size");
323 // if the segment isn't owned by the current process, these will
324 // trigger SIGSEGV
325 char checkMappingFront = *reinterpret_cast<char*>(mData);
326 char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
327
328 // avoid "unused" warnings for these variables:
329 unused << checkMappingFront;
330 unused << checkMappingBack;
331 }
332
333 void
334 Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
335 {
336 AssertInvariants();
337
338 size_t pageSize = SharedMemory::SystemPageSize();
339 Header* header = GetHeader(mSegment);
340
341 // Open this up for reading temporarily
342 mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
343
344 if (!header->mUnsafe) {
345 Protect(mSegment);
346 } else {
347 mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
348 }
349 }
350
351 // static
352 Shmem::SharedMemory*
353 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
354 size_t aNBytes,
355 SharedMemoryType aType,
356 bool aUnsafe,
357 bool aProtect)
358 {
359 NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
360 NS_ABORT_IF_FALSE(!aProtect || !aUnsafe, "protect => !unsafe");
361
362 size_t pageSize = SharedMemory::SystemPageSize();
363 SharedMemory* segment = nullptr;
364 // |2*pageSize| is for the front and back sentinel
365 size_t segmentSize = SharedMemory::PageAlignedSize(aNBytes + 2*pageSize);
366
367 if (aType == SharedMemory::TYPE_BASIC)
368 segment = CreateSegment(segmentSize, SharedMemoryBasic::NULLHandle());
369 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
370 else if (aType == SharedMemory::TYPE_SYSV)
371 segment = CreateSegment(segmentSize, SharedMemorySysV::NULLHandle());
372 #endif
373 else {
374 NS_ERROR("unknown shmem type");
375 return nullptr;
376 }
377
378 if (!segment)
379 return 0;
380
381 Header* header;
382 char *frontSentinel;
383 char *data;
384 char *backSentinel;
385 GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
386
387 // initialize the segment with Shmem-internal information
388
389 // NB: this can't be a static assert because technically pageSize
390 // isn't known at compile time, event though in practice it's always
391 // going to be 4KiB
392 NS_ABORT_IF_FALSE(sizeof(Header) <= pageSize,
393 "Shmem::Header has gotten too big");
394 memcpy(header->mMagic, sMagic, sizeof(sMagic));
395 header->mSize = static_cast<uint32_t>(aNBytes);
396 header->mUnsafe = aUnsafe;
397
398 if (aProtect)
399 Protect(segment);
400
401 return segment;
402 }
403
404 // static
405 Shmem::SharedMemory*
406 Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
407 const IPC::Message& aDescriptor,
408 id_t* aId,
409 bool aProtect)
410 {
411 if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
412 NS_ERROR("expected 'shmem created' message");
413 return nullptr;
414 }
415
416 void* iter = 0;
417 SharedMemory::SharedMemoryType type;
418 size_t size;
419 if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type))
420 return 0;
421
422 SharedMemory* segment = 0;
423 size_t pageSize = SharedMemory::SystemPageSize();
424 // |2*pageSize| is for the front and back sentinels
425 size_t segmentSize = SharedMemory::PageAlignedSize(size + 2*pageSize);
426
427 if (SharedMemory::TYPE_BASIC == type) {
428 SharedMemoryBasic::Handle handle;
429 if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
430 return 0;
431
432 if (!SharedMemoryBasic::IsHandleValid(handle)) {
433 NS_ERROR("trying to open invalid handle");
434 return nullptr;
435 }
436 segment = CreateSegment(segmentSize, handle);
437 }
438 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
439 else if (SharedMemory::TYPE_SYSV == type) {
440 SharedMemorySysV::Handle handle;
441 if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
442 return 0;
443
444 if (!SharedMemorySysV::IsHandleValid(handle)) {
445 NS_ERROR("trying to open invalid handle");
446 return nullptr;
447 }
448 segment = CreateSegment(segmentSize, handle);
449 }
450 #endif
451 else {
452 NS_ERROR("unknown shmem type");
453 return nullptr;
454 }
455
456 if (!segment)
457 return 0;
458
459 Header* header = GetHeader(segment);
460
461 if (size != header->mSize) {
462 NS_ERROR("Wrong size for this Shmem!");
463 delete segment;
464 return nullptr;
465 }
466
467 // The caller of this function may not know whether the segment is
468 // unsafe or not
469 if (!header->mUnsafe && aProtect)
470 Protect(segment);
471
472 return segment;
473 }
474
475 // static
476 void
477 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
478 SharedMemory* aSegment)
479 {
480 if (!aSegment)
481 return;
482
483 size_t pageSize = SharedMemory::SystemPageSize();
484 Header* header;
485 char *frontSentinel;
486 char *data;
487 char *backSentinel;
488 GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
489
490 aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
491 memset(header->mMagic, 0, sizeof(sMagic));
492 header->mSize = 0;
493 header->mUnsafe = false; // make it "safe" so as to catch errors
494
495 DestroySegment(aSegment);
496 }
497
498
499 #else // !defined(DEBUG)
500
501 // static
502 Shmem::SharedMemory*
503 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
504 size_t aNBytes,
505 SharedMemoryType aType,
506 bool /*unused*/,
507 bool /*unused*/)
508 {
509 SharedMemory *segment = nullptr;
510
511 if (aType == SharedMemory::TYPE_BASIC)
512 segment = CreateSegment(SharedMemory::PageAlignedSize(aNBytes + sizeof(uint32_t)),
513 SharedMemoryBasic::NULLHandle());
514 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
515 else if (aType == SharedMemory::TYPE_SYSV)
516 segment = CreateSegment(SharedMemory::PageAlignedSize(aNBytes + sizeof(uint32_t)),
517 SharedMemorySysV::NULLHandle());
518 #endif
519 else {
520 return nullptr;
521 }
522
523 if (!segment)
524 return 0;
525
526 *PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
527
528 return segment;
529 }
530
531 // static
532 Shmem::SharedMemory*
533 Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
534 const IPC::Message& aDescriptor,
535 id_t* aId,
536 bool /*unused*/)
537 {
538 if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
539 return nullptr;
540 }
541
542 SharedMemory::SharedMemoryType type;
543 void* iter = 0;
544 size_t size;
545 if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type))
546 return 0;
547
548 SharedMemory* segment = 0;
549 size_t segmentSize = SharedMemory::PageAlignedSize(size + sizeof(uint32_t));
550
551 if (SharedMemory::TYPE_BASIC == type) {
552 SharedMemoryBasic::Handle handle;
553 if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
554 return 0;
555
556 if (!SharedMemoryBasic::IsHandleValid(handle)) {
557 return nullptr;
558 }
559
560 segment = CreateSegment(segmentSize, handle);
561 }
562 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
563 else if (SharedMemory::TYPE_SYSV == type) {
564 SharedMemorySysV::Handle handle;
565 if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
566 return 0;
567
568 if (!SharedMemorySysV::IsHandleValid(handle)) {
569 return nullptr;
570 }
571 segment = CreateSegment(segmentSize, handle);
572 }
573 #endif
574 else {
575 return nullptr;
576 }
577
578 if (!segment)
579 return 0;
580
581 // this is the only validity check done in non-DEBUG builds
582 if (size != static_cast<size_t>(*PtrToSize(segment))) {
583 delete segment;
584 return nullptr;
585 }
586
587 return segment;
588 }
589
590 // static
591 void
592 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
593 SharedMemory* aSegment)
594 {
595 DestroySegment(aSegment);
596 }
597
598 #endif // if defined(DEBUG)
599
600 int
601 Shmem::GetSysVID() const
602 {
603 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
604 AssertInvariants();
605
606 if (mSegment->Type() != SharedMemory::TYPE_SYSV) {
607 NS_ERROR("Can't call GetSysVID() on a non-SysV Shmem!");
608 return -1;
609 }
610
611 SharedMemorySysV* seg = static_cast<SharedMemorySysV*>(mSegment);
612 return seg->GetHandle();
613 #else
614 NS_ERROR("Can't call GetSysVID() with no support for SysV shared memory!");
615 return -1; // not reached
616 #endif
617 }
618
619 IPC::Message*
620 Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
621 base::ProcessHandle aProcess,
622 int32_t routingId)
623 {
624 AssertInvariants();
625
626 if (SharedMemory::TYPE_BASIC == mSegment->Type()) {
627 SharedMemoryBasic* seg = static_cast<SharedMemoryBasic*>(mSegment);
628 SharedMemoryBasic::Handle handle;
629 if (!seg->ShareToProcess(aProcess, &handle))
630 return 0;
631
632 return new ShmemCreated(routingId, mId, mSize, handle);
633 }
634 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
635 else if (SharedMemory::TYPE_SYSV == mSegment->Type()) {
636 SharedMemorySysV* seg = static_cast<SharedMemorySysV*>(mSegment);
637 return new ShmemCreated(routingId, mId, mSize, seg->GetHandle());
638 }
639 #endif
640 else {
641 NS_ABORT_IF_FALSE(false, "unknown shmem type (here?!)");
642 return nullptr;
643 }
644
645 return 0;
646 }
647
648 IPC::Message*
649 Shmem::UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
650 base::ProcessHandle aProcess,
651 int32_t routingId)
652 {
653 AssertInvariants();
654 return new ShmemDestroyed(routingId, mId);
655 }
656
657 } // namespace ipc
658 } // namespace mozilla

mercurial