ipc/glue/Shmem.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     8 #include "Shmem.h"
    10 #include "ProtocolUtils.h"
    11 #include "SharedMemoryBasic.h"
    12 #include "SharedMemorySysV.h"
    14 #include "nsAutoPtr.h"
    15 #include "mozilla/unused.h"
    18 namespace mozilla {
    19 namespace ipc {
    21 class ShmemCreated : public IPC::Message
    22 {
    23 private:
    24   typedef Shmem::id_t id_t;
    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   }
    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.
    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   }
    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   }
    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   }
    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
    91   void Log(const std::string& aPrefix,
    92            FILE* aOutf) const
    93   {
    94     fputs("(special ShmemCreated msg)", aOutf);
    95   }
    96 };
    98 class ShmemDestroyed : public IPC::Message
    99 {
   100 private:
   101   typedef Shmem::id_t id_t;
   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 };
   113 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
   114 static Shmem::SharedMemory*
   115 CreateSegment(size_t aNBytes, SharedMemorySysV::Handle aHandle)
   116 {
   117   nsAutoPtr<SharedMemory> segment;
   119   if (SharedMemorySysV::IsHandleValid(aHandle)) {
   120     segment = new SharedMemorySysV(aHandle);
   121   }
   122   else {
   123     segment = new SharedMemorySysV();
   125     if (!segment->Create(aNBytes))
   126       return 0;
   127   }
   128   if (!segment->Map(aNBytes))
   129     return 0;
   131   segment->AddRef();
   132   return segment.forget();
   133 }
   134 #endif
   136 static Shmem::SharedMemory*
   137 CreateSegment(size_t aNBytes, SharedMemoryBasic::Handle aHandle)
   138 {
   139   nsAutoPtr<SharedMemory> segment;
   141   if (SharedMemoryBasic::IsHandleValid(aHandle)) {
   142     segment = new SharedMemoryBasic(aHandle);
   143   }
   144   else {
   145     segment = new SharedMemoryBasic();
   147     if (!segment->Create(aNBytes))
   148       return 0;
   149   }
   150   if (!segment->Map(aNBytes))
   151     return 0;
   153   segment->AddRef();
   154   return segment.forget();
   155 }
   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 }
   166 #if defined(DEBUG)
   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";
   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 };
   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)");
   194   *aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
   195   NS_ABORT_IF_FALSE(*aFrontSentinel, "NULL memory()");
   197   *aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
   199   size_t pageSize = Shmem::SharedMemory::SystemPageSize();
   200   *aData = *aFrontSentinel + pageSize;
   202   *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
   203 }
   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 }
   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 }
   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 }
   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 //
   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");
   290   Unprotect(mSegment);
   292   Header* header;
   293   char* frontSentinel;
   294   char* data;
   295   char* backSentinel;
   296   GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
   298   // do a quick validity check to avoid weird-looking crashes in libc
   299   char check = *frontSentinel;
   300   (void)check;
   302   NS_ABORT_IF_FALSE(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
   303                       "invalid segment");
   304   mSize = static_cast<size_t>(header->mSize);
   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);
   312   // don't set these until we know they're valid
   313   mData = data;
   314   mId = aId;
   315 }
   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);
   328   // avoid "unused" warnings for these variables:
   329   unused << checkMappingFront;
   330   unused << checkMappingBack;
   331 }
   333 void
   334 Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
   335 {
   336   AssertInvariants();
   338   size_t pageSize = SharedMemory::SystemPageSize();
   339   Header* header = GetHeader(mSegment);
   341   // Open this up for reading temporarily
   342   mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
   344   if (!header->mUnsafe) {
   345     Protect(mSegment);
   346   } else {
   347     mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
   348   }
   349 }
   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");
   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);
   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   }
   378   if (!segment)
   379     return 0;
   381   Header* header;
   382   char *frontSentinel;
   383   char *data;
   384   char *backSentinel;
   385   GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
   387   // initialize the segment with Shmem-internal information
   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;
   398   if (aProtect)
   399     Protect(segment);
   401   return segment;
   402 }
   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   }
   416   void* iter = 0;
   417   SharedMemory::SharedMemoryType type;
   418   size_t size;
   419   if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type))
   420     return 0;
   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);
   427   if (SharedMemory::TYPE_BASIC == type) {
   428     SharedMemoryBasic::Handle handle;
   429     if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
   430       return 0;
   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;
   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   }
   456   if (!segment)
   457     return 0;
   459   Header* header = GetHeader(segment);
   461   if (size != header->mSize) {
   462     NS_ERROR("Wrong size for this Shmem!");
   463     delete segment;
   464     return nullptr;
   465   }
   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);
   472   return segment;
   473 }
   475 // static
   476 void
   477 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   478                SharedMemory* aSegment)
   479 {
   480   if (!aSegment)
   481     return;
   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);
   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
   495   DestroySegment(aSegment);
   496 }
   499 #else  // !defined(DEBUG)
   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;
   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   }
   523   if (!segment)
   524     return 0;
   526   *PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
   528   return segment;
   529 }
   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   }
   542   SharedMemory::SharedMemoryType type;
   543   void* iter = 0;
   544   size_t size;
   545   if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type))
   546     return 0;
   548   SharedMemory* segment = 0;
   549   size_t segmentSize = SharedMemory::PageAlignedSize(size + sizeof(uint32_t));
   551   if (SharedMemory::TYPE_BASIC == type) {
   552     SharedMemoryBasic::Handle handle;
   553     if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle))
   554       return 0;
   556     if (!SharedMemoryBasic::IsHandleValid(handle)) {
   557       return nullptr;
   558     }
   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;
   568     if (!SharedMemorySysV::IsHandleValid(handle)) {
   569       return nullptr;
   570     }
   571     segment = CreateSegment(segmentSize, handle);
   572   }
   573 #endif
   574   else {
   575     return nullptr;
   576   }
   578   if (!segment)
   579     return 0;
   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   }
   587   return segment;
   588 }
   590 // static
   591 void
   592 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   593                SharedMemory* aSegment)
   594 {
   595   DestroySegment(aSegment);
   596 }
   598 #endif  // if defined(DEBUG)
   600 int
   601 Shmem::GetSysVID() const
   602 {
   603 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
   604   AssertInvariants();
   606   if (mSegment->Type() != SharedMemory::TYPE_SYSV) {
   607     NS_ERROR("Can't call GetSysVID() on a non-SysV Shmem!");
   608     return -1;
   609   }
   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 }
   619 IPC::Message*
   620 Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
   621                base::ProcessHandle aProcess,
   622                int32_t routingId)
   623 {
   624   AssertInvariants();
   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;
   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   }
   645   return 0;
   646 }
   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 }
   657 } // namespace ipc
   658 } // namespace mozilla

mercurial