Bug 1427860 - XDR failures should leave script isUncompleted(). r=nbp, a=RyanVM
authorTed Campbell <tcampbell@mozilla.com>
Mon, 29 Oct 2018 14:49:44 +0000
changeset 500941 af012e57e9de8ce72133fe5a1f2b8b0362336260
parent 500940 52b79f0655cb6bf92c4cd1fffafc617280feacd3
child 500942 43d2c9744a7d94ef0609d6f1e099ccb19c538297
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, RyanVM
bugs1427860
milestone64.0
Bug 1427860 - XDR failures should leave script isUncompleted(). r=nbp, a=RyanVM Failures during XDR may leave the script partially initialized in a way that confuses coverage collection. This ensures the shared script data is removed from a script if there are any XDR failures in it. Differential Revision: https://phabricator.services.mozilla.com/D9960
js/src/jit-test/tests/xdr/bug1427860.js
js/src/vm/JSScript.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1427860.js
@@ -0,0 +1,12 @@
+// |jit-test| skip-if: !('oomAtAllocation' in this)
+
+let x = cacheEntry("function inner() { return 3; }; inner()");
+evaluate(x, { saveIncrementalBytecode: true });
+
+try {
+    // Fail XDR decode with partial script
+    oomAtAllocation(20);
+    evaluate(x, { loadBytecode: true });
+} catch { }
+
+getLcovInfo();
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -692,16 +692,20 @@ js::XDRScript(XDRState<mode>* xdr, Handl
     }
 
     if (mode == XDR_DECODE) {
         if (!script->createScriptData(cx, length, nsrcnotes, natoms)) {
             return xdr->fail(JS::TranscodeResult_Throw);
         }
     }
 
+    // If XDR operation fails, we must call JSScript::freeScriptData in order
+    // to neuter the script. Various things that iterate raw scripts in a GC
+    // arena use the presense of this data to detect if initialization is
+    // complete.
     auto scriptDataGuard = mozilla::MakeScopeExit([&] {
         if (mode == XDR_DECODE) {
             script->freeScriptData();
         }
     });
 
     jsbytecode* code = script->code();
     MOZ_TRY(xdr->codeBytes(code, length));
@@ -713,17 +717,16 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             MOZ_TRY(XDRAtom(xdr, &tmp));
             script->atoms()[i].init(tmp);
         } else {
             RootedAtom tmp(cx, script->atoms()[i]);
             MOZ_TRY(XDRAtom(xdr, &tmp));
         }
     }
 
-    scriptDataGuard.release();
     if (mode == XDR_DECODE) {
         if (!script->shareScriptData(cx)) {
             return xdr->fail(JS::TranscodeResult_Throw);
         }
     }
 
     if (nconsts) {
         RootedValue val(cx);
@@ -972,16 +975,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         scriptp.set(script);
 
         /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
         if (!fun && !cx->helperThread()) {
             Debugger::onNewScript(cx, script);
         }
     }
 
+    scriptDataGuard.release();
     return Ok();
 }
 
 template XDRResult
 js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSourceObject, HandleFunction,
               MutableHandleScript);
 
 template XDRResult