Bug 1305005 - Remove race on TypeString() char buffers, r=jonco
authorSteve Fink <sfink@mozilla.com>
Thu, 12 Oct 2017 17:39:59 -0700
changeset 386165 1470a3142fadfc5927a6f87bb6c90bae1d47cb66
parent 386164 9408d9199042d6fa9155f76e7f349af19a2d9d9d
child 386166 8d668d63bfe060a77dab86feb644ce47d204117e
push id32676
push userarchaeopteryx@coole-files.de
push dateFri, 13 Oct 2017 21:38:18 +0000
treeherdermozilla-central@a31334a65a1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1305005
milestone58.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 1305005 - Remove race on TypeString() char buffers, r=jonco
js/src/jit/OptimizationTracking.cpp
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -140,17 +140,17 @@ SpewTempOptimizationTypeInfoVector(JitSp
                                    const char* indent = nullptr)
 {
 #ifdef JS_JITSPEW
     for (const OptimizationTypeInfo* t = types->begin(); t != types->end(); t++) {
         JitSpewStart(channel, "   %s%s of type %s, type set",
                      indent ? indent : "",
                      TrackedTypeSiteString(t->site()), StringFromMIRType(t->mirType()));
         for (uint32_t i = 0; i < t->types().length(); i++)
-            JitSpewCont(channel, " %s", TypeSet::TypeString(t->types()[i]));
+            JitSpewCont(channel, " %s", TypeSet::TypeString(t->types()[i]).get());
         JitSpewFin(channel);
     }
 #endif
 }
 
 void
 SpewTempOptimizationAttemptsVector(JitSpewChannel channel,
                                    const TempOptimizationAttemptsVector* attempts,
@@ -867,44 +867,44 @@ InterpretedFunctionFilenameAndLineNumber
 }
 
 static void
 SpewConstructor(TypeSet::Type ty, JSFunction* constructor)
 {
 #ifdef JS_JITSPEW
     if (!constructor->isInterpreted()) {
         JitSpew(JitSpew_OptimizationTrackingExtended, "   Unique type %s has native constructor",
-                TypeSet::TypeString(ty));
+                TypeSet::TypeString(ty).get());
         return;
     }
 
     char buf[512];
     if (constructor->displayAtom())
         PutEscapedString(buf, 512, constructor->displayAtom(), 0);
     else
         snprintf(buf, mozilla::ArrayLength(buf), "??");
 
     const char* filename;
     Maybe<unsigned> lineno;
     InterpretedFunctionFilenameAndLineNumber(constructor, &filename, &lineno);
 
     JitSpew(JitSpew_OptimizationTrackingExtended, "   Unique type %s has constructor %s (%s:%u)",
-            TypeSet::TypeString(ty), buf, filename, lineno.isSome() ? *lineno : 0);
+            TypeSet::TypeString(ty).get(), buf, filename, lineno.isSome() ? *lineno : 0);
 #endif
 }
 
 static void
 SpewAllocationSite(TypeSet::Type ty, JSScript* script, uint32_t offset)
 {
 #ifdef JS_JITSPEW
     if (!JitSpewEnabled(JitSpew_OptimizationTrackingExtended))
         return;
 
     JitSpew(JitSpew_OptimizationTrackingExtended, "   Unique type %s has alloc site %s:%u",
-            TypeSet::TypeString(ty), script->filename(),
+            TypeSet::TypeString(ty).get(), script->filename(),
             PCToLineNumber(script, script->offsetToPC(offset)));
 #endif
 }
 
 bool
 jit::WriteIonTrackedOptimizationsTable(JSContext* cx, CompactBufferWriter& writer,
                                        const NativeToTrackedOptimizations* start,
                                        const NativeToTrackedOptimizations* end,
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -641,17 +641,17 @@ TypeScript::SetThis(JSContext* cx, JSScr
     StackTypeSet* types = ThisTypes(script);
     if (!types)
         return;
 
     if (!types->hasType(type)) {
         AutoEnterAnalysis enter(cx);
 
         InferSpew(ISpewOps, "externalType: setThis %p: %s",
-                  script, TypeSet::TypeString(type));
+                  script, TypeSet::TypeString(type).get());
         types->addType(cx, type);
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext* cx, JSScript* script, const js::Value& value)
 {
     SetThis(cx, script, TypeSet::GetValueType(value));
@@ -665,17 +665,17 @@ TypeScript::SetArgument(JSContext* cx, J
     StackTypeSet* types = ArgTypes(script, arg);
     if (!types)
         return;
 
     if (!types->hasType(type)) {
         AutoEnterAnalysis enter(cx);
 
         InferSpew(ISpewOps, "externalType: setArg %p %u: %s",
-                  script, arg, TypeSet::TypeString(type));
+                  script, arg, TypeSet::TypeString(type).get());
         types->addType(cx, type);
     }
 }
 
 /* static */ inline void
 TypeScript::SetArgument(JSContext* cx, JSScript* script, unsigned arg, const js::Value& value)
 {
     SetArgument(cx, script, arg, TypeSet::GetValueType(value));
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -119,38 +119,43 @@ TypeSet::NonObjectTypeString(TypeSet::Ty
     }
     if (type.isUnknown())
         return "unknown";
 
     MOZ_ASSERT(type.isAnyObject());
     return "object";
 }
 
-/* static */ const char*
+static UniqueChars MakeStringCopy(const char* s)
+{
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+    char* copy = strdup(s);
+    if (!copy)
+        oomUnsafe.crash("Could not copy string");
+    return UniqueChars(copy);
+}
+
+/* static */ UniqueChars
 TypeSet::TypeString(TypeSet::Type type)
 {
     if (type.isPrimitive() || type.isUnknown() || type.isAnyObject())
-        return NonObjectTypeString(type);
-
-    static char bufs[4][40];
-    static unsigned which = 0;
-    which = (which + 1) & 3;
-
+        return MakeStringCopy(NonObjectTypeString(type));
+
+    char buf[100];
     if (type.isSingleton()) {
         JSObject* singleton = type.singletonNoBarrier();
-        snprintf(bufs[which], 40, "<%s %#" PRIxPTR ">",
-                 singleton->getClass()->name, uintptr_t(singleton));
+        SprintfLiteral(buf, "<%s %#" PRIxPTR ">", singleton->getClass()->name, uintptr_t(singleton));
     } else {
-        snprintf(bufs[which], 40, "[%s * %#" PRIxPTR "]", type.groupNoBarrier()->clasp()->name, uintptr_t(type.groupNoBarrier()));
+        SprintfLiteral(buf, "[%s * %#" PRIxPTR "]", type.groupNoBarrier()->clasp()->name, uintptr_t(type.groupNoBarrier()));
     }
 
-    return bufs[which];
+    return MakeStringCopy(buf);
 }
 
-/* static */ const char*
+/* static */ UniqueChars
 TypeSet::ObjectGroupString(ObjectGroup* group)
 {
     return TypeString(TypeSet::ObjectType(group));
 }
 
 #ifdef DEBUG
 
 bool
@@ -298,18 +303,18 @@ js::ObjectGroupHasProperty(JSContext* cx
             }
             JSObject* obj = &value.toObject();
             if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
                 return true;
         }
 
         if (!types->hasType(type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
-                        TypeSet::ObjectGroupString(group), TypeIdString(id),
-                        TypeSet::TypeString(type));
+                        TypeSet::ObjectGroupString(group).get(), TypeIdString(id),
+                        TypeSet::TypeString(type).get());
         }
     }
     return true;
 }
 
 #endif
 
 
@@ -699,17 +704,17 @@ ConstraintTypeSet::addType(JSContext* cx
 
     if (type.isObjectUnchecked() && unknownObject())
         type = AnyObjectType();
 
     postWriteBarrier(cx, type);
 
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
-              TypeString(type));
+              TypeString(type).get());
 
     /* Propagate the type to all constraints. */
     if (!cx->helperThread()) {
         TypeConstraint* constraint = constraintList();
         while (constraint) {
             constraint->newType(cx, this, type);
             constraint = constraint->next();
         }
@@ -764,17 +769,17 @@ TypeSet::print(FILE* fp)
     uint32_t objectCount = baseObjectCount();
     if (objectCount) {
         fprintf(fp, " object[%u]", objectCount);
 
         unsigned count = getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             ObjectKey* key = getObject(i);
             if (key)
-                fprintf(fp, " %s", TypeString(ObjectType(key)));
+                fprintf(fp, " %s", TypeString(ObjectType(key)).get());
         }
     }
 
     if (fromDebugger)
         fprintf(fp, "\n");
 }
 
 /* static */ void
@@ -2660,27 +2665,28 @@ UpdatePropertyType(JSContext* cx, HeapTy
             types->postWriteBarrier(cx, type);
         }
 
         if (indexed || shape->hadOverwrite()) {
             types->setNonConstantProperty(cx);
         } else {
             InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant",
                       InferSpewColor(types), types, InferSpewColorReset(),
-                      TypeSet::ObjectGroupString(obj->group()), TypeIdString(shape->propid()));
+                      TypeSet::ObjectGroupString(obj->group()).get(),
+                      TypeIdString(shape->propid()));
         }
     }
 }
 
 void
 ObjectGroup::updateNewPropertyTypes(JSContext* cx, JSObject* objArg, jsid id, HeapTypeSet* types)
 {
     InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
               InferSpewColor(types), types, InferSpewColorReset(),
-              TypeSet::ObjectGroupString(this), TypeIdString(id));
+              TypeSet::ObjectGroupString(this).get(), TypeIdString(id));
 
     MOZ_ASSERT_IF(objArg, objArg->group() == this);
     MOZ_ASSERT_IF(singleton(), objArg);
 
     if (!singleton() || !objArg->isNative()) {
         types->setNonConstantProperty(cx);
         return;
     }
@@ -2795,25 +2801,28 @@ js::AddTypePropertyId(JSContext* cx, Obj
 
     HeapTypeSet* types = group->getProperty(cx, obj, id);
     if (!types)
         return;
 
     // Clear any constant flag if it exists.
     if (!types->empty() && !types->nonConstantProperty()) {
         InferSpew(ISpewOps, "constantMutated: %sT%p%s %s",
-                  InferSpewColor(types), types, InferSpewColorReset(), TypeSet::TypeString(type));
+                  InferSpewColor(types), types, InferSpewColorReset(),
+                  TypeSet::TypeString(type).get());
         types->setNonConstantProperty(cx);
     }
 
     if (types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "externalType: property %s %s: %s",
-              TypeSet::ObjectGroupString(group), TypeIdString(id), TypeSet::TypeString(type));
+              TypeSet::ObjectGroupString(group).get(),
+              TypeIdString(id),
+              TypeSet::TypeString(type).get());
     types->addType(cx, type);
 
     // If this addType caused the type set to be marked as containing any
     // object, make sure that is reflected in other type sets the addType is
     // propagated to below.
     if (type.isObjectUnchecked() && types->unknownObject())
         type = TypeSet::AnyObjectType();
 
@@ -2894,17 +2903,17 @@ ObjectGroup::setFlags(JSContext* cx, Obj
 
     if (hasAllFlags(flags))
         return;
 
     AutoEnterAnalysis enter(cx);
 
     addFlags(flags);
 
-    InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeSet::ObjectGroupString(this), flags);
+    InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeSet::ObjectGroupString(this).get(), flags);
 
     ObjectStateChange(cx, this, false);
 
     // Propagate flag changes from partially to fully initialized groups for the
     // acquired properties analysis.
     if (newScript() && newScript()->initializedGroup())
         newScript()->initializedGroup()->setFlags(cx, flags);
 
@@ -2918,17 +2927,17 @@ ObjectGroup::setFlags(JSContext* cx, Obj
 void
 ObjectGroup::markUnknown(JSContext* cx)
 {
     AutoEnterAnalysis enter(cx);
 
     MOZ_ASSERT(cx->zone()->types.activeAnalysis);
     MOZ_ASSERT(!unknownProperties());
 
-    InferSpew(ISpewOps, "UnknownProperties: %s", TypeSet::ObjectGroupString(this));
+    InferSpew(ISpewOps, "UnknownProperties: %s", TypeSet::ObjectGroupString(this).get());
 
     clearNewScript(cx);
     ObjectStateChange(cx, this, true);
 
     /*
      * Existing constraints may have already been added to this object, which we need
      * to do the right thing for. We can't ensure that we will mark all unknown
      * objects before they have been accessed, as the __proto__ of a known object
@@ -3065,19 +3074,19 @@ ObjectGroup::clearNewScript(JSContext* c
     markStateChange(cx);
 }
 
 void
 ObjectGroup::print()
 {
     TaggedProto tagged(proto());
     fprintf(stderr, "%s : %s",
-            TypeSet::ObjectGroupString(this),
+            TypeSet::ObjectGroupString(this).get(),
             tagged.isObject()
-            ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject()))
+            ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject())).get()
             : tagged.isDynamic()
             ? "(dynamic)"
             : "(null)");
 
     if (unknownProperties()) {
         fprintf(stderr, " unknown");
     } else {
         if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
@@ -3320,33 +3329,33 @@ js::TypeMonitorResult(JSContext* cx, JSS
 
     AutoEnterAnalysis enter(cx);
 
     StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
     if (types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "bytecodeType: %p %05zu: %s",
-              script, script->pcToOffset(pc), TypeSet::TypeString(type));
+              script, script->pcToOffset(pc), TypeSet::TypeString(type).get());
     types->addType(cx, type);
 }
 
 void
 js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, StackTypeSet* types,
                       TypeSet::Type type)
 {
     assertSameCompartment(cx, script, type);
 
     AutoEnterAnalysis enter(cx);
 
     MOZ_ASSERT(types == TypeScript::BytecodeTypes(script, pc));
     MOZ_ASSERT(!types->hasType(type));
 
     InferSpew(ISpewOps, "bytecodeType: %p %05zu: %s",
-              script, script->pcToOffset(pc), TypeSet::TypeString(type));
+              script, script->pcToOffset(pc), TypeSet::TypeString(type).get());
     types->addType(cx, type);
 }
 
 void
 js::TypeMonitorResult(JSContext* cx, JSScript* script, jsbytecode* pc, const js::Value& rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(CodeSpec[*pc].format & JOF_TYPESET))
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -377,18 +377,18 @@ class TypeSet
     }
 
     static inline Type ObjectType(JSObject* obj);
     static inline Type ObjectType(ObjectGroup* group);
     static inline Type ObjectType(ObjectKey* key);
 
     static const char* NonObjectTypeString(Type type);
 
-    static const char* TypeString(Type type);
-    static const char* ObjectGroupString(ObjectGroup* group);
+    static UniqueChars TypeString(Type type);
+    static UniqueChars ObjectGroupString(ObjectGroup* group);
 
   protected:
     /* Flags for this type set. */
     TypeFlags flags;
 
     /* Possible objects this type set can represent. */
     ObjectKey** objectSet;