From b46bfc918b6f75120c27f9b897c056ed90441502 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 10 Mar 2017 17:01:38 -0800 Subject: [PATCH] Bug 1345978, part 2 - Check build ids early in content startup. r=billm If Firefox is updated while it is running, the content process can end up being a different version than the parent process. This can cause odd crashes, that will happen repeatedly until the user restarts Firefox. To handle this better, this patch adds a special build ID message that is sent early in content process startup. The parent process intentionally crashes if the build ID for the child process does not match that of the parent process. MozReview-Commit-ID: 7D3ggkaLxNS --HG-- extra : rebase_source : 1f8d917ce01919524f949dd5bedfbbbd557f7ed3 --- dom/ipc/ContentChild.cpp | 5 ++++ ipc/glue/MessageChannel.cpp | 60 +++++++++++++++++++++++++++++++++++++ ipc/glue/MessageChannel.h | 2 ++ ipc/glue/ProtocolUtils.h | 1 + 4 files changed, 68 insertions(+) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index fa1e825867ac..2b3e45c6f1a6 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -578,6 +578,11 @@ ContentChild::Init(MessageLoop* aIOLoop, GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY); #endif + // This must be sent before any IPDL message, which may hit sentinel + // errors due to parent and content processes having different + // versions. + GetIPCChannel()->SendBuildID(); + #ifdef MOZ_X11 // Send the parent our X socket to act as a proxy reference for our X // resources. diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 51efd5b47489..f549d99bf562 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -18,6 +18,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/Logging.h" #include "mozilla/TimeStamp.h" +#include "nsAppRunner.h" #include "nsAutoPtr.h" #include "nsDebug.h" #include "nsISupportsImpl.h" @@ -819,6 +820,45 @@ MessageChannel::Send(Message* aMsg) return true; } +class BuildIDMessage : public IPC::Message +{ +public: + BuildIDMessage() + : IPC::Message(MSG_ROUTING_NONE, BUILD_ID_MESSAGE_TYPE) + { + } + void Log(const std::string& aPrefix, FILE* aOutf) const + { + fputs("(special `Build ID' message)", aOutf); + } +}; + +// Send the parent a special async message to allow it to detect if +// this process is running a different build. This is a minor +// variation on MessageChannel::Send(Message* aMsg). +void +MessageChannel::SendBuildID() +{ + MOZ_ASSERT(!XRE_IsParentProcess()); + nsAutoPtr msg(new BuildIDMessage()); + nsCString buildID(mozilla::PlatformBuildID()); + IPC::WriteParam(msg, buildID); + + MOZ_RELEASE_ASSERT(!msg->is_sync()); + MOZ_RELEASE_ASSERT(msg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC); + + AssertWorkerThread(); + mMonitor->AssertNotCurrentThreadOwns(); + // Don't check for MSG_ROUTING_NONE. + + MonitorAutoLock lock(*mMonitor); + if (!Connected()) { + ReportConnectionError("MessageChannel", msg); + MOZ_CRASH(); + } + mLink->SendMessage(msg.forget()); +} + class CancelMessage : public IPC::Message { public: @@ -835,6 +875,22 @@ public: } }; +MOZ_NEVER_INLINE static void +CheckChildProcessBuildID(const IPC::Message& aMsg) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + nsCString childBuildID; + PickleIterator msgIter(aMsg); + MOZ_ALWAYS_TRUE(IPC::ReadParam(&aMsg, &msgIter, &childBuildID)); + aMsg.EndRead(msgIter); + + nsCString parentBuildID(mozilla::PlatformBuildID()); + + // This assert can fail if the child process has been updated + // to a newer version while the parent process was running. + MOZ_RELEASE_ASSERT(parentBuildID == childBuildID); +} + bool MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg) { @@ -856,6 +912,10 @@ MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg) CancelTransaction(aMsg.transaction_id()); NotifyWorkerThread(); return true; + } else if (BUILD_ID_MESSAGE_TYPE == aMsg.type()) { + IPC_LOG("Build ID message"); + CheckChildProcessBuildID(aMsg); + return true; } } return false; diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index a34fc5d55cb7..cb9e35875a93 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -153,6 +153,8 @@ class MessageChannel : HasResultCodes // Asynchronously send a message to the other side of the channel bool Send(Message* aMsg); + void SendBuildID(); + // Asynchronously deliver a message back to this side of the // channel bool Echo(Message* aMsg); diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 2291e37085cd..0c8ee2f4ccce 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -46,6 +46,7 @@ namespace { // protocol 0. Oops! We can get away with this until protocol 0 // starts approaching its 65,536th message. enum { + BUILD_ID_MESSAGE_TYPE = kuint16max - 7, CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6, SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5, SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,