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
This commit is contained in:
Andrew McCreight 2017-03-10 17:01:38 -08:00
parent 3cb01b77f0
commit b46bfc918b
4 changed files with 68 additions and 0 deletions

View File

@ -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.

View File

@ -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<BuildIDMessage> 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;

View File

@ -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);

View File

@ -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,