Bug 767775 - Viciously and loudly kill any process sending messages that race with RPC __delete__ messages. Test by bsmedberg. r=cjones
authorJosh Matthews <josh@joshmatthews.net>
Fri, 13 Jul 2012 13:53:00 -0400
changeset 99317 d97fd94a634862cfb0d3545d1118c078efdf7571
parent 99316 5e6d4028b17602db1688efdb7b7e42e25b3b832f
child 99318 d229299ec2bee2f863fbe9bedf3c1d67c91a2d94
child 99320 141fd2c4581bc3e6a2d502784230d37b0e4d87ae
push id23118
push userryanvm@gmail.com
push dateSat, 14 Jul 2012 23:14:51 +0000
treeherdermozilla-central@d229299ec2be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs767775
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 767775 - Viciously and loudly kill any process sending messages that race with RPC __delete__ messages. Test by bsmedberg. r=cjones
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
ipc/ipdl/test/cxx/Makefile.in
ipc/ipdl/test/cxx/PTestBadActor.ipdl
ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
ipc/ipdl/test/cxx/TestBadActor.cpp
ipc/ipdl/test/cxx/TestBadActor.h
ipc/ipdl/test/cxx/TestDataStructures.h
ipc/ipdl/test/cxx/genIPDLUnitTests.py
ipc/ipdl/test/cxx/ipdl.mk
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -395,16 +395,17 @@ class State(Node):
         return hash(repr(self))
     def __ne__(self, o):
         return not (self == o)
     def __repr__(self): return '<State %r start=%r>'% (self.name, self.start)
     def __str__(self): return '<State %s start=%s>'% (self.name, self.start)
 
 State.ANY = State(Loc.NONE, '[any]', start=True)
 State.DEAD = State(Loc.NONE, '[dead]', start=False)
+State.DYING = State(Loc.NONE, '[dying]', start=False)
 
 class Param(Node):
     def __init__(self, loc, typespec, name):
         Node.__init__(self, loc)
         self.name = name
         self.typespec = typespec
 
 class TypeSpec(Node):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -142,26 +142,34 @@ def _errorState(proto=None):
     if proto is not None:  pfx = proto.name() +'::'
     return ExprVar(pfx +'__Error')
 
 def _deadState(proto=None):
     pfx = ''
     if proto is not None:  pfx = proto.name() +'::'
     return ExprVar(pfx +'__Dead')
 
+def _dyingState(proto=None):
+    pfx = ''
+    if proto is not None:  pfx = proto.name() +'::'
+    return ExprVar(pfx +'__Dying')
+
 def _startState(proto=None, fq=False):
     pfx = ''
     if proto:
         if fq:  pfx = proto.fullname() +'::'
         else:   pfx = proto.name() +'::'
     return ExprVar(pfx +'__Start')
 
 def _deleteId():
     return ExprVar('Msg___delete____ID')
 
+def _deleteReplyId():
+    return ExprVar('Reply___delete____ID')
+
 def _lookupListener(idexpr):
     return ExprCall(ExprVar('Lookup'), args=[ idexpr ])
 
 def _shmemType(ptr=0, const=1, ref=0):
     return Type('Shmem', ptr=ptr, ref=ref)
 
 def _rawShmemType(ptr=0):
     return Type('Shmem::SharedMemory', ptr=ptr)
@@ -1489,16 +1497,17 @@ class _GenerateProtocolCode(ipdl.ast.Vis
 
         # state information
         stateenum = TypeEnum('State')
         # NB: __Dead is the first state on purpose, so that it has
         # value '0'
         stateenum.addId(_deadState().name)
         stateenum.addId(_nullState().name)
         stateenum.addId(_errorState().name)
+        stateenum.addId(_dyingState().name)
         for ts in p.transitionStmts:
             stateenum.addId(ts.state.decl.cxxname)
         if len(p.transitionStmts):
             startstate = p.transitionStmts[0].state.decl.cxxname
         else:
             startstate = _nullState().name
         stateenum.addId(_startState().name, startstate)
 
@@ -1653,32 +1662,51 @@ class _GenerateProtocolCode(ipdl.ast.Vis
                 StmtBreak()
             ])
             fromswitch.addcase(CaseLabel(ts.state.decl.cxxname), msgblock)
 
         # special cases for Null and Error
         nullerrorblock = Block()
         if ptype.hasDelete:
             ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgexpr))
+            if ptype.hasReentrantDelete:
+                nextState = _dyingState()
+            else:
+                nextState = _deadState()
             ifdelete.addifstmts([
-                StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())),
+                StmtExpr(ExprAssn(ExprDeref(nextvar), nextState)),
                 StmtReturn(ExprLiteral.TRUE) ])
             nullerrorblock.addstmt(ifdelete)
         nullerrorblock.addstmt(
             StmtReturn(ExprBinary(_nullState(), '==', fromvar)))
         fromswitch.addfallthrough(CaseLabel(_nullState().name))
         fromswitch.addcase(CaseLabel(_errorState().name), nullerrorblock)
 
         # special case for Dead
         deadblock = Block()
         deadblock.addstmts([
             _runtimeAbort('__delete__()d actor'),
             StmtReturn(ExprLiteral.FALSE) ])
         fromswitch.addcase(CaseLabel(_deadState().name), deadblock)
 
+        # special case for Dying
+        dyingblock = Block()
+        if ptype.hasReentrantDelete:
+            ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgexpr))
+            ifdelete.addifstmt(
+                StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
+            dyingblock.addstmt(ifdelete)
+            dyingblock.addstmt(
+                StmtReturn(ExprLiteral.TRUE))
+        else:
+            dyingblock.addstmts([
+                _runtimeAbort('__delete__()d (and unexpectedly dying) actor'),
+                StmtReturn(ExprLiteral.FALSE) ])
+        fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
+
         unreachedblock = Block()
         unreachedblock.addstmts([
             _runtimeAbort('corrupted actor state'),
             StmtReturn(ExprLiteral.FALSE) ])
         fromswitch.addcase(DefaultLabel(), unreachedblock)
 
         if usesend:
             transitionfunc.addstmt(
@@ -2907,16 +2935,31 @@ class _GenerateProtocolActorCode(ipdl.as
                 routeif.ifb.addstmt(failif)
 
                 routeif.ifb.addstmt(StmtReturn(ExprCall(
                     ExprSelect(routedvar, '->', name),
                     args=[ ExprVar(p.name) for p in params ])))
 
                 method.addstmts([ routedecl, routeif, Whitespace.NL ])
 
+            # in the event of an RPC delete message, we want to loudly complain about
+            # messages that are received that are not a reply to the original message
+            if ptype.hasReentrantDelete:
+                msgVar = ExprVar(params[0].name)
+                ifdying = StmtIf(ExprBinary(
+                    ExprBinary(ExprVar('mState'), '==', _dyingState(ptype)),
+                    '&&',
+                    ExprBinary(
+                        ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_reply')), '!=', ExprLiteral.TRUE),
+                        '||',
+                        ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_rpc')), '!=', ExprLiteral.TRUE))))
+                ifdying.addifstmts([_fatalError('incoming message racing with actor deletion'),
+                                    StmtReturn(_Result.Processed)])
+                method.addstmt(ifdying)
+
             # bug 509581: don't generate the switch stmt if there
             # is only the default case; MSVC doesn't like that
             if switch.nr_cases > 1:
                 method.addstmt(switch)
             else:
                 method.addstmt(StmtReturn(_Result.NotKnown))
 
             return method
@@ -4522,19 +4565,23 @@ class _GenerateProtocolActorCode(ipdl.as
 
         destmts = self.deserializeReply(
             md, ExprAddrOf(replyvar), self.side, errfnSend)
         ifsendok = StmtIf(ExprLiteral.FALSE)
         ifsendok.addifstmts(destmts)
         ifsendok.addifstmts([ Whitespace.NL,
                               StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, '&=')) ])
 
+        method.addstmt(ifsendok)
+
+        if self.protocol.decl.type.hasReentrantDelete:
+            method.addstmts(self.transition(md, 'in', actor.var(), reply=True))
+
         method.addstmts(
-            [ ifsendok ]
-            + self.dtorEpilogue(md, actor.var())
+            self.dtorEpilogue(md, actor.var())
             + [ Whitespace.NL, StmtReturn(sendok) ])
 
         return method
 
     def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion):
         if md.decl.type.isCtor():
             destroyedType = md.decl.type.constructedType()
         else:
@@ -4902,34 +4949,35 @@ class _GenerateProtocolActorCode(ipdl.as
             # only save the ID if we're actually going to use it, to
             # avoid unused-variable warnings
             saveIdStmts = [ StmtDecl(Decl(_actorIdType(), idvar.name),
                                      self.protocol.routingId()) ]
         else:
             saveIdStmts = [ ]
         return idvar, saveIdStmts
 
-    def transition(self, md, direction, actor=None):
+    def transition(self, md, direction, actor=None, reply=False):
         if actor is not None:  stateexpr = _actorState(actor)
         else:                  stateexpr = self.protocol.stateVar()
         
         if (self.side is 'parent' and direction is 'out'
             or self.side is 'child' and direction is 'in'):
             action = ExprVar('Trigger::Send')
         elif (self.side is 'parent' and direction is 'in'
             or self.side is 'child' and direction is 'out'):
             action = ExprVar('Trigger::Recv')
         else: assert 0 and 'unknown combo %s/%s'% (self.side, direction)
 
+        msgid = md.pqMsgId() if not reply else md.pqReplyId()
         ifbad = StmtIf(ExprNot(
             ExprCall(
                 ExprVar(self.protocol.name +'::Transition'),
                 args=[ stateexpr,
                        ExprCall(ExprVar('Trigger'),
-                                args=[ action, ExprVar(md.pqMsgId()) ]),
+                                args=[ action, ExprVar(msgid) ]),
                        ExprAddrOf(stateexpr) ])))
         ifbad.addifstmts(_badTransition())
         return [ ifbad ]
 
     def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn):
         ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
         ifbad.addifstmts(errfn('error deserializing (better message TODO)'))
         return ifbad
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -266,16 +266,17 @@ class ProtocolType(IPDLType):
         self.qname = qname
         self.sendSemantics = sendSemantics
         self.spawns = set()             # ProtocolType
         self.opens = set()              # ProtocolType
         self.managers = set()           # ProtocolType
         self.manages = [ ]
         self.stateless = stateless
         self.hasDelete = False
+        self.hasReentrantDelete = False
     def isProtocol(self): return True
 
     def name(self):
         return self.qname.baseid
     def fullname(self):
         return str(self.qname)
 
     def addManager(self, mgrtype):
@@ -822,16 +823,18 @@ class GatherDecls(TcheckVisitor):
 
         p.decl.type.hasDelete = (not not self.symtab.lookup(_DELETE_MSG))
         if not (p.decl.type.hasDelete or p.decl.type.isToplevel()):
             self.error(
                 p.loc,
                 "destructor declaration `%s(...)' required for managed protocol `%s'",
                 _DELETE_MSG, p.name)
 
+        p.decl.type.hasReentrantDelete = p.decl.type.hasDelete and self.symtab.lookup(_DELETE_MSG).type.isRpc()
+
         for managed in p.managesStmts:
             mgdname = managed.name
             ctordecl = self.symtab.lookup(mgdname +'Constructor')
 
             if not (ctordecl and ctordecl.type.isCtor()):
                 self.error(
                     managed.loc,
                     "constructor declaration required for managed protocol `%s' (managed by protocol `%s')",
@@ -840,23 +843,27 @@ class GatherDecls(TcheckVisitor):
         p.states = { }
         
         if len(p.transitionStmts):
             p.startStates = [ ts for ts in p.transitionStmts
                               if ts.state.start ]
             if 0 == len(p.startStates):
                 p.startStates = [ p.transitionStmts[0] ]
 
-        # declare implicit "any" and "dead" states
+        # declare implicit "any", "dead", and "dying" states
         self.declare(loc=State.ANY.loc,
                      type=StateType(p.decl.type, State.ANY.name, start=False),
                      progname=State.ANY.name)
         self.declare(loc=State.DEAD.loc,
                      type=StateType(p.decl.type, State.DEAD.name, start=False),
                      progname=State.DEAD.name)
+        if p.decl.type.hasReentrantDelete:
+            self.declare(loc=State.DYING.loc,
+                         type=StateType(p.decl.type, State.DYING.name, start=False),
+                         progname=State.DYING.name)
 
         # declare each state before decorating their mention
         for trans in p.transitionStmts:
             p.states[trans.state] = trans
             trans.state.decl = self.declare(
                 loc=trans.state.loc,
                 type=StateType(p.decl.type, trans.state, trans.state.start),
                 progname=trans.state.name)
@@ -866,16 +873,19 @@ class GatherDecls(TcheckVisitor):
             trans.accept(self)
 
         if not (p.decl.type.stateless
                 or (p.decl.type.isToplevel()
                     and None is self.symtab.lookup(_DELETE_MSG))):
             # add a special state |state DEAD: null goto DEAD;|
             deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
             p.states[State.DEAD] = deadtrans           
+            if p.decl.type.hasReentrantDelete:
+                dyingtrans = TransitionStmt.makeNullStmt(State.DYING)
+                p.states[State.DYING] = dyingtrans
 
         # visit the message decls once more and resolve the state names
         # attached to actor params and returns
         def resolvestate(loc, actortype):
             assert actortype.isIPDL() and actortype.isActor()
 
             # already resolved this guy's state
             if isinstance(actortype.state, Decl):
--- a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsRegion.h"
-
 #include "mozilla/ipc/IOThreadChild.h"
 
 #include "IPDLUnitTestProcessChild.h"
 #include "IPDLUnitTests.h"
 
+#include "nsRegion.h"
+
 using mozilla::ipc::IOThreadChild;
 
 namespace mozilla {
 namespace _ipdltest {
 
 bool
 IPDLUnitTestProcessChild::Init()
 {
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -106,17 +106,17 @@ IPDLUnitTestToString(IPDLUnitTestType aT
         return NULL;
     }
 }
 
 
 IPDLUnitTestType
 IPDLUnitTest()
 {
-    return IPDLUnitTestFromString(mozilla::_ipdltest::IPDLUnitTestName());
+    return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
 }
 
 
 } // namespace <anon>
 
 
 //-----------------------------------------------------------------------------
 // parent process only
@@ -245,17 +245,17 @@ DeleteParentActor()
     if (!gParentActor)
         return;
 
     switch (IPDLUnitTest()) {
 //-----------------------------------------------------------------------------
 //===== TEMPLATED =====
 ${PARENT_DELETE_CASES}
 //-----------------------------------------------------------------------------
-    default:  mozilla::_ipdltest::fail("???");
+    default:  ::mozilla::_ipdltest::fail("???");
     }
 }
 
 void
 QuitXPCOM()
 {
   DeleteParentActor();
 
@@ -348,17 +348,17 @@ DeleteChildActor()
     if (!gChildActor)
         return;
 
     switch (IPDLUnitTest()) {
 //-----------------------------------------------------------------------------
 //===== TEMPLATED =====
 ${CHILD_DELETE_CASES}
 //-----------------------------------------------------------------------------
-    default:  mozilla::_ipdltest::fail("???");
+    default:  ::mozilla::_ipdltest::fail("???");
     }
 }
 
 void
 IPDLUnitTestChildInit(IPC::Channel* transport,
                       base::ProcessHandle parent,
                       MessageLoop* worker)
 {
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -49,16 +49,17 @@ IPDLTESTS = \
   TestSanity  \
   TestSelfManageRoot \
   TestShmem \
   TestShutdown \
   TestStackHooks \
   TestSyncError \
   TestSyncHang \
   TestSyncWakeup \
+  TestBadActor \
   $(NULL)
 
 ifeq ($(OS_ARCH),Linux)
 IPDLTESTS += TestSysVShmem
 endif
 
 EXTRA_PROTOCOLS = \
   TestBridgeSub \
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestBadActorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// Test that a parent sending a reentrant __delete__ message
+// is not killed if a child's message races with the reply.
+
+rpc protocol PTestBadActor {
+  manages PTestBadActorSub;
+
+child:
+  PTestBadActorSub();
+  __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestBadActor;
+
+namespace mozilla {
+namespace _ipdltest {
+
+rpc protocol PTestBadActorSub {
+  manager PTestBadActor;
+
+child:
+  rpc __delete__();
+
+parent:
+  Ping();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.cpp
@@ -0,0 +1,51 @@
+#include "TestBadActor.h"
+#include "IPDLUnitTests.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+TestBadActorParent::Main()
+{
+  // This test is designed to test a race condition where the child sends us
+  // a message on an actor that we've already destroyed. The child process
+  // should die, and the parent process should not abort.
+
+  PTestBadActorSubParent* child = SendPTestBadActorSubConstructor();
+  if (!child)
+    fail("Sending constructor");
+
+  unused << child->Call__delete__(child);
+}
+
+PTestBadActorSubParent*
+TestBadActorParent::AllocPTestBadActorSub()
+{
+  return new TestBadActorSubParent();
+}
+
+bool
+TestBadActorSubParent::RecvPing()
+{
+  fail("Shouldn't have received ping.");
+  return false;
+}
+
+PTestBadActorSubChild*
+TestBadActorChild::AllocPTestBadActorSub()
+{
+  return new TestBadActorSubChild();
+}
+
+bool
+TestBadActorChild::RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor)
+{
+  if (!actor->SendPing()) {
+    fail("Couldn't send ping to an actor which supposedly isn't dead yet.");
+  }
+  return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -0,0 +1,91 @@
+#ifndef mozilla__ipdltest_TestBadActor_h
+#define mozilla__ipdltest_TestBadActor_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBadActorParent.h"
+#include "mozilla/_ipdltest/PTestBadActorChild.h"
+
+#include "mozilla/_ipdltest/PTestBadActorSubParent.h"
+#include "mozilla/_ipdltest/PTestBadActorSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestBadActorParent
+  : public PTestBadActorParent
+{
+public:
+  TestBadActorParent() { }
+  virtual ~TestBadActorParent() { }
+
+  static bool RunTestInProcesses() { return true; }
+  static bool RunTestInThreads() { return false; }
+
+  void Main();
+
+protected:
+  NS_OVERRIDE
+  virtual void ActorDestroy(ActorDestroyReason why)
+  {
+    if (AbnormalShutdown != why)
+      fail("unexpected destruction");
+    passed("ok");
+    QuitParent();
+  }
+
+  virtual PTestBadActorSubParent*
+  AllocPTestBadActorSub();
+
+  virtual bool
+  DeallocPTestBadActorSub(PTestBadActorSubParent* actor) {
+    delete actor;
+    return true;
+  }
+};
+
+class TestBadActorSubParent
+  : public PTestBadActorSubParent
+{
+public:
+  TestBadActorSubParent() { }
+  virtual ~TestBadActorSubParent() { }
+
+protected:
+  virtual bool RecvPing();
+};
+
+class TestBadActorChild
+  : public PTestBadActorChild
+{
+public:
+  TestBadActorChild() { }
+  virtual ~TestBadActorChild() { }
+
+protected:
+  virtual PTestBadActorSubChild*
+  AllocPTestBadActorSub();
+
+  virtual bool
+  DeallocPTestBadActorSub(PTestBadActorSubChild* actor)
+  {
+    delete actor;
+    return true;
+  }
+
+  virtual bool
+  RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor);
+};
+
+class TestBadActorSubChild
+  : public PTestBadActorSubChild
+{
+public:
+  TestBadActorSubChild() { }
+  virtual ~TestBadActorSubChild() { }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // mozilla__ipdltest_TestBadActor_h
--- a/ipc/ipdl/test/cxx/TestDataStructures.h
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -19,17 +19,23 @@ class TestDataStructuresSub :
         public PTestDataStructuresSubParent,
         public PTestDataStructuresSubChild
 {
 public:
     TestDataStructuresSub(uint32 i) : mI(i)
     { }
     virtual ~TestDataStructuresSub()
     { }
-    uint32 mI;
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why)
+    {
+      if (Deletion != why)
+        fail("unexpected destruction!");
+    }
+  uint32 mI;
 };
 
 //-----------------------------------------------------------------------------
 // Main actors
 
 class TestDataStructuresParent :
     public PTestDataStructuresParent
 {
--- a/ipc/ipdl/test/cxx/genIPDLUnitTests.py
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -89,19 +89,19 @@ def main(argv):
         %sParent** parent =
         reinterpret_cast<%sParent**>(&gParentActor);
         *parent = new %sParent();
 
         %sChild** child =
         reinterpret_cast<%sChild**>(&gChildActor);
         *child = new %sChild();
 
-        mozilla::ipc::AsyncChannel *childChannel = (*child)->GetIPCChannel();
-        mozilla::ipc::AsyncChannel::Side parentSide = 
-            mozilla::ipc::AsyncChannel::Parent;
+        ::mozilla::ipc::AsyncChannel *childChannel = (*child)->GetIPCChannel();
+        ::mozilla::ipc::AsyncChannel::Side parentSide = 
+            ::mozilla::ipc::AsyncChannel::Parent;
 
         (*parent)->Open(childChannel, childMessageLoop, parentSide);
         return (*parent)->Main();
         }
 '''% (t, t, t, t, t, t, t) for t in unittests ])
 
     child_delete_cases = '\n'.join([
 '''    case %s: {
--- a/ipc/ipdl/test/cxx/ipdl.mk
+++ b/ipc/ipdl/test/cxx/ipdl.mk
@@ -40,9 +40,11 @@ IPDLSRCS =					\
   PTestShutdown.ipdl				\
   PTestShutdownSub.ipdl				\
   PTestShutdownSubsub.ipdl			\
   PTestStackHooks.ipdl				\
   PTestSyncError.ipdl                           \
   PTestSyncHang.ipdl                            \
   PTestSyncWakeup.ipdl				\
   PTestSysVShmem.ipdl				\
+  PTestBadActor.ipdl                            \
+  PTestBadActorSub.ipdl                         \
   $(NULL)