LCOV - code coverage report
Current view: top level - ipc/glue - Shmem.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 109 160 68.1 %
Date: 2017-07-14 16:53:18 Functions: 16 23 69.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "SharedMemoryBasic.h"
      11             : 
      12             : #include "mozilla/Unused.h"
      13             : 
      14             : 
      15             : namespace mozilla {
      16             : namespace ipc {
      17             : 
      18           9 : class ShmemCreated : public IPC::Message
      19             : {
      20             : private:
      21             :   typedef Shmem::id_t id_t;
      22             : 
      23             : public:
      24           3 :   ShmemCreated(int32_t routingId,
      25             :                id_t aIPDLId,
      26             :                size_t aSize,
      27           3 :                SharedMemory::SharedMemoryType aType) :
      28           3 :     IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, 0, NESTED_INSIDE_CPOW)
      29             :   {
      30           3 :     IPC::WriteParam(this, aIPDLId);
      31           3 :     IPC::WriteParam(this, aSize);
      32           3 :     IPC::WriteParam(this, int32_t(aType));
      33           3 :   }
      34             : 
      35             :   static bool
      36           2 :   ReadInfo(const Message* msg, PickleIterator* iter,
      37             :            id_t* aIPDLId,
      38             :            size_t* aSize,
      39             :            SharedMemory::SharedMemoryType* aType)
      40             :   {
      41           6 :     if (!IPC::ReadParam(msg, iter, aIPDLId) ||
      42           4 :         !IPC::ReadParam(msg, iter, aSize) ||
      43           2 :         !IPC::ReadParam(msg, iter, reinterpret_cast<int32_t*>(aType)))
      44           0 :       return false;
      45           2 :     return true;
      46             :   }
      47             : 
      48             :   void Log(const std::string& aPrefix,
      49             :            FILE* aOutf) const
      50             :   {
      51             :     fputs("(special ShmemCreated msg)", aOutf);
      52             :   }
      53             : };
      54             : 
      55           0 : class ShmemDestroyed : public IPC::Message
      56             : {
      57             : private:
      58             :   typedef Shmem::id_t id_t;
      59             : 
      60             : public:
      61           0 :   ShmemDestroyed(int32_t routingId,
      62           0 :                  id_t aIPDLId) :
      63           0 :     IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE)
      64             :   {
      65           0 :     IPC::WriteParam(this, aIPDLId);
      66           0 :   }
      67             : };
      68             : 
      69             : static SharedMemory*
      70           5 : NewSegment(SharedMemory::SharedMemoryType aType)
      71             : {
      72           5 :   if (SharedMemory::TYPE_BASIC == aType) {
      73           5 :     return new SharedMemoryBasic;
      74             :   } else {
      75           0 :     NS_ERROR("unknown Shmem type");
      76           0 :     return nullptr;
      77             :   }
      78             : }
      79             : 
      80             : static already_AddRefed<SharedMemory>
      81           3 : CreateSegment(SharedMemory::SharedMemoryType aType, size_t aNBytes, size_t aExtraSize)
      82             : {
      83           6 :   RefPtr<SharedMemory> segment = NewSegment(aType);
      84           3 :   if (!segment) {
      85           0 :     return nullptr;
      86             :   }
      87           3 :   size_t size = SharedMemory::PageAlignedSize(aNBytes + aExtraSize);
      88           3 :   if (!segment->Create(size) || !segment->Map(size)) {
      89           0 :     return nullptr;
      90             :   }
      91           3 :   return segment.forget();
      92             : }
      93             : 
      94             : static already_AddRefed<SharedMemory>
      95           2 : ReadSegment(const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes, size_t aExtraSize)
      96             : {
      97           2 :   if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
      98           0 :     NS_ERROR("expected 'shmem created' message");
      99           0 :     return nullptr;
     100             :   }
     101             :   SharedMemory::SharedMemoryType type;
     102           2 :   PickleIterator iter(aDescriptor);
     103           2 :   if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, aNBytes, &type)) {
     104           0 :     return nullptr;
     105             :   }
     106           4 :   RefPtr<SharedMemory> segment = NewSegment(type);
     107           2 :   if (!segment) {
     108           0 :     return nullptr;
     109             :   }
     110           2 :   if (!segment->ReadHandle(&aDescriptor, &iter)) {
     111           0 :     NS_ERROR("trying to open invalid handle");
     112           0 :     return nullptr;
     113             :   }
     114           2 :   aDescriptor.EndRead(iter);
     115           2 :   size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize);
     116           2 :   if (!segment->Map(size)) {
     117           0 :     return nullptr;
     118             :   }
     119             :   // close the handle to the segment after it is mapped
     120           2 :   segment->CloseHandle();
     121           2 :   return segment.forget();
     122             : }
     123             : 
     124             : static void
     125           0 : DestroySegment(SharedMemory* aSegment)
     126             : {
     127             :   // the SharedMemory dtor closes and unmaps the actual OS shmem segment
     128           0 :   if (aSegment) {
     129           0 :     aSegment->Release();
     130             :   }
     131           0 : }
     132             : 
     133             : 
     134             : #if defined(DEBUG)
     135             : 
     136             : static const char sMagic[] =
     137             :     "This little piggy went to market.\n"
     138             :     "This little piggy stayed at home.\n"
     139             :     "This little piggy has roast beef,\n"
     140             :     "This little piggy had none.\n"
     141             :     "And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
     142             : 
     143             : 
     144             : struct Header {
     145             :   // Don't use size_t or bool here because their size depends on the
     146             :   // architecture.
     147             :   uint32_t mSize;
     148             :   uint32_t mUnsafe;
     149             :   char mMagic[sizeof(sMagic)];
     150             : };
     151             : 
     152             : static void
     153          16 : GetSections(Shmem::SharedMemory* aSegment,
     154             :             Header** aHeader,
     155             :             char** aFrontSentinel,
     156             :             char** aData,
     157             :             char** aBackSentinel)
     158             : {
     159          16 :   MOZ_ASSERT(aSegment && aFrontSentinel && aData && aBackSentinel,
     160             :              "null param(s)");
     161             : 
     162          16 :   *aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
     163          16 :   MOZ_ASSERT(*aFrontSentinel, "null memory()");
     164             : 
     165          16 :   *aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
     166             : 
     167          16 :   size_t pageSize = Shmem::SharedMemory::SystemPageSize();
     168          16 :   *aData = *aFrontSentinel + pageSize;
     169             : 
     170          16 :   *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
     171          16 : }
     172             : 
     173             : static Header*
     174           5 : GetHeader(Shmem::SharedMemory* aSegment)
     175             : {
     176             :   Header* header;
     177             :   char* dontcare;
     178           5 :   GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
     179           5 :   return header;
     180             : }
     181             : 
     182             : static void
     183           0 : Protect(SharedMemory* aSegment)
     184             : {
     185           0 :   MOZ_ASSERT(aSegment, "null segment");
     186           0 :   aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
     187             :                     aSegment->Size(),
     188           0 :                     RightsNone);
     189           0 : }
     190             : 
     191             : static void
     192           8 : Unprotect(SharedMemory* aSegment)
     193             : {
     194           8 :   MOZ_ASSERT(aSegment, "null segment");
     195           8 :   aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
     196             :                     aSegment->Size(),
     197           8 :                     RightsRead | RightsWrite);
     198           8 : }
     199             : 
     200             : //
     201             : // In debug builds, we specially allocate shmem segments.  The layout
     202             : // is as follows
     203             : //
     204             : //   Page 0: "front sentinel"
     205             : //     size of mapping
     206             : //     magic bytes
     207             : //   Page 1 through n-1:
     208             : //     user data
     209             : //   Page n: "back sentinel"
     210             : //     [nothing]
     211             : //
     212             : // The mapping can be in one of the following states, wrt to the
     213             : // current process.
     214             : //
     215             : //   State "unmapped": all pages are mapped with no access rights.
     216             : //
     217             : //   State "mapping": all pages are mapped with read/write access.
     218             : //
     219             : //   State "mapped": the front and back sentinels are mapped with no
     220             : //     access rights, and all the other pages are mapped with
     221             : //     read/write access.
     222             : //
     223             : // When a SharedMemory segment is first allocated, it starts out in
     224             : // the "mapping" state for the process that allocates the segment, and
     225             : // in the "unmapped" state for the other process.  The allocating
     226             : // process will then create a Shmem, which takes the segment into the
     227             : // "mapped" state, where it can be accessed by clients.
     228             : //
     229             : // When a Shmem is sent to another process in an IPDL message, the
     230             : // segment transitions into the "unmapped" state for the sending
     231             : // process, and into the "mapping" state for the receiving process.
     232             : // The receiving process will then create a Shmem from the underlying
     233             : // segment, and take the segment into the "mapped" state.
     234             : //
     235             : // In the "mapping" state, we use the front sentinel to verify the
     236             : // integrity of the shmem segment.  If valid, it has a size_t
     237             : // containing the number of bytes the user allocated followed by the
     238             : // magic bytes above.
     239             : //
     240             : // In the "mapped" state, the front and back sentinels have no access
     241             : // rights.  They act as guards against buffer overflows and underflows
     242             : // in client code; if clients touch a sentinel, they die with SIGSEGV.
     243             : //
     244             : // The "unmapped" state is used to enforce single-owner semantics of
     245             : // the shmem segment.  If a process other than the current owner tries
     246             : // to touch the segment, it dies with SIGSEGV.
     247             : //
     248             : 
     249           8 : Shmem::Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     250           8 :              SharedMemory* aSegment, id_t aId) :
     251             :     mSegment(aSegment),
     252             :     mData(nullptr),
     253           8 :     mSize(0)
     254             : {
     255           8 :   MOZ_ASSERT(mSegment, "null segment");
     256           8 :   MOZ_ASSERT(aId != 0, "invalid ID");
     257             : 
     258           8 :   Unprotect(mSegment);
     259             : 
     260             :   Header* header;
     261             :   char* frontSentinel;
     262             :   char* data;
     263             :   char* backSentinel;
     264           8 :   GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
     265             : 
     266             :   // do a quick validity check to avoid weird-looking crashes in libc
     267           8 :   char check = *frontSentinel;
     268             :   (void)check;
     269             : 
     270           8 :   MOZ_ASSERT(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
     271             :              "invalid segment");
     272           8 :   mSize = static_cast<size_t>(header->mSize);
     273             : 
     274           8 :   size_t pageSize = SharedMemory::SystemPageSize();
     275             :   // transition into the "mapped" state by protecting the front and
     276             :   // back sentinels (which guard against buffer under/overflows)
     277           8 :   mSegment->Protect(frontSentinel, pageSize, RightsNone);
     278           8 :   mSegment->Protect(backSentinel, pageSize, RightsNone);
     279             : 
     280             :   // don't set these until we know they're valid
     281           8 :   mData = data;
     282           8 :   mId = aId;
     283           8 : }
     284             : 
     285             : void
     286          11 : Shmem::AssertInvariants() const
     287             : {
     288          11 :   MOZ_ASSERT(mSegment, "null segment");
     289          11 :   MOZ_ASSERT(mData, "null data pointer");
     290          11 :   MOZ_ASSERT(mSize > 0, "invalid size");
     291             :   // if the segment isn't owned by the current process, these will
     292             :   // trigger SIGSEGV
     293          11 :   char checkMappingFront = *reinterpret_cast<char*>(mData);
     294          11 :   char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
     295             : 
     296             :   // avoid "unused" warnings for these variables:
     297             :   Unused << checkMappingFront;
     298             :   Unused << checkMappingBack;
     299          11 : }
     300             : 
     301             : void
     302           3 : Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
     303             : {
     304           3 :   AssertInvariants();
     305             : 
     306           3 :   size_t pageSize = SharedMemory::SystemPageSize();
     307           3 :   Header* header = GetHeader(mSegment);
     308             : 
     309             :   // Open this up for reading temporarily
     310           3 :   mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
     311             : 
     312           3 :   if (!header->mUnsafe) {
     313           0 :     Protect(mSegment);
     314             :   } else {
     315           3 :     mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
     316             :   }
     317           3 : }
     318             : 
     319             : // static
     320             : already_AddRefed<Shmem::SharedMemory>
     321           3 : Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     322             :              size_t aNBytes,
     323             :              SharedMemoryType aType,
     324             :              bool aUnsafe,
     325             :              bool aProtect)
     326             : {
     327           3 :   NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
     328           3 :   MOZ_ASSERT(!aProtect || !aUnsafe, "protect => !unsafe");
     329             : 
     330           3 :   size_t pageSize = SharedMemory::SystemPageSize();
     331             :   // |2*pageSize| is for the front and back sentinel
     332           6 :   RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, 2*pageSize);
     333           3 :   if (!segment) {
     334           0 :     return nullptr;
     335             :   }
     336             : 
     337             :   Header* header;
     338             :   char *frontSentinel;
     339             :   char *data;
     340             :   char *backSentinel;
     341           3 :   GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
     342             : 
     343             :   // initialize the segment with Shmem-internal information
     344             : 
     345             :   // NB: this can't be a static assert because technically pageSize
     346             :   // isn't known at compile time, event though in practice it's always
     347             :   // going to be 4KiB
     348           3 :   MOZ_ASSERT(sizeof(Header) <= pageSize,
     349             :              "Shmem::Header has gotten too big");
     350           3 :   memcpy(header->mMagic, sMagic, sizeof(sMagic));
     351           3 :   header->mSize = static_cast<uint32_t>(aNBytes);
     352           3 :   header->mUnsafe = aUnsafe;
     353             : 
     354           3 :   if (aProtect)
     355           0 :     Protect(segment);
     356             : 
     357           3 :   return segment.forget();
     358             : }
     359             : 
     360             : // static
     361             : already_AddRefed<Shmem::SharedMemory>
     362           2 : Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     363             :                     const IPC::Message& aDescriptor,
     364             :                     id_t* aId,
     365             :                     bool aProtect)
     366             : {
     367             :   size_t size;
     368           2 :   size_t pageSize = SharedMemory::SystemPageSize();
     369             :   // |2*pageSize| is for the front and back sentinels
     370           4 :   RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, 2*pageSize);
     371           2 :   if (!segment) {
     372           0 :     return nullptr;
     373             :   }
     374             : 
     375           2 :   Header* header = GetHeader(segment);
     376             : 
     377           2 :   if (size != header->mSize) {
     378             :     // Deallocation should zero out the header, so check for that.
     379           0 :     if (header->mSize || header->mUnsafe || header->mMagic[0] ||
     380           0 :         memcmp(header->mMagic, &header->mMagic[1], sizeof(header->mMagic)-1)) {
     381           0 :       NS_ERROR("Wrong size for this Shmem!");
     382             :     } else {
     383           0 :       NS_WARNING("Shmem was deallocated");
     384             :     }
     385           0 :     return nullptr;
     386             :   }
     387             : 
     388             :   // The caller of this function may not know whether the segment is
     389             :   // unsafe or not
     390           2 :   if (!header->mUnsafe && aProtect)
     391           0 :     Protect(segment);
     392             : 
     393           2 :   return segment.forget();
     394             : }
     395             : 
     396             : // static
     397             : void
     398           0 : Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     399             :                SharedMemory* aSegment)
     400             : {
     401           0 :   if (!aSegment)
     402           0 :     return;
     403             : 
     404           0 :   size_t pageSize = SharedMemory::SystemPageSize();
     405             :   Header* header;
     406             :   char *frontSentinel;
     407             :   char *data;
     408             :   char *backSentinel;
     409           0 :   GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
     410             : 
     411           0 :   aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
     412           0 :   memset(header->mMagic, 0, sizeof(sMagic));
     413           0 :   header->mSize = 0;
     414           0 :   header->mUnsafe = false;          // make it "safe" so as to catch errors
     415             : 
     416           0 :   DestroySegment(aSegment);
     417             : }
     418             : 
     419             : 
     420             : #else  // !defined(DEBUG)
     421             : 
     422             : // static
     423             : already_AddRefed<Shmem::SharedMemory>
     424             : Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     425             :              size_t aNBytes,
     426             :              SharedMemoryType aType,
     427             :              bool /*unused*/,
     428             :              bool /*unused*/)
     429             : {
     430             :   RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, sizeof(uint32_t));
     431             :   if (!segment) {
     432             :     return nullptr;
     433             :   }
     434             : 
     435             :   *PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
     436             : 
     437             :   return segment.forget();
     438             : }
     439             : 
     440             : // static
     441             : already_AddRefed<Shmem::SharedMemory>
     442             : Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     443             :                     const IPC::Message& aDescriptor,
     444             :                     id_t* aId,
     445             :                     bool /*unused*/)
     446             : {
     447             :   size_t size;
     448             :   RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, sizeof(uint32_t));
     449             :   if (!segment) {
     450             :     return nullptr;
     451             :   }
     452             : 
     453             :   // this is the only validity check done in non-DEBUG builds
     454             :   if (size != static_cast<size_t>(*PtrToSize(segment))) {
     455             :     return nullptr;
     456             :   }
     457             : 
     458             :   return segment.forget();
     459             : }
     460             : 
     461             : // static
     462             : void
     463             : Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     464             :                SharedMemory* aSegment)
     465             : {
     466             :   DestroySegment(aSegment);
     467             : }
     468             : 
     469             : #endif  // if defined(DEBUG)
     470             : 
     471             : IPC::Message*
     472           3 : Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     473             :                base::ProcessId aTargetPid,
     474             :                int32_t routingId)
     475             : {
     476           3 :   AssertInvariants();
     477             : 
     478           6 :   IPC::Message *msg = new ShmemCreated(routingId, mId, mSize, mSegment->Type());
     479           3 :   if (!mSegment->ShareHandle(aTargetPid, msg)) {
     480           0 :     return nullptr;
     481             :   }
     482             :   // close the handle to the segment after it is shared
     483           3 :   mSegment->CloseHandle();
     484           3 :   return msg;
     485             : }
     486             : 
     487             : IPC::Message*
     488           0 : Shmem::UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
     489             :                    base::ProcessId aTargetPid,
     490             :                    int32_t routingId)
     491             : {
     492           0 :   AssertInvariants();
     493           0 :   return new ShmemDestroyed(routingId, mId);
     494             : }
     495             : 
     496             : } // namespace ipc
     497             : } // namespace mozilla

Generated by: LCOV version 1.13