bug 523174: add a higher-level Shmem class that implements exclusive access rights between parent/child actors and does over/underflow checking. (Also adds a stop-gap lower-level SharedMemory class in lieu of bug 523172.) r=joedrew
authorChris Jones <jones.chris.g@gmail.com>
Fri, 04 Dec 2009 12:45:15 -0600
changeset 36152 5ffc5409203c649967a622154eb7437eeb10fdd8
parent 36151 21051d997076ea0b57572d562c816744a20f6fec
child 36153 dc4201e0d9e5e503d5c94fae43371056eff397d2
push idunknown
push userunknown
push dateunknown
reviewersjoedrew
bugs523174, 523172
milestone1.9.3a1pre
bug 523174: add a higher-level Shmem class that implements exclusive access rights between parent/child actors and does over/underflow checking. (Also adds a stop-gap lower-level SharedMemory class in lieu of bug 523172.) r=joedrew
gfx/ipc/SharedDIB.cpp
ipc/chromium/src/base/shared_memory.h
ipc/chromium/src/base/shared_memory_posix.cc
ipc/chromium/src/base/shared_memory_win.cc
ipc/chromium/src/base/stats_table.cc
ipc/chromium/src/chrome/common/transport_dib_win.cc
ipc/glue/Makefile.in
ipc/glue/SharedMemory.h
ipc/glue/SharedMemory_posix.cpp
ipc/glue/SharedMemory_windows.cpp
ipc/glue/Shmem.cpp
ipc/glue/Shmem.h
--- a/gfx/ipc/SharedDIB.cpp
+++ b/gfx/ipc/SharedDIB.cpp
@@ -52,17 +52,17 @@ SharedDIB::~SharedDIB()
 }
 
 nsresult
 SharedDIB::Create(PRUint32 aSize)
 {
   Close();
 
   mShMem = new base::SharedMemory();
-  if (!mShMem || !mShMem->Create(std::wstring(L""), false, false, aSize))
+  if (!mShMem || !mShMem->Create("", false, false, aSize))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Map the entire section
   if (!mShMem->Map(0))
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -62,17 +62,21 @@ class SharedMemory {
   static SharedMemoryHandle NULLHandle();
 
   // Creates or opens a shared memory segment based on a name.
   // If read_only is true, opens the memory as read-only.
   // If open_existing is true, and the shared memory already exists,
   // opens the existing shared memory and ignores the size parameter.
   // If name is the empty string, use a unique name.
   // Returns true on success, false on failure.
+#ifdef CHROMIUM_MOZILLA_BUILD
+  bool Create(const std::string& name, bool read_only, bool open_existing,
+#else
   bool Create(const std::wstring& name, bool read_only, bool open_existing,
+#endif
               size_t size);
 
   // Deletes resources associated with a shared memory segment based on name.
   // Not all platforms require this call.
   bool Delete(const std::wstring& name);
 
   // Opens a shared memory segment based on a name.
   // If read_only is true, opens for read-only access.
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -65,20 +65,28 @@ bool SharedMemory::IsHandleValid(const S
   return handle.fd >= 0;
 }
 
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() {
   return SharedMemoryHandle();
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+bool SharedMemory::Create(const std::string &cname, bool read_only,
+#else
 bool SharedMemory::Create(const std::wstring &name, bool read_only,
+#endif
                           bool open_existing, size_t size) {
   read_only_ = read_only;
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+  std::wstring name = UTF8ToWide(cname);
+#endif
+
   int posix_flags = 0;
   posix_flags |= read_only ? O_RDONLY : O_RDWR;
   if (!open_existing || mapped_file_ <= 0)
     posix_flags |= O_CREAT;
 
   if (!CreateOrOpen(name, posix_flags, size))
     return false;
 
--- a/ipc/chromium/src/base/shared_memory_win.cc
+++ b/ipc/chromium/src/base/shared_memory_win.cc
@@ -1,16 +1,19 @@
 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/shared_memory.h"
 
 #include "base/logging.h"
 #include "base/win_util.h"
+#ifdef CHROMIUM_MOZILLA_BUILD
+#  include "base/string_util.h"
+#endif
 
 namespace base {
 
 SharedMemory::SharedMemory()
     : mapped_file_(NULL),
       memory_(NULL),
       read_only_(false),
       max_size_(0),
@@ -50,20 +53,28 @@ bool SharedMemory::IsHandleValid(const S
   return handle != NULL;
 }
 
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() {
   return NULL;
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+bool SharedMemory::Create(const std::string &cname, bool read_only,
+#else
 bool SharedMemory::Create(const std::wstring &name, bool read_only,
+#endif
                           bool open_existing, size_t size) {
   DCHECK(mapped_file_ == NULL);
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+  std::wstring name = UTF8ToWide(cname);
+#endif
+
   name_ = name;
   read_only_ = read_only;
   mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
       read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, static_cast<DWORD>(size),
       name.empty() ? NULL : name.c_str());
   if (!mapped_file_)
     return false;
 
--- a/ipc/chromium/src/base/stats_table.cc
+++ b/ipc/chromium/src/base/stats_table.cc
@@ -165,17 +165,21 @@ class StatsTablePrivate {
 };
 
 // static
 StatsTablePrivate* StatsTablePrivate::New(const std::string& name,
                                           int size,
                                           int max_threads,
                                           int max_counters) {
   scoped_ptr<StatsTablePrivate> priv(new StatsTablePrivate());
+#ifdef CHROMIUM_MOZILLA_BUILD
+  if (!priv->shared_memory_.Create(name, false, true,
+#else
   if (!priv->shared_memory_.Create(base::SysUTF8ToWide(name), false, true,
+#endif
                                    size))
     return NULL;
   if (!priv->shared_memory_.Map(size))
     return NULL;
   void* memory = priv->shared_memory_.memory();
 
   TableHeader* header = static_cast<TableHeader*>(memory);
 
--- a/ipc/chromium/src/chrome/common/transport_dib_win.cc
+++ b/ipc/chromium/src/chrome/common/transport_dib_win.cc
@@ -22,17 +22,21 @@ TransportDIB::TransportDIB(HANDLE handle
 // static
 TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) {
   size_t allocation_granularity = base::SysInfo::VMAllocationGranularity();
   size = size / allocation_granularity + 1;
   size = size * allocation_granularity;
 
   TransportDIB* dib = new TransportDIB;
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+  if (!dib->shared_memory_.Create("", false /* read write */,
+#else
   if (!dib->shared_memory_.Create(L"", false /* read write */,
+#endif
                                   true /* open existing */, size)) {
     delete dib;
     return NULL;
   }
 
   dib->size_ = size;
   dib->sequence_num_ = sequence_num;
 
--- a/ipc/glue/Makefile.in
+++ b/ipc/glue/Makefile.in
@@ -52,35 +52,43 @@ EXPORTS_NAMESPACES = IPC mozilla/ipc
 EXPORTS_IPC = IPCMessageUtils.h
 
 EXPORTS_mozilla/ipc = \
   AsyncChannel.h \
   GeckoChildProcessHost.h \
   GeckoThread.h \
   ProtocolUtils.h \
   RPCChannel.h \
+  SharedMemory.h \
+  Shmem.h \
   SyncChannel.h \
   ScopedXREEmbed.h \
   $(NULL)
 
 ENABLE_CXX_EXCEPTIONS = 1
 
 CPPSRCS += \
   AsyncChannel.cpp \
   GeckoChildProcessHost.cpp \
   GeckoThread.cpp \
   MessagePump.cpp \
   RPCChannel.cpp \
   ScopedXREEmbed.cpp \
+  Shmem.cpp \
   StringUtil.cpp \
   SyncChannel.cpp \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
-CPPSRCS += WindowsMessageLoop.cpp
+CPPSRCS += \
+  SharedMemory_windows.cpp \
+  WindowsMessageLoop.cpp \
+  $(NULL)
+else
+CPPSRCS += SharedMemory_posix.cpp
 endif
 
 include $(topsrcdir)/ipc/app/defs.mk
 DEFINES += -DMOZ_CHILD_PROCESS_NAME="\"$(MOZ_CHILD_PROCESS_NAME)\""
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/glue/SharedMemory.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_ipc_SharedMemory_h
+#define mozilla_ipc_SharedMemory_h
+
+#include "base/shared_memory.h"
+
+#include "nsDebug.h"
+
+//
+// This is a low-level wrapper around platform shared memory.  Don't
+// use it directly; use Shmem allocated through IPDL interfaces.
+//
+namespace {
+enum Rights {
+  RightsNone = 0,
+  RightsRead = 1 << 0,
+  RightsWrite = 1 << 1
+};
+}
+
+namespace mozilla {
+namespace ipc {
+
+class SharedMemory : public base::SharedMemory
+{
+public:
+  typedef base::SharedMemoryHandle SharedMemoryHandle;
+
+  SharedMemory() :
+    base::SharedMemory(),
+    mSize(0)
+  {
+  }
+
+  SharedMemory(const SharedMemoryHandle& aHandle) :
+    base::SharedMemory(aHandle, false),
+    mSize(0)
+  {
+  }
+
+  bool Map(size_t nBytes)
+  {
+    bool ok = base::SharedMemory::Map(nBytes);
+    if (ok)
+      mSize = nBytes;
+    return ok;
+  }
+
+  size_t Size()
+  {
+    return mSize;
+  }
+
+  void
+  Protect(char* aAddr, size_t aSize, int aRights)
+  {
+    char* memStart = reinterpret_cast<char*>(memory());
+    if (!memStart)
+      NS_RUNTIMEABORT("SharedMemory region points at NULL!");
+    char* memEnd = memStart + Size();
+
+    char* protStart = aAddr;
+    if (!protStart)
+      NS_RUNTIMEABORT("trying to Protect() a NULL region!");
+    char* protEnd = protStart + aSize;
+
+    if (!(memStart <= protStart
+          && protEnd <= memEnd))
+      NS_RUNTIMEABORT("attempt to Protect() a region outside this SharedMemory");
+
+    // checks alignment etc.
+    SystemProtect(aAddr, aSize, aRights);
+  }
+
+  static void SystemProtect(char* aAddr, size_t aSize, int aRights);
+  static size_t SystemPageSize();
+
+private:
+  // NB: we have to track this because shared_memory_win.cc doesn't
+  size_t mSize;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ipc_SharedMemory_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/SharedMemory_posix.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <sys/mman.h>         // mprotect
+#include <unistd.h>           // sysconf
+
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+namespace ipc {
+
+void
+SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights)
+{
+  int flags = 0;
+  if (aRights & RightsRead)
+    flags |= PROT_READ;
+  if (aRights & RightsWrite)
+    flags |= PROT_WRITE;
+  if (RightsNone == aRights)
+    flags = PROT_NONE;
+
+  if (0 < mprotect(aAddr, aSize, flags))
+    NS_RUNTIMEABORT("can't mprotect()");
+}
+
+size_t
+SharedMemory::SystemPageSize()
+{
+  return sysconf(_SC_PAGESIZE);
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/SharedMemory_windows.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+namespace ipc {
+
+void
+SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights)
+{
+  DWORD flags;
+  if ((aRights & RightsRead) && (aRights & RightsWrite))
+    flags = PAGE_READWRITE;
+  else if (aRights & RightsRead)
+    flags = PAGE_READONLY;
+  else
+    flags = PAGE_NOACCESS;
+
+  DWORD oldflags;
+  if (!VirtualProtect(aAddr, aSize, flags, &oldflags))
+    NS_RUNTIMEABORT("can't VirtualProtect()");
+}
+
+size_t
+SharedMemory::SystemPageSize()
+{
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwPageSize;
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Shmem.cpp
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <math.h>
+
+#include "Shmem.h"
+
+#include "nsAutoPtr.h"
+
+
+#if defined(DEBUG)
+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";
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+
+#if defined(DEBUG)
+
+namespace {
+
+struct Header
+{
+  size_t mSize;
+  char mMagic[sizeof(sMagic)];
+};
+
+void
+GetSections(Shmem::SharedMemory* aSegment,
+            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()");
+
+  size_t pageSize = Shmem::SharedMemory::SystemPageSize();
+  *aData = *aFrontSentinel + pageSize;
+
+  *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
+}
+
+} // namespace <anon>
+
+//
+// In debug builds, we specially allocate shmem segments.  The layout
+// is as follows
+//
+//   Page 0: "front sentinel"
+//     size of mapping
+//     magic bytes
+//   Page 1 through n-1:
+//     user data
+//   Page n: "back sentinel"
+//     [nothing]
+//
+// The mapping can be in one of the following states, wrt to the
+// current process.
+//
+//   State "unmapped": all pages are mapped with no access rights.
+//
+//   State "mapping": all pages are mapped with read/write access.
+//
+//   State "mapped": the front and back sentinels are mapped with no
+//     access rights, and all the other pages are mapped with
+//     read/write access.
+//
+// When a SharedMemory segment is first allocated, it starts out in
+// the "mapping" state for the process that allocates the segment, and
+// in the "unmapped" state for the other process.  The allocating
+// process will then create a Shmem, which takes the segment into the
+// "mapped" state, where it can be accessed by clients.
+//
+// When a Shmem is sent to another process in an IPDL message, the
+// segment transitions into the "unmapped" state for the sending
+// process, and into the "mapping" state for the receiving process.
+// The receiving process will then create a Shmem from the underlying
+// segment, and take the segment into the "mapped" state.
+//
+// In the "mapping" state, we use the front sentinel to verify the
+// integrity of the shmem segment.  If valid, it has a size_t
+// containing the number of bytes the user allocated followed by the
+// magic bytes above.
+//
+// In the "mapped" state, the front and back sentinels have no access
+// rights.  They act as guards against buffer overflows and underflows
+// in client code; if clients touch a sentinel, they die with SIGSEGV.
+//
+// The "unmapped" state is used to enforce single-owner semantics of
+// the shmem segment.  If a process other than the current owner tries
+// to touch the segment, it dies with SIGSEGV.
+//
+
+Shmem::Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+             SharedMemory* aSegment, id_t aId) :
+    mSegment(aSegment),
+    mData(0),
+    mSize(0)
+{
+  NS_ABORT_IF_FALSE(mSegment, "NULL segment");
+  NS_ABORT_IF_FALSE(aId != 0, "invalid ID");
+
+  Unprotect(mSegment);
+
+  char* frontSentinel;
+  char* data;
+  char* backSentinel;
+  GetSections(aSegment, &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 = 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);
+  mSegment->Protect(backSentinel, pageSize, RightsNone);
+
+  // don't set these until we know they're valid
+  mData = data;
+  mId = aId;
+}
+
+void
+Shmem::AssertInvariants() const
+{
+  NS_ABORT_IF_FALSE(mSegment, "NULL segment");
+  NS_ABORT_IF_FALSE(mData, "NULL data pointer");
+  NS_ABORT_IF_FALSE(mSize > 0, "invalid size");
+  // if the segment isn't owned by the current process, these will
+  // trigger SIGSEGV
+  char checkMappingFront = *reinterpret_cast<char*>(mData);
+  char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
+  checkMappingFront = checkMappingBack; // avoid "unused" warnings
+}
+
+void
+Shmem::Protect(SharedMemory* aSegment)
+{
+  NS_ABORT_IF_FALSE(aSegment, "NULL segment");
+  aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
+                    aSegment->Size(),
+                    RightsNone);
+}
+
+void
+Shmem::Unprotect(SharedMemory* aSegment)
+{
+  NS_ABORT_IF_FALSE(aSegment, "NULL segment");
+  aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
+                    aSegment->Size(),
+                    RightsRead | RightsWrite);
+}
+
+Shmem::SharedMemory*
+Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+             size_t aNBytes,
+             bool aProtect)
+{
+  size_t pageSize = SharedMemory::SystemPageSize();
+  // |2*pageSize| is for the front and back sentinel
+  SharedMemory* segment = CreateSegment(PageAlignedSize(aNBytes + 2*pageSize));
+  if (!segment)
+    return 0;
+
+  char *frontSentinel;
+  char *data;
+  char *backSentinel;
+  GetSections(segment, &frontSentinel, &data, &backSentinel);
+
+  // initialize the segment with Shmem-internal information
+  Header* header = reinterpret_cast<Header*>(frontSentinel);
+  memcpy(header->mMagic, sMagic, sizeof(sMagic));
+  header->mSize = aNBytes;
+
+  if (aProtect)
+    Protect(segment);
+
+  return segment;
+}
+
+Shmem::SharedMemory*
+Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+                    SharedMemoryHandle aHandle,
+                    size_t aNBytes,
+                    bool aProtect)
+{
+  if (!SharedMemory::IsHandleValid(aHandle))
+    NS_RUNTIMEABORT("trying to open invalid handle");
+
+  size_t pageSize = SharedMemory::SystemPageSize();
+  // |2*pageSize| is for the front and back sentinels
+  SharedMemory* segment = CreateSegment(PageAlignedSize(aNBytes + 2*pageSize),
+                                        aHandle);
+  if (!segment)
+    return 0;
+
+  if (aProtect)
+    Protect(segment);
+
+  return segment;
+}
+
+void
+Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+               SharedMemory* aSegment)
+{
+  if (!aSegment)
+    return;
+
+  size_t pageSize = SharedMemory::SystemPageSize();
+  char *frontSentinel;
+  char *data;
+  char *backSentinel;
+  GetSections(aSegment, &frontSentinel, &data, &backSentinel);
+
+  aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
+  Header* header = reinterpret_cast<Header*>(frontSentinel);
+  memset(header->mMagic, 0, sizeof(sMagic));
+  header->mSize = 0;
+
+  DestroySegment(aSegment);
+}
+
+
+#else  // !defined(DEBUG)
+
+Shmem::SharedMemory*
+Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+             size_t aNBytes, 
+             bool /*unused*/)
+{
+  SharedMemory* segment =
+    CreateSegment(PageAlignedSize(aNBytes + sizeof(size_t)));
+  if (!segment)
+    return 0;
+
+  *PtrToSize(segment) = aNBytes;
+
+  return segment;
+}
+
+Shmem::SharedMemory*
+Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+                    SharedMemoryHandle aHandle,
+                    size_t aNBytes,
+                    bool /* unused */)
+{
+  SharedMemory* segment =
+    CreateSegment(PageAlignedSize(aNBytes + sizeof(size_t)), aHandle);
+  if (!segment)
+    return 0;
+
+  // this is the only validity check done OPT builds
+  if (aNBytes != *PtrToSize(segment))
+    NS_RUNTIMEABORT("Alloc() segment size disagrees with OpenExisting()'s");
+
+  return segment;
+}
+
+void
+Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+               SharedMemory* aSegment)
+{
+  DestroySegment(aSegment);
+}
+
+
+#endif  // if defined(DEBUG)
+
+
+Shmem::SharedMemory*
+Shmem::CreateSegment(size_t aNBytes, SharedMemoryHandle aHandle)
+{
+  nsAutoPtr<SharedMemory> segment;
+
+  if (SharedMemory::IsHandleValid(aHandle)) {
+    segment = new SharedMemory(aHandle);
+  }
+  else {
+    segment = new SharedMemory();
+    if (!segment->Create("", false, false, aNBytes))
+      return 0;
+  }
+  if (!segment->Map(aNBytes))
+    return 0;
+  return segment.forget();
+}
+
+void
+Shmem::DestroySegment(SharedMemory* aSegment)
+{
+  // the SharedMemory dtor closes and unmaps the actual OS shmem segment
+  delete aSegment;
+}
+
+size_t
+Shmem::PageAlignedSize(size_t aSize)
+{
+  size_t pageSize = SharedMemory::SystemPageSize();
+  size_t nPagesNeeded = int(ceil(double(aSize) / double(pageSize)));
+  return pageSize * nPagesNeeded;
+}
+
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Shmem.h
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_ipc_Shmem_h
+#define mozilla_ipc_Shmem_h
+
+#include "base/basictypes.h"
+
+#include "nscore.h"
+#include "nsDebug.h"
+
+#include "IPC/IPCMessageUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+
+/**
+ * |Shmem| is one agent in the IPDL shared memory scheme.  The way it
+    works is essentially
+ *
+ *  (1) C++ code calls, say, |parentActor->AllocShmem(size)|
+
+ *  (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
+ *  wrapping the bare OS shmem primitives.  The code then adds the new
+ *  SharedMemory to the set of shmem segments being managed by IPDL.
+ *
+ *  (3) IPDL-generated code "shares" the new SharedMemory to the child
+ *  process, and then sends a special asynchronous IPC message to the
+ *  child notifying it of the creation of the segment.  (What this
+ *  means is OS specific.)
+ *
+ *  (4a) The child receives the special IPC message, and using the
+ *  |mozilla::ipc::SharedMemoryHandle| (which is an fd or HANDLE) it
+ *  was passed, creates a |mozilla::ipc::SharedMemory| in the child
+ *  process.
+ *
+ *  (4b) After sending the "shmem-created" IPC message, IPDL-generated
+ *  code in the parent returns a |mozilla::ipc::Shmem| back to the C++
+ *  caller of |parentActor->AllocShmem()|.  The |Shmem| is a "weak
+ *  reference" to the underlying |SharedMemory|, which is managed by
+ *  IPDL-generated code.  C++ consumers of |Shmem| can't get at the
+ *  underlying |SharedMemory|.
+ *
+ * If parent code wants to give access rights to the Shmem to the
+ * child, it does so by sending its |Shmem| to the child, in an IPDL
+ * message.  The parent's |Shmem| then "dies", i.e. becomes
+ * inaccessible.  This process could be compared to passing a
+ * "shmem-access baton" between parent and child.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+
+class NS_FINAL_CLASS Shmem
+{
+  friend class IPC::ParamTraits<mozilla::ipc::Shmem>;
+
+public:
+  typedef int32 id_t;
+  // Low-level wrapper around platform shmem primitives.
+  typedef mozilla::ipc::SharedMemory SharedMemory;
+  typedef SharedMemory::SharedMemoryHandle SharedMemoryHandle;
+  struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
+
+  Shmem() :
+    mSegment(0),
+    mData(0),
+    mSize(0),
+    mId(0)
+  {
+  }
+
+  Shmem(const Shmem& aOther) :
+    mSegment(aOther.mSegment),
+    mData(aOther.mData),
+    mSize(aOther.mSize),
+    mId(aOther.mId)
+  {
+  }
+
+#if !defined(DEBUG)
+  Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+        SharedMemory* aSegment, id_t aId) :
+    mSegment(aSegment),
+    mData(aSegment->memory()),
+    mSize(0),
+    mId(aId)
+  {
+    mSize = *PtrToSize(mSegment);
+  }
+#else
+  Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+        SharedMemory* aSegment, id_t aId);
+#endif
+
+  ~Shmem()
+  {
+    // Shmem only holds a "weak ref" to the actual segment, which is
+    // owned by IPDL. So there's nothing interesting to be done here
+    forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
+  }
+
+  Shmem& operator=(const Shmem& aRhs)
+  {
+    mSegment = aRhs.mSegment;
+    mData = aRhs.mData;
+    mSize = aRhs.mSize;
+    mId = aRhs.mId;
+    return *this;
+  }
+
+  // Return a pointer to the user-visible data segment.
+  template<typename T>
+  T*
+  get() const
+  {
+    AssertInvariants();
+    AssertAligned<T>();
+
+    return reinterpret_cast<T*>(mData);
+  }
+
+  // Return the size of the segment as requested when this shmem
+  // segment was allocated, in units of T.  The underlying mapping may
+  // actually be larger because of page alignment and private data,
+  // but this isn't exposed to clients.
+  template<typename T>
+  size_t
+  Size() const
+  {
+    AssertInvariants();
+    AssertAligned<T>();
+
+    return mSize / sizeof(T);
+  }
+
+  // These shouldn't be used directly, use the IPDL interface instead.
+  id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) {
+    return mId;
+  }
+
+  void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
+  {
+    Protect(mSegment);
+  }
+
+  void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
+  {
+    mSegment = 0;
+    mData = 0;
+    mSize = 0;
+    mId = 0;
+  }
+
+  static SharedMemory*
+  Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+        size_t aNBytes,
+        bool aProtect=false);
+
+  // Return a SharedMemory instance in this process using the
+  // SharedMemoryHandle (fd/HANDLE) shared to us by the process that
+  // created the underlying OS shmem resource
+  static SharedMemory*
+  OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+               SharedMemoryHandle aHandle,
+               size_t aNBytes,
+               bool aProtect=false);
+
+  static void
+  Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+          SharedMemory* aSegment);
+
+private:
+  template<typename T>
+  void AssertAligned() const
+  {
+    if (0 != (mSize % sizeof(T)))
+      NS_RUNTIMEABORT("shmem is not T-aligned");
+  }
+
+#if !defined(DEBUG)
+  void AssertInvariants() const
+  { }
+  static void Unprotect(SharedMemory* aSegment)
+  { }
+  static void Protect(SharedMemory* aSegment)
+  { }
+
+  static size_t*
+  PtrToSize(SharedMemory* aSegment)
+  {
+    char* endOfSegment =
+      reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
+    return reinterpret_cast<size_t*>(endOfSegment - sizeof(size_t));
+  }
+
+#else
+  void AssertInvariants() const;
+
+  static void Unprotect(SharedMemory* aSegment);
+  static void Protect(SharedMemory* aSegment);
+#endif
+
+  static SharedMemory*
+  CreateSegment(size_t aNBytes,
+                SharedMemoryHandle aHandle=SharedMemory::NULLHandle());
+
+  static void
+  DestroySegment(SharedMemory* aSegment);
+
+  static size_t
+  PageAlignedSize(size_t aSize);
+
+  SharedMemory* mSegment;
+  void* mData;
+  size_t mSize;
+  id_t mId;
+
+  // disable these
+  static void* operator new(size_t) CPP_THROW_NEW;
+  static void operator delete(void*);  
+};
+
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::Shmem>
+{
+  typedef mozilla::ipc::Shmem paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mId);
+  }
+
+  // NB: Read()/Write() look creepy, but IPDL internally uses mId to
+  // properly initialize a "real" Shmem
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    paramType::id_t id;
+    if (!ReadParam(aMsg, aIter, &id))
+      return false;
+    aResult->mId = id;
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    aLog->append(L"(shmem segment)");
+  }
+};
+
+
+} // namespace IPC
+
+
+#endif // ifndef mozilla_ipc_Shmem_h