Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2020-11-10
Initial Package Version: 2.53.4
Origin:                  Upstream + Self (modified to fit this codebase)
Upstream Status:         Not Applied
Description:             Fixes CVE-2020-26950, a 0day vulnerability in
                         Seamonkey, MozJS, Firefox, and Thunderbird.
                         This patch was heavily modified to fit the
                         Seamonkey codebase. The following are relevant URLs:
                         https://www.mozilla.org/en-US/security/advisories/mfsa2020-49/
                         https://hg.mozilla.org/releases/mozilla-esr78/rev/22b8bef3c436a4d36b586804f342928e1ab11e51
                         https://us-cert.cisa.gov/ncas/current-activity/2020/11/10/mozilla-releases-security-updates-firefox-firefox-esr-and

diff -Naurp seamonkey-2.53.4.orig/mozilla/js/src/jit/IonBuilder.cpp seamonkey-2.53.4/mozilla/js/src/jit/IonBuilder.cpp
--- seamonkey-2.53.4.orig/mozilla/js/src/jit/IonBuilder.cpp	2020-04-01 06:01:50.000000000 -0500
+++ seamonkey-2.53.4/mozilla/js/src/jit/IonBuilder.cpp	2020-11-10 14:17:11.795757359 -0600
@@ -4848,35 +4848,24 @@ IonBuilder::createCallObject(MDefinition
 }
 
 MDefinition*
-IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
-{
+IonBuilder::createThisScripted(MDefinition* callee, 
+                               MDefinition* newTarget) {
     // Get callee.prototype.
     //
     // This instruction MUST be idempotent: since it does not correspond to an
     // explicit operation in the bytecode, we cannot use resumeAfter().
     // Getters may not override |prototype| fetching, so this operation is indeed idempotent.
-    // - First try an idempotent property cache.
-    // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
-    //   therefore we fallback to CallGetProperty
-    //
-    // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
-    //       and thus invalidation.
-    MInstruction* getProto;
-    if (!invalidatedIdempotentCache()) {
-        MConstant* id = constant(StringValue(names().prototype));
-        MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, id,
-                                                                 /* monitored = */ false);
-        getPropCache->setIdempotent();
-        getProto = getPropCache;
-    } else {
-        MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), newTarget, names().prototype);
-        callGetProp->setIdempotent();
-        getProto = callGetProp;
-    }
-    current->add(getProto);
+    // Note: GetPropertyCache can trigger a GC, and thus invalidation.
+    MConstant* id = constant(StringValue(names().prototype));
+    MGetPropertyCache* getPropCache = 
+        MGetPropertyCache::New(alloc(), newTarget, id,
+                /* monitored = */ false);
+    getPropCache->setIdempotent();
+    current->add(getPropCache);
 
     // Create this from prototype
-    MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, newTarget, getProto);
+    MCreateThisWithProto* createThis = 
+        MCreateThisWithProto::New(alloc(), callee, newTarget, getPropCache);
     current->add(createThis);
 
     return createThis;
diff -Naurp seamonkey-2.53.4.orig/mozilla/js/src/jit/IonIC.cpp seamonkey-2.53.4/mozilla/js/src/jit/IonIC.cpp
--- seamonkey-2.53.4.orig/mozilla/js/src/jit/IonIC.cpp	2020-08-10 06:30:34.000000000 -0500
+++ seamonkey-2.53.4/mozilla/js/src/jit/IonIC.cpp	2020-11-10 15:21:27.465714885 -0600
@@ -162,6 +162,16 @@ IonGetPropertyIC::update(JSContext* cx,
         if (outerScript->hasIonScript())
             Invalidate(cx, outerScript);
 
+
+        // IonBuilder::createScriptedThis does not use InvalidedIdempotentCache
+        // flag so prevent bailout-loop by disabling Ion for the script.
+        MOZ_ASSERT(ic->kind() == CacheKind::GetProp);
+        if (idVal.toString()->asAtom().asPropertyName() == cx->names().prototype) {
+            if (val.isObject() && val.toObject().is<JSFunction>()) {
+                return Method_Skipped;
+            }
+        }
+
         // We will redo the potentially effectful lookup in Baseline.
         return true;
     }
