Bug 598866, part 1: Add library support for an "unsafe" Shmem variant. r=joe
authorChris Jones <jones.chris.g@gmail.com>
Fri, 05 Nov 2010 02:17:07 -0500
changeset 56895 336b100dd53cf72edd9fdc5f7a110ac8f7b8063f
parent 56894 ef66ba9e8c622524e6ecad31a8bd3663a098deb6
child 56896 8cd0af801cb97026ee3a5403e204a2fa6fbe5275
push idunknown
push userunknown
push dateunknown
reviewersjoe
bugs598866
milestone2.0b8pre
Bug 598866, part 1: Add library support for an "unsafe" Shmem variant. r=joe
ipc/glue/Shmem.cpp
ipc/glue/Shmem.h
ipc/ipdl/ipdl/lower.py
--- a/ipc/glue/Shmem.cpp
+++ b/ipc/glue/Shmem.cpp
@@ -211,40 +211,53 @@ static const char sMagic[] =
     "This little piggy went to market.\n"
     "This little piggy stayed at home.\n"
     "This little piggy has roast beef,\n"
     "This little piggy had none.\n"
     "And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
 
 
 struct Header {
-  // Don't use size_t here because the data type's length depends
-  // on the architecture.
+  // Don't use size_t or bool here because their size depends on the
+  // architecture.
   uint32 mSize;
+  uint32 mUnsafe;
   char mMagic[sizeof(sMagic)];
 };
 
 static void
 GetSections(Shmem::SharedMemory* aSegment,
+            Header** aHeader,
             char** aFrontSentinel,
             char** aData,
             char** aBackSentinel)
 {
   NS_ABORT_IF_FALSE(aSegment && aFrontSentinel && aData && aBackSentinel,
                     "NULL param(s)");
 
   *aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
   NS_ABORT_IF_FALSE(*aFrontSentinel, "NULL memory()");
 
+  *aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
+
   size_t pageSize = Shmem::SharedMemory::SystemPageSize();
   *aData = *aFrontSentinel + pageSize;
 
   *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
 }
 
+static Header*
+GetHeader(Shmem::SharedMemory* aSegment)
+{
+  Header* header;
+  char* dontcare;
+  GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
+  return header;
+}
+
 static void
 Protect(SharedMemory* aSegment)
 {
   NS_ABORT_IF_FALSE(aSegment, "NULL segment");
   aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
                     aSegment->Size(),
                     RightsNone);
 }
@@ -313,26 +326,26 @@ Shmem::Shmem(IHadBetterBeIPDLCodeCalling
     mData(0),
     mSize(0)
 {
   NS_ABORT_IF_FALSE(mSegment, "NULL segment");
   NS_ABORT_IF_FALSE(aId != 0, "invalid ID");
 
   Unprotect(mSegment);
 
+  Header* header;
   char* frontSentinel;
   char* data;
   char* backSentinel;
-  GetSections(aSegment, &frontSentinel, &data, &backSentinel);
+  GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
 
   // do a quick validity check to avoid weird-looking crashes in libc
   char check = *frontSentinel;
   (void)check;
 
-  Header* header = reinterpret_cast<Header*>(frontSentinel);
   NS_ABORT_IF_FALSE(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
                       "invalid segment");
   mSize = static_cast<size_t>(header->mSize);
 
   size_t pageSize = SharedMemory::SystemPageSize();
   // transition into the "mapped" state by protecting the front and
   // back sentinels (which guard against buffer under/overflows)
   mSegment->Protect(frontSentinel, pageSize, RightsNone);
@@ -355,27 +368,40 @@ Shmem::AssertInvariants() const
   char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
   checkMappingFront = checkMappingBack; // avoid "unused" warnings
 }
 
 void
 Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
 {
   AssertInvariants();
-  Protect(mSegment);
+
+  size_t pageSize = SharedMemory::SystemPageSize();
+  Header* header = GetHeader(mSegment);
+
+  // Open this up for reading temporarily
+  mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
+
+  if (!header->mUnsafe) {
+    Protect(mSegment);
+  } else {
+    mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
+  }
 }
 
 // static
 Shmem::SharedMemory*
 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
              size_t aNBytes,
              SharedMemoryType aType,
+             bool aUnsafe,
              bool aProtect)
 {
   NS_ASSERTION(aNBytes <= PR_UINT32_MAX, "Will truncate shmem segment size!");
+  NS_ABORT_IF_FALSE(!aProtect || !aUnsafe, "protect => !unsafe");
 
   size_t pageSize = SharedMemory::SystemPageSize();
   SharedMemory* segment = nsnull;
   // |2*pageSize| is for the front and back sentinel
   size_t segmentSize = PageAlignedSize(aNBytes + 2*pageSize);
 
   if (aType == SharedMemory::TYPE_BASIC)
     segment = CreateSegment(segmentSize, SharedMemoryBasic::NULLHandle());
@@ -384,25 +410,32 @@ Shmem::Alloc(IHadBetterBeIPDLCodeCalling
     segment = CreateSegment(segmentSize, SharedMemorySysV::NULLHandle());
 #endif
   else
     NS_RUNTIMEABORT("unknown shmem type");
 
   if (!segment)
     return 0;
 
+  Header* header;
   char *frontSentinel;
   char *data;
   char *backSentinel;
-  GetSections(segment, &frontSentinel, &data, &backSentinel);
+  GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
 
   // initialize the segment with Shmem-internal information
-  Header* header = reinterpret_cast<Header*>(frontSentinel);
+
+  // NB: this can't be a static assert because technically pageSize
+  // isn't known at compile time, event though in practice it's always
+  // going to be 4KiB
+  NS_ABORT_IF_FALSE(sizeof(Header) <= pageSize,
+                    "Shmem::Header has gotten too big");
   memcpy(header->mMagic, sMagic, sizeof(sMagic));
   header->mSize = static_cast<uint32>(aNBytes);
+  header->mUnsafe = aUnsafe;
 
   if (aProtect)
     Protect(segment);
 
   return segment;
 }
 
 // static
@@ -448,52 +481,57 @@ Shmem::OpenExisting(IHadBetterBeIPDLCode
 #endif
   else {
     NS_RUNTIMEABORT("unknown shmem type");
   }
 
   if (!segment)
     return 0;
 
-  if (aProtect)
+  // The caller of this function may not know whether the segment is
+  // unsafe or not
+  Header* header = GetHeader(segment);
+  if (!header->mUnsafe && aProtect)
     Protect(segment);
 
   return segment;
 }
 
 // static
 void
 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
                SharedMemory* aSegment)
 {
   if (!aSegment)
     return;
 
   size_t pageSize = SharedMemory::SystemPageSize();
+  Header* header;
   char *frontSentinel;
   char *data;
   char *backSentinel;
-  GetSections(aSegment, &frontSentinel, &data, &backSentinel);
+  GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
 
   aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
-  Header* header = reinterpret_cast<Header*>(frontSentinel);
   memset(header->mMagic, 0, sizeof(sMagic));
   header->mSize = 0;
+  header->mUnsafe = false;          // make it "safe" so as to catch errors
 
   DestroySegment(aSegment);
 }
 
 
 #else  // !defined(DEBUG)
 
 // static
 Shmem::SharedMemory*
 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
              size_t aNBytes, 
              SharedMemoryType aType,
+             bool /*unused*/,
              bool /*unused*/)
 {
   SharedMemory *segment = nsnull;
 
   if (aType == SharedMemory::TYPE_BASIC)
     segment = CreateSegment(PageAlignedSize(aNBytes + sizeof(uint32)),
                             SharedMemoryBasic::NULLHandle());
 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
--- a/ipc/glue/Shmem.h
+++ b/ipc/glue/Shmem.h
@@ -223,16 +223,17 @@ public:
     mSize = 0;
     mId = 0;
   }
 
   static SharedMemory*
   Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
         size_t aNBytes,
         SharedMemoryType aType,
+        bool aUnsafe,
         bool aProtect=false);
 
   // Prepare this to be shared with |aProcess|.  Return an IPC message
   // that contains enough information for the other process to map
   // this segment in OpenExisting() below.  Return a new message if
   // successful (owned by the caller), NULL if not.
   IPC::Message*
   ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -186,17 +186,17 @@ def _shmemId(shmemexpr):
 
 def _shmemSegment(shmemexpr):
     return ExprCall(ExprSelect(shmemexpr, '.', 'Segment'),
                     args=[ _shmemBackstagePass() ])
 
 def _shmemAlloc(size, type):
     # starts out UNprotected
     return ExprCall(ExprVar('Shmem::Alloc'),
-                    args=[ _shmemBackstagePass(), size, type ])
+                    args=[ _shmemBackstagePass(), size, type, ExprLiteral.FALSE ])
 
 def _shmemDealloc(rawmemvar):
     return ExprCall(ExprVar('Shmem::Dealloc'),
                     args=[ _shmemBackstagePass(), rawmemvar ])
 
 def _shmemShareTo(shmemvar, processvar, route):
     return ExprCall(ExprSelect(shmemvar, '.', 'ShareTo'),
                     args=[ _shmemBackstagePass(),