Fixes CVE-2019-11750.

Backported from mozjs-68esr:
https://hg.mozilla.org/releases/mozilla-esr68/rev/b1482a07eafe84c33351cb4524404686a9fce427

# HG changeset patch
# User Jan de Mooij <jdemooij@mozilla.com>
# Date 1565773483 0
# Node ID b1482a07eafe84c33351cb4524404686a9fce427
# Parent  e432c2b3ca00d0f6fd14168161f47b929db8c901
Bug 1568397 part 1 - Fix definite properties analysis to use the correct group for constraints. r=iain,tcampbell, a=RyanVM

We now add information about the constraints to a new class (DPAConstraintInfo)
so we can then finish all constraints at the end. This is also nice to avoid
adding unnecessary constraints when the analysis fails.

Differential Revision: https://phabricator.services.mozilla.com/D40444

--- mozjs-60.8.0/js/src/jit/IonAnalysis.cpp
+++ mozjs-60.8.0/js/src/jit/IonAnalysis.cpp
@@ -3722,9 +3722,9 @@ MCompare* jit::ConvertLinearInequality(T
 }
 
 static bool AnalyzePoppedThis(
-    JSContext* cx, ObjectGroup* group, MDefinition* thisValue,
-    MInstruction* ins, bool definitelyExecuted, HandlePlainObject baseobj,
-    Vector<TypeNewScript::Initializer>* initializerList,
+    JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
+    MDefinition* thisValue, MInstruction* ins, bool definitelyExecuted,
+    HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList,
     Vector<PropertyName*>* accessedProperties, bool* phandled) {
   // Determine the effect that a use of the |this| value when calling |new|
   // on a script has on the properties definitely held by the new object.
@@ -3756,7 +3756,12 @@ static bool AnalyzePoppedThis(
     if (!definitelyExecuted) return true;
 
     RootedId id(cx, NameToId(setprop->name()));
-    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+    bool added = false;
+    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+                                                       group, id, &added)) {
+      return false;
+    }
+    if (!added) {
       // The prototype chain already contains a getter/setter for this
       // property, or type information is too imprecise.
       return true;
@@ -3815,7 +3820,12 @@ static bool AnalyzePoppedThis(
     if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name()))
       return false;
 
-    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+    bool added = false;
+    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+                                                       group, id, &added)) {
+      return false;
+    }
+    if (!added) {
       // The |this| value can escape if any property reads it does go
       // through a getter.
       return true;
@@ -3839,8 +3849,8 @@ static int CmpInstructions(const void* a
 }
 
 bool jit::AnalyzeNewScriptDefiniteProperties(
-    JSContext* cx, HandleFunction fun, ObjectGroup* group,
-    HandlePlainObject baseobj,
+    JSContext* cx, DPAConstraintInfo& constraintInfo,
+    HandleFunction fun, ObjectGroup* group, HandlePlainObject baseobj,
     Vector<TypeNewScript::Initializer>* initializerList) {
   MOZ_ASSERT(cx->zone()->types.activeAnalysis);
 
@@ -3991,9 +4001,9 @@ bool jit::AnalyzeNewScriptDefiniteProper
 
     bool handled = false;
     size_t slotSpan = baseobj->slotSpan();
-    if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted,
-                           baseobj, initializerList, &accessedProperties,
-                           &handled)) {
+    if (!AnalyzePoppedThis(cx, constraintInfo, group, thisValue, ins,
+                           definitelyExecuted, baseobj, initializerList,
+                           &accessedProperties, &handled)) {
       return false;
     }
     if (!handled) break;
@@ -4009,7 +4019,6 @@ bool jit::AnalyzeNewScriptDefiniteProper
     // contingent on the correct frames being inlined. Add constraints to
     // invalidate the definite properties if additional functions could be
     // called at the inline frame sites.
-    Vector<MBasicBlock*> exitBlocks(cx);
     for (MBasicBlockIterator block(graph.begin()); block != graph.end();
          block++) {
       // Inlining decisions made after the last new property was added to
@@ -4018,9 +4027,9 @@ bool jit::AnalyzeNewScriptDefiniteProper
       if (MResumePoint* rp = block->callerResumePoint()) {
         if (block->numPredecessors() == 1 &&
             block->getPredecessor(0) == rp->block()) {
-          JSScript* script = rp->block()->info().script();
-          if (!AddClearDefiniteFunctionUsesInScript(cx, group, script,
-                                                    block->info().script()))
+          JSScript* caller = rp->block()->info().script();
+          JSScript* callee = block->info().script();
+          if (!constraintInfo.addInliningConstraint(caller, callee))
             return false;
         }
       }
--- mozjs-60.8.0/js/src/jit/IonAnalysis.h
+++ mozjs-60.8.0/js/src/jit/IonAnalysis.h
@@ -161,8 +161,8 @@ MCompare* ConvertLinearInequality(TempAl
                                   const LinearSum& sum);
 
 MOZ_MUST_USE bool AnalyzeNewScriptDefiniteProperties(
-    JSContext* cx, HandleFunction fun, ObjectGroup* group,
-    HandlePlainObject baseobj,
+    JSContext* cx, DPAConstraintInfo& constraintInfo, HandleFunction fun,
+    ObjectGroup* group, HandlePlainObject baseobj,
     Vector<TypeNewScript::Initializer>* initializerList);
 
 MOZ_MUST_USE bool AnalyzeArgumentsUsage(JSContext* cx, JSScript* script);
--- mozjs-60.8.0/js/src/vm/TypeInference.cpp
+++ mozjs-60.8.0/js/src/vm/TypeInference.cpp
@@ -2759,33 +2759,36 @@ class TypeConstraintClearDefiniteGetterS
   JSCompartment* maybeCompartment() override { return group->compartment(); }
 };
 
-bool js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
-                                                       ObjectGroup* group,
-                                                       HandleId id) {
+bool js::AddClearDefiniteGetterSetterForPrototypeChain(
+    JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
+    HandleId id, bool* added) {
   /*
    * Ensure that if the properties named here could have a getter, setter or
    * a permanent property in any transitive prototype, the definite
    * properties get cleared from the group.
    */
+
+  *added = false;
+
   RootedObject proto(cx, group->proto().toObjectOrNull());
   while (proto) {
     ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
     if (!protoGroup) {
-      cx->recoverFromOutOfMemory();
       return false;
     }
-    if (protoGroup->unknownProperties()) return false;
+    if (protoGroup->unknownProperties())
+      return true;
     HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
-    if (!protoTypes || protoTypes->nonDataProperty() ||
-        protoTypes->nonWritableProperty())
+    if (!protoTypes)
       return false;
-    if (!protoTypes->addConstraint(
-            cx,
-            cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
-                group)))
+    if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
+      return true;
+    if (!constraintInfo.addProtoConstraint(proto, id))
       return false;
     proto = proto->staticPrototype();
   }
+
+  *added = true;
   return true;
 }
 
@@ -3272,6 +3275,43 @@ struct DestroyTypeNewScript {
 
 }  // namespace
 
+bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) {
+  for (const ProtoConstraint& constraint : protoConstraints_) {
+    ObjectGroup* protoGroup = constraint.proto->group();
+
+    // Note: we rely on the group's type information being unchanged since
+    // AddClearDefiniteGetterSetterForPrototypeChain.
+
+    bool unknownProperties = protoGroup->unknownProperties();
+    MOZ_RELEASE_ASSERT(!unknownProperties);
+
+    HeapTypeSet* protoTypes =
+        protoGroup->getProperty(cx, constraint.proto, constraint.id);
+    MOZ_RELEASE_ASSERT(protoTypes);
+
+    MOZ_ASSERT(!protoTypes->nonDataProperty());
+    MOZ_ASSERT(!protoTypes->nonWritableProperty());
+
+    if (!protoTypes->addConstraint(
+            cx,
+            cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
+                group))) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+  }
+
+  for (const InliningConstraint& constraint : inliningConstraints_) {
+    if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller,
+                                              constraint.callee)) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group,
                                  bool* regenerate, bool force) {
   // Perform the new script properties analysis if necessary, returning
@@ -3367,10 +3407,12 @@ bool TypeNewScript::maybeAnalyze(JSConte
 
   Vector<Initializer> initializerVector(cx);
 
+  DPAConstraintInfo constraintInfo(cx);
+
   RootedPlainObject templateRoot(cx, templateObject());
   RootedFunction fun(cx, function());
-  if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, group, templateRoot,
-                                               &initializerVector))
+  if (!jit::AnalyzeNewScriptDefiniteProperties(
+          cx, constraintInfo, fun, group, templateRoot, &initializerVector))
     return false;
 
   if (!group->newScript()) return true;
@@ -3445,6 +3487,14 @@ bool TypeNewScript::maybeAnalyze(JSConte
     // The definite properties analysis found exactly the properties that
     // are held in common by the preliminary objects. No further analysis
     // is needed.
+
+    if (!constraintInfo.finishConstraints(cx, group)) {
+      return false;
+    }
+    if (!group->newScript()) {
+      return true;
+    }
+
     group->addDefiniteProperties(cx, templateObject()->lastProperty());
 
     destroyNewScript.group = nullptr;
@@ -3465,6 +3515,16 @@ bool TypeNewScript::maybeAnalyze(JSConte
       cx, group->clasp(), protoRoot, initialFlags);
   if (!initialGroup) return false;
 
+  // Add the constraints. Use the initialGroup as group referenced by the
+  // constraints because that's the group that will have the TypeNewScript
+  // associated with it. See the detachNewScript and setNewScript calls below.
+  if (!constraintInfo.finishConstraints(cx, initialGroup)) {
+    return false;
+  }
+  if (!group->newScript()) {
+    return true;
+  }
+
   initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty());
   group->addDefiniteProperties(cx, prefixShape);
 
--- mozjs-60.8.0/js/src/vm/TypeInference.h
+++ mozjs-60.8.0/js/src/vm/TypeInference.h
@@ -858,9 +858,58 @@ class TemporaryTypeSet : public TypeSet
                                TypedArraySharedness* sharedness);
 };
 
-bool AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
-                                                   ObjectGroup* group,
-                                                   HandleId id);
+// Stack class to record information about constraints that need to be added
+// after finishing the Definite Properties Analysis. When the analysis succeeds
+// the |finishConstraints| method must be called to add the constraints to the
+// TypeSets.
+//
+// There are two constraint types managed here:
+//
+//   1. Proto constraints for HeapTypeSets, to guard against things like getters
+//      and setters on the proto chain.
+//
+//   2. Inlining constraints for StackTypeSets, to invalidate when additional
+//      functions could be called at call sites where we inlined a function.
+//
+// This class uses bare GC-thing pointers because GC is suppressed when the
+// analysis runs.
+class MOZ_RAII DPAConstraintInfo {
+  struct ProtoConstraint {
+    JSObject* proto;
+    jsid id;
+    ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {}
+  };
+  struct InliningConstraint {
+    JSScript* caller;
+    JSScript* callee;
+    InliningConstraint(JSScript* caller, JSScript* callee)
+        : caller(caller), callee(callee) {}
+  };
+
+  JS::AutoCheckCannotGC nogc_;
+  Vector<ProtoConstraint, 8> protoConstraints_;
+  Vector<InliningConstraint, 4> inliningConstraints_;
+
+ public:
+  explicit DPAConstraintInfo(JSContext* cx)
+      : nogc_(cx), protoConstraints_(cx), inliningConstraints_(cx) {}
+
+  DPAConstraintInfo(const DPAConstraintInfo&) = delete;
+  void operator=(const DPAConstraintInfo&) = delete;
+
+  MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) {
+    return protoConstraints_.emplaceBack(proto, id);
+  }
+  MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) {
+    return inliningConstraints_.emplaceBack(caller, callee);
+  }
+
+  MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group);
+};
+
+bool AddClearDefiniteGetterSetterForPrototypeChain(
+    JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
+    HandleId id, bool* added);
 
 bool AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
                                           JSScript* script,
