Bug 1274074 - console.log()ging too much data can crash e10s via IPC call from mozilla::dom::ProcessGlobal::SendAsyncMessage; r=mconley
authorHaik Aftandilian <haftandilian@mozilla.com>
Sat, 28 May 2016 01:15:14 -0700
changeset 341016 4b44dee24a773b23b7214107ddd39556fbe3f071
parent 341015 16042a3a145c271f6937cb25ca53f65614a61543
child 341017 2e253d272ceb1514b3932342a1532c4bb9e287ac
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1274074
milestone49.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 1274074 - console.log()ging too much data can crash e10s via IPC call from mozilla::dom::ProcessGlobal::SendAsyncMessage; r=mconley MozReview-Commit-ID: 6ICgsnNHFpq
toolkit/components/processsingleton/ContentProcessSingleton.js
--- a/toolkit/components/processsingleton/ContentProcessSingleton.js
+++ b/toolkit/components/processsingleton/ContentProcessSingleton.js
@@ -9,16 +9,37 @@ const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
+/*
+ * The message manager has an upper limit on message sizes that it can
+ * reliably forward to the parent so we limit the size of console log event
+ * messages that we forward here. The web console is local and receives the
+ * full console message, but addons subscribed to console event messages
+ * in the parent receive the truncated version. Due to fragmentation,
+ * messages as small as 1MB have resulted in IPC allocation failures on
+ * 32-bit platforms. To limit IPC allocation sizes, console.log messages
+ * with arguments with total size > MSG_MGR_CONSOLE_MAX_SIZE (bytes) have
+ * their arguments completely truncated. MSG_MGR_CONSOLE_VAR_SIZE is an
+ * approximation of how much space (in bytes) a JS non-string variable will
+ * require in the manager's implementation. For strings, we use 2 bytes per
+ * char. The console message URI and function name are limited to
+ * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate
+ * the exact amount of space the message manager implementation will require
+ * for a given message so this is imperfect.
+ */
+const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
+const MSG_MGR_CONSOLE_VAR_SIZE = 8;
+const MSG_MGR_CONSOLE_INFO_MAX = 1024;
+
 function ContentProcessSingleton() {}
 ContentProcessSingleton.prototype = {
   classID: Components.ID("{ca2a8470-45c7-11e4-916c-0800200c9a66}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   observe: function(subject, topic, data) {
     switch (topic) {
@@ -28,30 +49,51 @@ ContentProcessSingleton.prototype = {
       cpmm.addMessageListener("DevTools:InitDebuggerServer", this);
       break;
     }
     case "console-api-log-event": {
       let consoleMsg = subject.wrappedJSObject;
 
       let msgData = {
         level: consoleMsg.level,
-        filename: consoleMsg.filename,
+        filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
         lineNumber: consoleMsg.lineNumber,
-        functionName: consoleMsg.functionName,
+        functionName: consoleMsg.functionName.substring(0,
+          MSG_MGR_CONSOLE_INFO_MAX),
         timeStamp: consoleMsg.timeStamp,
         arguments: [],
       };
 
       // We can't send objects over the message manager, so we sanitize
-      // them out.
+      // them out, replacing those arguments with "<unavailable>".
+      let unavailString = "<unavailable>";
+      let unavailStringLength = unavailString.length * 2; // 2-bytes per char
+
+      // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
+      // replace all arguments with "<truncated>".
+      let totalArgLength = 0;
+
+      // Walk through the arguments, checking the type and size.
       for (let arg of consoleMsg.arguments) {
-        if ((typeof arg == "object" || typeof arg == "function") && arg !== null) {
-          msgData.arguments.push("<unavailable>");
+        if ((typeof arg == "object" || typeof arg == "function") &&
+            arg !== null) {
+          arg = unavailString;
+          totalArgLength += unavailStringLength;
+        } else if (typeof arg == "string") {
+          totalArgLength += arg.length * 2; // 2-bytes per char
         } else {
+          totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
+        }
+
+        if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
           msgData.arguments.push(arg);
+        } else {
+          // arguments take up too much space
+          msgData.arguments = ["<truncated>"];
+          break;
         }
       }
 
       cpmm.sendAsyncMessage("Console:Log", msgData);
       break;
     }
 
     case "xpcom-shutdown":