Переглянути джерело

ChakraCore fix for servicing release 18-02B: CVE-2018-0858

Jianchun Xu 8 роки тому
батько
коміт
972009a89e
40 змінених файлів з 238 додано та 106 видалено
  1. 2 2
      lib/Backend/FunctionJITTimeInfo.cpp
  2. 2 2
      lib/Backend/JITTimeFunctionBody.cpp
  3. 1 1
      lib/Common/Memory/Allocator.h
  4. 8 0
      lib/Common/Memory/WriteBarrierMacros.h
  5. 3 2
      lib/Runtime/Base/FunctionBody.h
  6. 1 1
      lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp
  7. 1 1
      lib/Runtime/Debug/DiagObjectModel.cpp
  8. 3 2
      lib/Runtime/Debug/DiagObjectModel.h
  9. 2 2
      lib/Runtime/Debug/TTInflateMap.cpp
  10. 4 4
      lib/Runtime/Debug/TTInflateMap.h
  11. 1 1
      lib/Runtime/Debug/TTSnapObjects.cpp
  12. 4 4
      lib/Runtime/Debug/TTSnapValues.cpp
  13. 1 1
      lib/Runtime/Debug/TTSnapValues.h
  14. 1 1
      lib/Runtime/Debug/TTSnapshot.cpp
  15. 3 3
      lib/Runtime/Debug/TTSnapshotExtractor.cpp
  16. 1 1
      lib/Runtime/Debug/TTSnapshotExtractor.h
  17. 1 1
      lib/Runtime/Language/AsmJsModule.cpp
  18. 4 3
      lib/Runtime/Language/InterpreterStackFrame.cpp
  19. 2 2
      lib/Runtime/Language/JavascriptOperators.cpp
  20. 4 3
      lib/Runtime/Library/BoundFunction.cpp
  21. 2 1
      lib/Runtime/Library/BoundFunction.h
  22. 3 1
      lib/Runtime/Library/ConcatString.cpp
  23. 17 5
      lib/Runtime/Library/JavascriptArray.cpp
  24. 3 2
      lib/Runtime/Library/JavascriptArray.h
  25. 2 2
      lib/Runtime/Library/JavascriptGeneratorFunction.cpp
  26. 2 1
      lib/Runtime/Library/JavascriptLibrary.cpp
  27. 2 1
      lib/Runtime/Library/JavascriptLibrary.h
  28. 4 4
      lib/Runtime/Library/RegexHelper.cpp
  29. 6 4
      lib/Runtime/Library/ScriptFunction.cpp
  30. 5 0
      lib/Runtime/Library/SparseArraySegment.h
  31. 17 15
      lib/Runtime/Library/StackScriptFunction.cpp
  32. 1 1
      lib/Runtime/Library/StackScriptFunction.h
  33. 1 1
      lib/Runtime/Library/WebAssemblyEnvironment.cpp
  34. 1 1
      lib/Runtime/Library/WebAssemblyEnvironment.h
  35. 2 2
      lib/Runtime/Library/WebAssemblyInstance.cpp
  36. 4 3
      lib/Runtime/Library/WebAssemblyTable.cpp
  37. 2 1
      lib/Runtime/Library/WebAssemblyTable.h
  38. 1 1
      lib/Runtime/Types/DynamicObjectPropertyEnumerator.cpp
  39. 89 23
      tools/RecyclerChecker/RecyclerChecker.cpp
  40. 25 0
      tools/RecyclerChecker/RecyclerChecker.h

+ 2 - 2
lib/Backend/FunctionJITTimeInfo.cpp

@@ -112,7 +112,7 @@ FunctionJITTimeInfo::BuildJITTimeData(
         if(objTypeSpecInfo)
         {
             jitData->objTypeSpecFldInfoCount = jitData->bodyData->inlineCacheCount;
-            jitData->objTypeSpecFldInfoArray = (ObjTypeSpecFldIDL**)objTypeSpecInfo;
+            jitData->objTypeSpecFldInfoArray = unsafe_write_barrier_cast<ObjTypeSpecFldIDL**>(objTypeSpecInfo);
         }
         for (Js::InlineCacheIndex i = 0; i < jitData->bodyData->inlineCacheCount; ++i)
         {
@@ -131,7 +131,7 @@ FunctionJITTimeInfo::BuildJITTimeData(
         Assert(globObjTypeSpecInfo != nullptr);
 
         jitData->globalObjTypeSpecFldInfoCount = codeGenData->GetGlobalObjTypeSpecFldInfoCount();
-        jitData->globalObjTypeSpecFldInfoArray = (ObjTypeSpecFldIDL**)globObjTypeSpecInfo;
+        jitData->globalObjTypeSpecFldInfoArray = unsafe_write_barrier_cast<ObjTypeSpecFldIDL**>(globObjTypeSpecInfo);
     }
     const Js::FunctionCodeGenJitTimeData * nextJITData = codeGenData->GetNext();
     if (nextJITData != nullptr)

+ 2 - 2
lib/Backend/JITTimeFunctionBody.cpp

@@ -25,7 +25,7 @@ JITTimeFunctionBody::InitializeJITFunctionData(
     jitBody->constCount = numConstants;
     if (numConstants > 0)
     {
-        jitBody->constTable = (intptr_t *)PointerValue(functionBody->GetConstTable());
+        jitBody->constTable = unsafe_write_barrier_cast<intptr_t *>(functionBody->GetConstTable());
         if (!functionBody->GetIsAsmJsFunction())
         {
             jitBody->constTableContent = AnewStructZ(arena, ConstTableContentIDL);
@@ -242,7 +242,7 @@ JITTimeFunctionBody::InitializeJITFunctionData(
     jitBody->displayName = (char16 *)functionBody->GetDisplayName();
     jitBody->objectLiteralTypesAddr = (intptr_t)functionBody->GetObjectLiteralTypesWithLock();
     jitBody->literalRegexCount = functionBody->GetLiteralRegexCount();
-    jitBody->literalRegexes = (intptr_t*)functionBody->GetLiteralRegexesWithLock();
+    jitBody->literalRegexes = unsafe_write_barrier_cast<intptr_t*>(functionBody->GetLiteralRegexesWithLock());
 
     Js::AuxArray<uint32> * slotIdInCachedScopeToNestedIndexArray = functionBody->GetSlotIdInCachedScopeToNestedIndexArrayWithLock();
     if (slotIdInCachedScopeToNestedIndexArray)

+ 1 - 1
lib/Common/Memory/Allocator.h

@@ -174,7 +174,7 @@ inline T* PostAllocationCallback(const type_info& objType, T *obj)
 
 // Free routine where we don't care about following C++ semantics (e.g. calling the destructor)
 #define AllocatorFree(alloc, freeFunc, obj, size) \
-        (alloc->*freeFunc)((void*)obj, size)
+        (alloc->*freeFunc)(obj, size)
 
 // default type allocator implementation
 template <typename TAllocator, typename T>

+ 8 - 0
lib/Common/Memory/WriteBarrierMacros.h

@@ -41,3 +41,11 @@
 
 // use with FieldWithBarrier structs
 #define FORCE_NO_WRITE_BARRIER_TAG(arg) arg, _no_write_barrier_tag()
+
+// Unsafely cast a typical "Field() *" type. Only use in rare cases where we
+// understand the underlying memory usage.
+template <class T, class U>
+inline T unsafe_write_barrier_cast(U p)
+{
+    return (T)p;
+}

+ 3 - 2
lib/Runtime/Base/FunctionBody.h

@@ -3598,8 +3598,9 @@ namespace Js
         static uint const EncodedSlotCountSlotIndex = 0;
         static uint const ScopeMetadataSlotIndex = 1;    // Either a FunctionBody* or DebuggerScope*
         static uint const FirstSlotIndex = 2;
+
     public:
-        ScopeSlots(Var* slotArray) : slotArray((Field(Var)*)slotArray)
+        ScopeSlots(Field(Var)* slotArray) : slotArray(slotArray)
         {
         }
 
@@ -3710,7 +3711,7 @@ namespace Js
         bool   GetStrictMode() const { return strictMode; }
         void   SetStrictMode(bool flag) { this->strictMode = flag; }
 
-        void** GetDataAddress() { return (void**)&this->scopes; }
+        Field(void*)* GetDataAddress() { return this->scopes; }
         static uint32 GetOffsetOfStrictMode() { return offsetof(FrameDisplay, strictMode); }
         static uint32 GetOffsetOfLength() { return offsetof(FrameDisplay, length); }
         static uint32 GetOffsetOfScopes() { return offsetof(FrameDisplay, scopes); }

+ 1 - 1
lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp

@@ -217,7 +217,7 @@ namespace Js
 
     void AsmJsByteCodeDumper::DumpConstants(AsmJsFunc* func, FunctionBody* body)
     {
-        byte* table = (byte*)((Var*)body->GetConstTable());
+        byte* table = (byte*)body->GetConstTable();
         auto constSrcInfos = func->GetTypedRegisterAllocator().GetConstSourceInfos();
         for (int i = 0; i < WAsmJs::LIMIT; ++i)
         {

+ 1 - 1
lib/Runtime/Debug/DiagObjectModel.cpp

@@ -4086,7 +4086,7 @@ namespace Js
         else
         {
             // The scope is defined by a slot array object so grab the function body out to get the function name.
-            ScopeSlots slotArray = ScopeSlots(reinterpret_cast<Var*>(instance));
+            ScopeSlots slotArray = ScopeSlots(reinterpret_cast<Field(Var)*>(instance));
 
             if(slotArray.IsDebuggerScopeSlotArray())
             {

+ 3 - 2
lib/Runtime/Debug/DiagObjectModel.h

@@ -251,8 +251,9 @@ namespace Js
         virtual void PopulateMembers() override;
         virtual IDiagObjectAddress * GetObjectAddress(int index) override;
 
-        ScopeSlots GetSlotArray() {
-            Var *slotArray = (Var *) instance;
+        ScopeSlots GetSlotArray()
+        {
+            Field(Var) *slotArray = (Field(Var) *) instance;
             Assert(slotArray != nullptr);
             return ScopeSlots(slotArray);
         }

+ 2 - 2
lib/Runtime/Debug/TTInflateMap.cpp

@@ -191,7 +191,7 @@ namespace TTD
         return this->m_environmentMap.LookupKnownItem(envid);
     }
 
-    Js::Var* InflateMap::LookupSlotArray(TTD_PTR_ID slotid) const
+    Field(Js::Var)* InflateMap::LookupSlotArray(TTD_PTR_ID slotid) const
     {
         return this->m_slotArrayMap.LookupKnownItem(slotid);
     }
@@ -238,7 +238,7 @@ namespace TTD
         this->m_environmentPinSet->AddNew(value);
     }
 
-    void InflateMap::AddSlotArray(TTD_PTR_ID slotId, Js::Var* value)
+    void InflateMap::AddSlotArray(TTD_PTR_ID slotId, Field(Js::Var)* value)
     {
         this->m_slotArrayMap.AddItem(slotId, value);
         this->m_slotArrayPinSet->AddNew(value);

+ 4 - 4
lib/Runtime/Debug/TTInflateMap.h

@@ -15,14 +15,14 @@ namespace TTD
         TTDIdentifierDictionary<TTD_PTR_ID, Js::DynamicTypeHandler*> m_handlerMap;
         TTDIdentifierDictionary<TTD_PTR_ID, Js::Type*> m_typeMap;
 
-        //The maps for script contexts and objects 
+        //The maps for script contexts and objects
         TTDIdentifierDictionary<TTD_LOG_PTR_ID, Js::GlobalObject*> m_tagToGlobalObjectMap; //get the script context from here
         TTDIdentifierDictionary<TTD_PTR_ID, Js::RecyclableObject*> m_objectMap;
 
         //The maps for inflated function bodies
         TTDIdentifierDictionary<TTD_PTR_ID, Js::FunctionBody*> m_functionBodyMap;
         TTDIdentifierDictionary<TTD_PTR_ID, Js::FrameDisplay*> m_environmentMap;
-        TTDIdentifierDictionary<TTD_PTR_ID, Js::Var*> m_slotArrayMap;
+        TTDIdentifierDictionary<TTD_PTR_ID, Field(Js::Var)*> m_slotArrayMap;
 
         //The maps for resolving debug scopes
         TTDIdentifierDictionary<TTD_PTR_ID, Js::FunctionBody*> m_debuggerScopeHomeBodyMap;
@@ -72,7 +72,7 @@ namespace TTD
 
         Js::FunctionBody* LookupFunctionBody(TTD_PTR_ID functionId) const;
         Js::FrameDisplay* LookupEnvironment(TTD_PTR_ID envid) const;
-        Js::Var* LookupSlotArray(TTD_PTR_ID slotid) const;
+        Field(Js::Var)* LookupSlotArray(TTD_PTR_ID slotid) const;
 
         void LookupInfoForDebugScope(TTD_PTR_ID dbgScopeId, Js::FunctionBody** homeBody, int32* chainIndex) const;
 
@@ -86,7 +86,7 @@ namespace TTD
 
         void AddInflationFunctionBody(TTD_PTR_ID functionId, Js::FunctionBody* value);
         void AddEnvironment(TTD_PTR_ID envId, Js::FrameDisplay* value);
-        void AddSlotArray(TTD_PTR_ID slotId, Js::Var* value);
+        void AddSlotArray(TTD_PTR_ID slotId, Field(Js::Var)* value);
 
         void UpdateFBScopes(const NSSnapValues::SnapFunctionBodyScopeChain& scopeChainInfo, Js::FunctionBody* fb);
 

+ 1 - 1
lib/Runtime/Debug/TTSnapObjects.cpp

@@ -918,7 +918,7 @@ namespace TTD
                 }
             }
 
-            return ctx->GetLibrary()->CreateBoundFunction_TTD(bFunction, bThis, snapBoundInfo->ArgCount, (Js::Var*)bArgs);
+            return ctx->GetLibrary()->CreateBoundFunction_TTD(bFunction, bThis, snapBoundInfo->ArgCount, bArgs);
         }
 
         void EmitAddtlInfo_SnapBoundFunctionInfo(const SnapObject* snpObject, FileWriter* writer)

+ 4 - 4
lib/Runtime/Debug/TTSnapValues.cpp

@@ -556,12 +556,12 @@ namespace TTD
 
         //////////////////
 
-        Js::Var* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator)
+        Field(Js::Var)* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator)
         {
             Js::ScriptContext* ctx = inflator->LookupScriptContext(slotInfo->ScriptContextLogId);
             Field(Js::Var)* slotArray = RecyclerNewArray(ctx->GetRecycler(), Field(Js::Var), slotInfo->SlotCount + Js::ScopeSlots::FirstSlotIndex);
 
-            Js::ScopeSlots scopeSlots((Js::Var*)slotArray);
+            Js::ScopeSlots scopeSlots(slotArray);
             scopeSlots.SetCount(slotInfo->SlotCount);
 
             Js::Var undef = ctx->GetLibrary()->GetUndefined();
@@ -625,7 +625,7 @@ namespace TTD
                 }
             }
 
-            return (Js::Var*)slotArray;
+            return slotArray;
         }
 
         void EmitSlotArrayInfo(const SlotArrayInfo* slotInfo, FileWriter* writer, NSTokens::Separator separator)
@@ -782,7 +782,7 @@ namespace TTD
                 }
                 case Js::ScopeType::ScopeType_SlotArray:
                 {
-                    Js::Var* saval = inflator->LookupSlotArray(scp.IDValue);
+                    Field(Js::Var)* saval = inflator->LookupSlotArray(scp.IDValue);
                     environment->SetItem(i, saval);
                     break;
                 }

+ 1 - 1
lib/Runtime/Debug/TTSnapValues.h

@@ -153,7 +153,7 @@ namespace TTD
             TTD_WELLKNOWN_TOKEN OptWellKnownDbgScope;
         };
 
-        Js::Var* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator);
+        Field(Js::Var)* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator);
 
         void EmitSlotArrayInfo(const SlotArrayInfo* slotInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParseSlotArrayInfo(SlotArrayInfo* slotInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);

+ 1 - 1
lib/Runtime/Debug/TTSnapshot.cpp

@@ -541,7 +541,7 @@ namespace TTD
         for(auto iter = this->m_slotArrayEntries.GetIterator(); iter.IsValid(); iter.MoveNext())
         {
             const NSSnapValues::SlotArrayInfo* sai = iter.Current();
-            Js::Var* slots = NSSnapValues::InflateSlotArrayInfo(sai, inflator);
+            Field(Js::Var)* slots = NSSnapValues::InflateSlotArrayInfo(sai, inflator);
 
             inflator->AddSlotArray(sai->SlotId, slots);
         }

+ 3 - 3
lib/Runtime/Debug/TTSnapshotExtractor.cpp

@@ -88,7 +88,7 @@ namespace TTD
         }
     }
 
-    void SnapshotExtractor::ExtractSlotArrayIfNeeded(Js::ScriptContext* ctx, Js::Var* scope)
+    void SnapshotExtractor::ExtractSlotArrayIfNeeded(Js::ScriptContext* ctx, Field(Js::Var)* scope)
     {
         if(this->m_marks.IsMarked(scope))
         {
@@ -177,7 +177,7 @@ namespace TTD
                     break;
                 case Js::ScopeType::ScopeType_SlotArray:
                 {
-                    this->ExtractSlotArrayIfNeeded(ctx, (Js::Var*)scope);
+                    this->ExtractSlotArrayIfNeeded(ctx, (Field(Js::Var)*)scope);
 
                     entryInfo->IDValue = TTD_CONVERT_SLOTARRAY_TO_PTR_ID((Js::Var*)scope);
                     break;
@@ -322,7 +322,7 @@ namespace TTD
                 {
                     if(this->m_marks.MarkAndTestAddr<MarkTableTag::SlotArrayTag>(scope))
                     {
-                        Js::ScopeSlots slotArray = (Js::Var*)scope;
+                        Js::ScopeSlots slotArray = (Field(Js::Var)*)scope;
                         uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
                         if(!slotArray.IsDebuggerScopeSlotArray())
                         {

+ 1 - 1
lib/Runtime/Debug/TTSnapshotExtractor.h

@@ -39,7 +39,7 @@ namespace TTD
         void ExtractTypeIfNeeded(Js::Type* jstype, ThreadContext* threadContext);
 
         //Ensure that a slot/scope has been extracted
-        void ExtractSlotArrayIfNeeded(Js::ScriptContext* ctx, Js::Var* scope);
+        void ExtractSlotArrayIfNeeded(Js::ScriptContext* ctx, Field(Js::Var)* scope);
         void ExtractScopeIfNeeded(Js::ScriptContext* ctx, Js::FrameDisplay* environment);
         void ExtractScriptFunctionEnvironmentIfNeeded(Js::ScriptFunction* function);
 

+ 1 - 1
lib/Runtime/Language/AsmJsModule.cpp

@@ -2501,7 +2501,7 @@ namespace Js
 #else
         Field(Var) * slotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), moduleBody->scopeSlotArraySize + ScopeSlots::FirstSlotIndex);
 #endif
-        ScopeSlots scopeSlots((Js::Var*)slotArray);
+        ScopeSlots scopeSlots(slotArray);
         scopeSlots.SetCount(moduleBody->scopeSlotArraySize);
         scopeSlots.SetScopeMetadata(moduleBody->GetFunctionInfo());
 

+ 4 - 3
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -1254,7 +1254,7 @@ namespace Js
                 {
                     uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
                     Assert(scopeSlots != 0);
-                    ScopeSlots((Var*)nextAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
+                    ScopeSlots((Field(Var)*)nextAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
                     newInstance->localClosure = nextAllocBytes;
                     nextAllocBytes += (scopeSlots + ScopeSlots::FirstSlotIndex) * sizeof(Var);
                 }
@@ -2733,7 +2733,7 @@ namespace Js
 
                 scriptFuncObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint);
                 scriptFuncObj->GetFunctionBody()->GetAsmJsFunctionInfo()->SetModuleFunctionBody(asmJsModuleFunctionBody);
-                scriptFuncObj->SetModuleEnvironment((Field(Var)*)moduleMemoryPtr);
+                scriptFuncObj->SetModuleEnvironment(moduleMemoryPtr);
                 if (!info->IsRuntimeProcessed())
                 {
                     // don't reset entrypoint upon relinking
@@ -3812,6 +3812,7 @@ namespace Js
 #endif
 
         if (playout->Return == Js::Constants::NoRegister)
+        
         {
             Arguments args(CallInfo(CallFlags_NotUsed, playout->ArgCount), m_outParams);
             JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args);
@@ -7406,7 +7407,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(uint loopId)
         slotArray = (Field(Var)*)this->GetLocalClosure();
         Assert(slotArray != nullptr);
 
-        ScopeSlots scopeSlots((Js::Var*)slotArray);
+        ScopeSlots scopeSlots(slotArray);
         scopeSlots.SetCount(scopeSlotCount);
         scopeSlots.SetScopeMetadata((Var)functionBody->GetFunctionInfo());
         Var undef = functionBody->GetScriptContext()->GetLibrary()->GetUndefined();

+ 2 - 2
lib/Runtime/Language/JavascriptOperators.cpp

@@ -6975,7 +6975,7 @@ CommonNumber:
         Assert(size > ScopeSlots::FirstSlotIndex); // Should never see empty slot array
         Field(Var)* slotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), size); // last initialized slot contains reference to array of propertyIds, correspondent to objects in previous slots
         uint count = size - ScopeSlots::FirstSlotIndex;
-        ScopeSlots slots((Js::Var*)slotArray);
+        ScopeSlots slots(slotArray);
         slots.SetCount(count);
         AssertMsg(!FunctionBody::Is(scope), "Scope should only be FunctionInfo or DebuggerScope, not FunctionBody");
         slots.SetScopeMetadata(scope);
@@ -7002,7 +7002,7 @@ CommonNumber:
 
     Field(Var)* JavascriptOperators::OP_CloneScopeSlots(Field(Var) *slotArray, ScriptContext *scriptContext)
     {
-        ScopeSlots slots((Js::Var*)slotArray);
+        ScopeSlots slots(slotArray);
         uint size = ScopeSlots::FirstSlotIndex + static_cast<uint>(slots.GetCount());
 
         Field(Var)* slotArrayClone = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), size);

+ 4 - 3
lib/Runtime/Library/BoundFunction.cpp

@@ -188,7 +188,7 @@ namespace Js
                 newValues[index++] = args[i];
             }
 
-            actualArgs = Arguments(args.Info, (Var*)newValues);
+            actualArgs = Arguments(args.Info, unsafe_write_barrier_cast<Var*>(newValues));
             actualArgs.Info.Count = boundFunction->count + argCount;
         }
         else
@@ -522,13 +522,14 @@ namespace Js
         TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapBoundFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::SnapBoundFunctionObject>(objData, bfi, alloc, depCount, depArray);
     }
 
-    BoundFunction* BoundFunction::InflateBoundFunction(ScriptContext* ctx, RecyclableObject* function, Var bThis, uint32 ct, Var* args)
+    BoundFunction* BoundFunction::InflateBoundFunction(
+        ScriptContext* ctx, RecyclableObject* function, Var bThis, uint32 ct, Field(Var)* args)
     {
         BoundFunction* res = RecyclerNew(ctx->GetRecycler(), BoundFunction, ctx->GetLibrary()->GetBoundFunctionType());
 
         res->boundThis = bThis;
         res->count = ct;
-        res->boundArgs = (Field(Var)*)args;
+        res->boundArgs = args;
 
         res->targetFunction = function;
 

+ 2 - 1
lib/Runtime/Library/BoundFunction.h

@@ -63,7 +63,8 @@ namespace Js
         virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
         virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
 
-        static BoundFunction* InflateBoundFunction(ScriptContext* ctx, RecyclableObject* function, Var bThis, uint32 ct, Var* args);
+        static BoundFunction* InflateBoundFunction(
+            ScriptContext* ctx, RecyclableObject* function, Var bThis, uint32 ct, Field(Var)* args);
 #endif
 
     private:

+ 3 - 1
lib/Runtime/Library/ConcatString.cpp

@@ -130,8 +130,10 @@ namespace Js
 
         if (this->propertyRecord == nullptr)
         {
+            Js::PropertyRecord const * propertyRecord = nullptr;
             scriptContext->GetOrAddPropertyRecord(this->GetSz(), static_cast<int>(this->GetLength()),
-                (Js::PropertyRecord const **)&(this->propertyRecord));
+                &propertyRecord);
+            this->propertyRecord = propertyRecord;
         }
 
         this->propertyString = scriptContext->GetPropertyString(propertyRecord->GetPropertyId());

+ 17 - 5
lib/Runtime/Library/JavascriptArray.cpp

@@ -7016,7 +7016,8 @@ Case0:
     }
 
     template<typename T>
-    void JavascriptArray::ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment<T> *seg, SparseArraySegment<T> **prev,
+    void JavascriptArray::ArraySegmentSpliceHelper(
+        JavascriptArray *pnewArr, SparseArraySegment<T> *seg, Field(SparseArraySegment<T>*) *prev,
                                                     uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, Recycler *recycler)
     {
         // book keeping variables
@@ -7138,7 +7139,9 @@ Case0:
                 // All splice happens in one segment.
                 SparseArraySegmentBase *nextSeg = startSeg->next;
                 // Splice the segment first, which might OOM throw but the array would be intact.
-                JavascriptArray::ArraySegmentSpliceHelper(pnewArr, (SparseArraySegment<T>*)startSeg, (SparseArraySegment<T>**)prevSeg, start, deleteLen, insertArgs, insertLen, recycler);
+                JavascriptArray::ArraySegmentSpliceHelper(
+                    pnewArr, startSeg, SparseArraySegment<T>::AddressFrom(prevSeg),
+                    start, deleteLen, insertArgs, insertLen, recycler);
                 while (nextSeg)
                 {
                     // adjust next segments left
@@ -7476,15 +7479,24 @@ Case0:
                 bool isInlineSegment = JavascriptArray::IsInlineSegment(oldHead, pArr);
                 if (isIntArray)
                 {
-                    ArraySegmentSpliceHelper<int32>(newArr, SparseArraySegment<int32>::From(pArr->head), (SparseArraySegment<int32>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
+                    ArraySegmentSpliceHelper<int32>(newArr,
+                        SparseArraySegment<int32>::From(pArr->head),
+                        SparseArraySegment<int32>::AddressFrom(&pArr->head),
+                        start, deleteLen, insertArgs, insertLen, recycler);
                 }
                 else if (isFloatArray)
                 {
-                    ArraySegmentSpliceHelper<double>(newArr, SparseArraySegment<double>::From(pArr->head), (SparseArraySegment<double>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
+                    ArraySegmentSpliceHelper<double>(newArr,
+                        SparseArraySegment<double>::From(pArr->head),
+                        SparseArraySegment<double>::AddressFrom(&pArr->head),
+                        start, deleteLen, insertArgs, insertLen, recycler);
                 }
                 else
                 {
-                    ArraySegmentSpliceHelper<Var>(newArr, SparseArraySegment<Var>::From(pArr->head), (SparseArraySegment<Var>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
+                    ArraySegmentSpliceHelper<Var>(newArr,
+                        SparseArraySegment<Var>::From(pArr->head),
+                        SparseArraySegment<Var>::AddressFrom(&pArr->head),
+                        start, deleteLen, insertArgs, insertLen, recycler);
                 }
 
                 if (isInlineSegment && oldHead != pArr->head)

+ 3 - 2
lib/Runtime/Library/JavascriptArray.h

@@ -595,8 +595,9 @@ namespace Js
         static void ArraySpliceHelper(JavascriptArray* pNewArr, JavascriptArray* pArr, uint32 start, uint32 deleteLen,
                                                     Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext);
         template<typename T>
-        static void ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment<T> *seg, SparseArraySegment<T> **prev, uint32 start, uint32 deleteLen,
-                                                    Var* insertArgs, uint32 insertLen, Recycler *recycler);
+        static void ArraySegmentSpliceHelper(
+            JavascriptArray *pnewArr, SparseArraySegment<T> *seg, Field(SparseArraySegment<T>*) *prev,
+            uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, Recycler *recycler);
         template<typename T>
         static RecyclableObject* ObjectSpliceHelper(RecyclableObject* pObj, T len, T start, T deleteLen,
                                                     Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj = nullptr);

+ 2 - 2
lib/Runtime/Library/JavascriptGeneratorFunction.cpp

@@ -137,7 +137,7 @@ namespace Js
         // and use that buffer for this InterpreterStackFrame.
         Field(Var)* argsHeapCopy = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), stackArgs.Info.Count);
         CopyArray(argsHeapCopy, stackArgs.Info.Count, stackArgs.Values, stackArgs.Info.Count);
-        Arguments heapArgs(callInfo, (Var*)argsHeapCopy);
+        Arguments heapArgs(callInfo, unsafe_write_barrier_cast<Var*>(argsHeapCopy));
 
         DynamicObject* prototype = scriptContext->GetLibrary()->CreateGeneratorConstructorPrototypeObject();
         JavascriptGenerator* generator = scriptContext->GetLibrary()->CreateGenerator(heapArgs, generatorFunction->scriptFunction, prototype);
@@ -163,7 +163,7 @@ namespace Js
         // and use that buffer for this InterpreterStackFrame.
         Field(Var)* argsHeapCopy = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), stackArgs.Info.Count);
         CopyArray(argsHeapCopy, stackArgs.Info.Count, stackArgs.Values, stackArgs.Info.Count);
-        Arguments heapArgs(callInfo, (Var*)argsHeapCopy);
+        Arguments heapArgs(callInfo, unsafe_write_barrier_cast<Var*>(argsHeapCopy));
 
         JavascriptExceptionObject* e = nullptr;
         JavascriptPromiseResolveOrRejectFunction* resolve;

+ 2 - 1
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -5736,7 +5736,8 @@ namespace Js
         return function;
     }
 
-    Js::RecyclableObject* JavascriptLibrary::CreateBoundFunction_TTD(RecyclableObject* function, Var bThis, uint32 ct, Var* args)
+    Js::RecyclableObject* JavascriptLibrary::CreateBoundFunction_TTD(
+        RecyclableObject* function, Var bThis, uint32 ct, Field(Var)* args)
     {
         return BoundFunction::InflateBoundFunction(this->scriptContext, function, bThis, ct, args);
     }

+ 2 - 1
lib/Runtime/Library/JavascriptLibrary.h

@@ -733,7 +733,8 @@ namespace Js
         static void AddWeakMapElementInflate_TTD(Js::JavascriptWeakMap* map, Var key, Var value);
 
         Js::RecyclableObject* CreateExternalFunction_TTD(Js::Var fname);
-        Js::RecyclableObject* CreateBoundFunction_TTD(RecyclableObject* function, Var bThis, uint32 ct, Var* args);
+        Js::RecyclableObject* CreateBoundFunction_TTD(
+                RecyclableObject* function, Var bThis, uint32 ct, Field(Var)* args);
 
         Js::RecyclableObject* CreateProxy_TTD(RecyclableObject* handler, RecyclableObject* target);
         Js::RecyclableObject* CreateRevokeFunction_TTD(RecyclableObject* proxy);

+ 4 - 4
lib/Runtime/Library/RegexHelper.cpp

@@ -915,7 +915,7 @@ namespace Js
             ArenaAllocator* tempAlloc,
             JavascriptString* matchStr,
             int numberOfCaptures,
-            Var* captures,
+            Field(Var)* captures,
             CharCount position)
         {
             CharCount* substitutionOffsets = nullptr;
@@ -925,7 +925,7 @@ namespace Js
                 tempAlloc,
                 &substitutionOffsets);
             auto getGroup = [&](int captureIndex, Var nonMatchValue) {
-                return captureIndex <= numberOfCaptures ? captures[captureIndex] : nonMatchValue;
+                return captureIndex <= numberOfCaptures ? PointerValue(captures[captureIndex]) : nonMatchValue;
             };
             UnifiedRegex::GroupInfo match(position, matchStr->GetLength());
             int numGroups = numberOfCaptures + 1; // Take group 0 into account.
@@ -951,7 +951,7 @@ namespace Js
             ArenaAllocator* tempAlloc,
             JavascriptString* matchStr,
             int numberOfCaptures,
-            Var* captures,
+            Field(Var)* captures,
             CharCount position)
         {
             // replaceFn Arguments:
@@ -1088,7 +1088,7 @@ namespace Js
                     CharCount substringLength = position - nextSourcePosition;
                     accumulatedResultBuilder.Append(input, nextSourcePosition, substringLength);
 
-                    appendReplacement(accumulatedResultBuilder, tempAlloc, matchStr, (int) numberOfCapturesToKeep, (Var*)captures, position);
+                    appendReplacement(accumulatedResultBuilder, tempAlloc, matchStr, (int) numberOfCapturesToKeep, captures, position);
 
                     nextSourcePosition = JavascriptRegExp::AddIndex(position, matchStr->GetLength());
                 }

+ 6 - 4
lib/Runtime/Library/ScriptFunction.cpp

@@ -490,7 +490,7 @@ namespace Js
             BufferStringBuilder builder(cch, scriptContext);
             utf8::DecodeOptions options = pFuncBody->GetUtf8SourceInfo()->IsCesu8() ? utf8::doAllowThreeByteSurrogates : utf8::doDefault;
             size_t decodedCount = utf8::DecodeUnitsInto(builder.DangerousGetWritableBuffer(), pbStart, pbStart + cbLength, options);
-            
+
             if (decodedCount != cch)
             {
                 AssertMsg(false, "Decoded incorrect number of characters for function body");
@@ -574,7 +574,7 @@ namespace Js
             }
             case Js::ScopeType::ScopeType_SlotArray:
             {
-                Js::ScopeSlots slotArray = (Js::Var*)scope;
+                Js::ScopeSlots slotArray = (Field(Js::Var)*)scope;
                 uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
 
                 //get the function body associated with the scope
@@ -707,7 +707,8 @@ namespace Js
     JavascriptArrayBuffer* AsmJsScriptFunction::GetAsmJsArrayBuffer() const
     {
 #ifdef ASMJS_PLAT
-        return *(JavascriptArrayBuffer**)(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset);
+        return (JavascriptArrayBuffer*)PointerValue(
+            *(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset));
 #else
         Assert(UNREACHED);
         return nullptr;
@@ -742,7 +743,8 @@ namespace Js
 
     WebAssemblyMemory* WasmScriptFunction::GetWebAssemblyMemory() const
     {
-        return *(WebAssemblyMemory**)(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset);
+        return (WebAssemblyMemory*)PointerValue(
+            *(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset));
     }
 #endif
 

+ 5 - 0
lib/Runtime/Library/SparseArraySegment.h

@@ -104,6 +104,11 @@ namespace Js
             return static_cast<SparseArraySegment*>(seg);
         }
 
+        static inline Field(SparseArraySegment*)* AddressFrom(Field(SparseArraySegmentBase*) *addr)
+        {
+            return reinterpret_cast<Field(SparseArraySegment*)*>(addr);
+        }
+
     private:
         template<bool isLeaf>
         static SparseArraySegment<T>* Allocate(Recycler* recycler, uint32 left, uint32 length, uint32 size, uint32 fillStart = 0);

+ 17 - 15
lib/Runtime/Library/StackScriptFunction.cpp

@@ -229,12 +229,12 @@ namespace Js
                     }
                     if (callerFunctionBody->DoStackScopeSlots() && interpreterFrame->IsClosureInitDone())
                     {
-                        Var* stackScopeSlots = (Var*)interpreterFrame->GetLocalClosure();
+                        Field(Var)* stackScopeSlots = (Field(Var)*)interpreterFrame->GetLocalClosure();
                         if (stackScopeSlots)
                         {
                             // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
-                            Var* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
-                            interpreterFrame->SetLocalClosure((Var)boxedScopeSlots);
+                            Field(Var)* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
+                            interpreterFrame->SetLocalClosure(boxedScopeSlots);
                         }
                     }
 
@@ -304,7 +304,7 @@ namespace Js
                         }
                         if (callerFunctionBody->DoStackScopeSlots())
                         {
-                            Var* stackScopeSlots = this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
+                            Field(Var)* stackScopeSlots = (Field(Var)*)this->GetScopeSlotsFromNativeFrame(walker, callerFunctionBody);
                             if (stackScopeSlots)
                             {
                                 // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
@@ -356,7 +356,7 @@ namespace Js
                     int i;
                     for (i = 0; i < frameDisplay->GetLength(); i++)
                     {
-                        Var *slotArray = (Var*)frameDisplay->GetItem(i);
+                        Field(Var) *slotArray = (Field(Var)*)frameDisplay->GetItem(i);
 
                         if (ScopeSlots::Is(slotArray))
                         {
@@ -373,10 +373,10 @@ namespace Js
                     }
                     for (; i < frameDisplay->GetLength(); i++)
                     {
-                        Var *pScope = (Var*)frameDisplay->GetItem(i);
+                        Field(Var) *pScope = (Field(Var)*)frameDisplay->GetItem(i);
                         if (ScopeSlots::Is(pScope))
                         {
-                            Var *boxedSlots = this->BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
+                            Field(Var) *boxedSlots = this->BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
                             frameDisplay->SetItem(i, boxedSlots);
                         }
                     }
@@ -652,7 +652,7 @@ namespace Js
         for (uint16 i = 0; i < length; i++)
         {
             // TODO: Once we allocate the slots on the stack, we can only look those slots
-            Var * pScope = (Var *)frameDisplay->GetItem(i);
+            Field(Var) * pScope = (Field(Var) *)frameDisplay->GetItem(i);
             // We don't do stack slots if we exceed max encoded slot count
             if (ScopeSlots::Is(pScope))
             {
@@ -664,19 +664,21 @@ namespace Js
         return boxedFrameDisplay;
     }
 
-    Var * StackScriptFunction::BoxState::BoxScopeSlots(Var * slotArray, uint count)
+    Field(Var) * StackScriptFunction::BoxState::BoxScopeSlots(Field(Var) * slotArray, uint count)
     {
         Assert(slotArray != nullptr);
         Assert(count != 0);
-        Field(Var) * boxedSlotArray = nullptr;
-        if (boxedValues.TryGetValue(slotArray, (void **)&boxedSlotArray))
+
+        void * tmp = nullptr;
+        if (boxedValues.TryGetValue(slotArray, &tmp))
         {
-            return (Var*)boxedSlotArray;
+            return (Field(Var) *)tmp;
         }
 
+        Field(Var) * boxedSlotArray = nullptr;
         if (!ThreadContext::IsOnStack(slotArray))
         {
-            boxedSlotArray = (Field(Var)*)slotArray;
+            boxedSlotArray = slotArray;
         }
         else
         {
@@ -686,7 +688,7 @@ namespace Js
         boxedValues.Add(slotArray, boxedSlotArray);
 
         ScopeSlots scopeSlots(slotArray);
-        ScopeSlots boxedScopeSlots((Js::Var*)boxedSlotArray);
+        ScopeSlots boxedScopeSlots(boxedSlotArray);
 
         boxedScopeSlots.SetCount(count);
         boxedScopeSlots.SetScopeMetadata(scopeSlots.GetScopeMetadataRaw());
@@ -702,7 +704,7 @@ namespace Js
             }
             boxedScopeSlots.Set(i, slotValue);
         }
-        return (Var*)boxedSlotArray;
+        return boxedSlotArray;
     }
 
     ScriptFunction * StackScriptFunction::BoxState::BoxStackFunction(ScriptFunction * scriptFunction)

+ 1 - 1
lib/Runtime/Library/StackScriptFunction.h

@@ -56,7 +56,7 @@ namespace Js
             ScriptContext * scriptContext;
             void * returnAddress;
 
-            Var * BoxScopeSlots(Var * scopeSlots, uint count);
+            Field(Var) * BoxScopeSlots(Field(Var) * scopeSlots, uint count);
             bool NeedBoxFrame(FunctionBody * functionBody);
             bool NeedBoxScriptFunction(ScriptFunction * scriptFunction);
             ScriptFunction * BoxStackFunction(ScriptFunction * scriptFunction);

+ 1 - 1
lib/Runtime/Library/WebAssemblyEnvironment.cpp

@@ -82,7 +82,7 @@ void WebAssemblyEnvironment::SetVarElement(Field(Var)* ptr, T* val, uint32 index
 
     Field(Var)* dst = ptr + index;
     CheckPtrIsValid<Var>((intptr_t)dst);
-    AssertMsg(*(T**)dst == nullptr, "We shouldn't overwrite anything on the environment once it is set");
+    AssertMsg(*dst == nullptr, "We shouldn't overwrite anything on the environment once it is set");
     *dst = val;
 }
 

+ 1 - 1
lib/Runtime/Library/WebAssemblyEnvironment.h

@@ -20,7 +20,7 @@ namespace Js
     public:
         WebAssemblyEnvironment(WebAssemblyModule* module);
 
-        Var* GetStartPtr() const { return (Var*)PointerValue(start); }
+        Field(Var)* GetStartPtr() const { return start; }
 
         WasmScriptFunction* GetWasmFunction(uint32 index) const;
         void SetWasmFunction(uint32 index, WasmScriptFunction* func);

+ 2 - 2
lib/Runtime/Library/WebAssemblyInstance.cpp

@@ -183,7 +183,7 @@ void WebAssemblyInstance::CreateWasmFunctions(WebAssemblyModule * wasmModule, Sc
         Wasm::WasmFunctionInfo* wasmFuncInfo = wasmModule->GetWasmFunctionInfo(i);
         FunctionBody* body = wasmFuncInfo->GetBody();
         WasmScriptFunction* funcObj = ctx->GetLibrary()->CreateWasmScriptFunction(body);
-        funcObj->SetModuleEnvironment((Field(Var)*)env->GetStartPtr());
+        funcObj->SetModuleEnvironment(env->GetStartPtr());
         funcObj->SetSignature(body->GetAsmJsFunctionInfo()->GetWasmSignature());
         funcObj->SetEnvironment(frameDisplay);
 
@@ -440,7 +440,7 @@ void WebAssemblyInstance::InitialGlobals(WebAssemblyModule * wasmModule, ScriptC
             {
                 JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidGlobalRef);
             }
-            
+
             if (sourceGlobal->GetType() != global->GetType())
             {
                 JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidTypeConversion);

+ 4 - 3
lib/Runtime/Library/WebAssemblyTable.cpp

@@ -11,9 +11,10 @@
 namespace Js
 {
 
-WebAssemblyTable::WebAssemblyTable(Var * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type) :
+WebAssemblyTable::WebAssemblyTable(
+        Field(Var) * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type) :
     DynamicObject(type),
-    m_values((Field(Var)*)values),
+    m_values(values),
     m_currentLength(currentLength),
     m_initialLength(initialLength),
     m_maxLength(maxLength)
@@ -229,7 +230,7 @@ WebAssemblyTable::Create(uint32 initial, uint32 maximum, ScriptContext * scriptC
     {
         values = RecyclerNewArrayZ(scriptContext->GetRecycler(), Field(Var), initial);
     }
-    return RecyclerNew(scriptContext->GetRecycler(), WebAssemblyTable, (Var*)values, initial, initial, maximum, scriptContext->GetLibrary()->GetWebAssemblyTableType());
+    return RecyclerNew(scriptContext->GetRecycler(), WebAssemblyTable, values, initial, initial, maximum, scriptContext->GetLibrary()->GetWebAssemblyTableType());
 }
 
 void

+ 2 - 1
lib/Runtime/Library/WebAssemblyTable.h

@@ -23,7 +23,8 @@ namespace Js
             static FunctionInfo Get;
             static FunctionInfo Set;
         };
-        WebAssemblyTable(Var * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type);
+        WebAssemblyTable(
+            Field(Var) * values, uint32 currentLength, uint32 initialLength, uint32 maxLength, DynamicType * type);
         static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryGetterLength(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryGrow(RecyclableObject* function, CallInfo callInfo, ...);

+ 1 - 1
lib/Runtime/Types/DynamicObjectPropertyEnumerator.cpp

@@ -117,7 +117,7 @@ namespace Js
         data->cachedCount = 0;
         data->propertyCount = propertyCount;
         data->strings = reinterpret_cast<Field(PropertyString*)*>(data + 1);
-        data->indexes = (BigPropertyIndex *)(data->strings + propertyCount);
+        data->indexes = unsafe_write_barrier_cast<BigPropertyIndex *>(data->strings + propertyCount);
         data->attributes = (PropertyAttributes*)(data->indexes + propertyCount);
         data->completed = false;
         data->enumNonEnumerable = GetEnumNonEnumerable();

+ 89 - 23
tools/RecyclerChecker/RecyclerChecker.cpp

@@ -7,20 +7,47 @@
 MainVisitor::MainVisitor(
         CompilerInstance& compilerInstance, ASTContext& context, bool fix)
     : _compilerInstance(compilerInstance), _context(context),
-     _fix(fix), _fixed(false), _barrierTypeDefined(false)
+     _fix(fix), _fixed(false), _diagEngine(context.getDiagnostics()),
+     _barrierTypeDefined(false)
 {
     if (_fix)
     {
         _rewriter.setSourceMgr(compilerInstance.getSourceManager(),
                                compilerInstance.getLangOpts());
     }
+
+#define SWB_WIKI \
+    "https://github.com/microsoft/ChakraCore/wiki/Software-Write-Barrier#coding-rules"
+
+    _diagUnbarrieredField = _diagEngine.getCustomDiagID(
+        DiagnosticsEngine::Error,
+        "Unbarriered field, see " SWB_WIKI);
+   _diagIllegalBarrierCast = _diagEngine.getCustomDiagID(
+        DiagnosticsEngine::Error,
+        "Illegal casting away of write barrier, see " SWB_WIKI);
+#undef SWB_WIKI
+}
+
+void MainVisitor::ReportUnbarriedField(SourceLocation location)
+{
+    DiagReport(location, _diagUnbarrieredField);
+}
+
+void MainVisitor::ReportIllegalBarrierCast(SourceLocation location)
+{
+    DiagReport(location, _diagIllegalBarrierCast);
+}
+
+void MainVisitor::DiagReport(SourceLocation location, unsigned diagId)
+{
+    _diagEngine.Report(location, diagId);
 }
 
 bool MainVisitor::VisitCXXRecordDecl(CXXRecordDecl* recordDecl)
 {
-    if (Log::GetLevel() < Log::LogLevel::Info)
+    if (Log::GetLevel() < Log::LogLevel::Verbose)
     {
-        return true; // At least Info level, otherwise this not needed
+        return true; // At least Verbose level, otherwise this not needed
     }
 
     std::string typeName = recordDecl->getQualifiedNameAsString();
@@ -99,11 +126,6 @@ void MainVisitor::ProcessUnbarrieredFields(
     }
 
     const auto& sourceMgr = _compilerInstance.getSourceManager();
-    DiagnosticsEngine& diagEngine = _context.getDiagnostics();
-    const unsigned diagID = diagEngine.getCustomDiagID(
-        DiagnosticsEngine::Error,
-        "Unbarriered field, see "
-        "https://github.com/microsoft/ChakraCore/wiki/Software-Write-Barrier#coding-rules");
 
     for (auto field : recordDecl->fields())
     {
@@ -141,7 +163,7 @@ void MainVisitor::ProcessUnbarrieredFields(
             {
                 if (pushFieldType(originalType))
                 {
-                    Log::outs() << "Queue field type: " << originalTypeName
+                    Log::verbose() << "Queue field type: " << originalTypeName
                         << " (" << typeName << "::" << fieldName << ")\n";
                 }
             }
@@ -168,7 +190,7 @@ void MainVisitor::ProcessUnbarrieredFields(
                             << fieldName << "\n";
             }
 
-            diagEngine.Report(location, diagID);
+            ReportUnbarriedField(location);
         }
     }
 }
@@ -351,14 +373,14 @@ void MainVisitor::RecordRecyclerAllocation(const string& allocationFunction, con
 template <class Set, class DumpItemFunc>
 void MainVisitor::dump(const char* name, const Set& set, const DumpItemFunc& func)
 {
-    Log::outs() << "-------------------------\n\n";
-    Log::outs() << name << "\n";
-    Log::outs() << "-------------------------\n\n";
+    Log::verbose() << "-------------------------\n\n";
+    Log::verbose() << name << "\n";
+    Log::verbose() << "-------------------------\n\n";
     for (auto item : set)
     {
-        func(Log::outs(), item);
+        func(Log::verbose(), item);
     }
-    Log::outs() << "-------------------------\n\n";
+    Log::verbose() << "-------------------------\n\n";
 }
 
 template <class Item>
@@ -384,7 +406,7 @@ void MainVisitor::Inspect()
     Dump(pointerClasses);
     Dump(barrieredClasses);
 
-    Log::outs() << "Recycler allocations\n";
+    Log::verbose() << "Recycler allocations\n";
     for (auto item : _allocatorTypeMap)
     {
         dump(item.first.c_str(), item.second);
@@ -428,7 +450,7 @@ void MainVisitor::Inspect()
             {
                 if (pushBarrierType(base.getType().getTypePtr()))
                 {
-                    Log::outs() << "Queue base type: " << base.getType().getAsString()
+                    Log::verbose() << "Queue base type: " << base.getType().getAsString()
                         << " (base of " << typeName << ")\n";
                 }
             }
@@ -521,8 +543,8 @@ void CheckAllocationsInFunctionVisitor::VisitAllocate(
 
         if (allocationType & AllocationTypes::WriteBarrier)
         {
-            Log::outs() << "In \"" << _functionDecl->getQualifiedNameAsString() << "\"\n";
-            Log::outs() << "  Allocating \"" << allocatedTypeStr << "\" in write barriered memory\n";
+            Log::verbose() << "In \"" << _functionDecl->getQualifiedNameAsString() << "\"\n";
+            Log::verbose() << "  Allocating \"" << allocatedTypeStr << "\" in write barriered memory\n";
         }
 
         _mainVisitor->RecordAllocation(allocatedType, allocationType);
@@ -565,6 +587,45 @@ bool CheckAllocationsInFunctionVisitor::VisitCallExpr(CallExpr* callExpr)
     return true;
 }
 
+// Check if type is a "Field() *" pointer type, or alternatively a pointer to
+// any type in "alt" if provided.
+bool CheckAllocationsInFunctionVisitor::IsFieldPointer(
+    const QualType& qtype, const char* alt)
+{
+    if (qtype->isPointerType())
+    {
+        auto name = qtype->getPointeeType()
+            .getDesugaredType(_mainVisitor->getContext()).getAsString();
+        return StartsWith(name, "class Memory::WriteBarrierPtr<")
+            || StartsWith(name, "typename WriteBarrierFieldTypeTraits<")
+            || (alt && strstr(alt, name.c_str()));
+    }
+
+    return false;
+}
+
+bool CheckAllocationsInFunctionVisitor::CommonVisitCastExpr(CastExpr *cast)
+{
+    if (IsFieldPointer(cast->getSubExpr()->getType()) &&  // from Field() *
+        cast->getType()->isPointerType() &&     // to a pointer type
+        !IsFieldPointer(cast->getType(),        // not another Field() *
+            "int|float|double|unsigned char"))  // not int/float/double/byte *
+    {
+        _mainVisitor->ReportIllegalBarrierCast(cast->getLocStart());
+
+        if (Log::GetLevel() >= Log::LogLevel::Info)
+        {
+            cast->dumpColor();
+            cast->getSubExpr()->getType()->getPointeeType()
+                .getDesugaredType(_mainVisitor->getContext()).dump("CAST_FROM");
+            cast->getType()->getPointeeType()
+                .getDesugaredType(_mainVisitor->getContext()).dump("CAST_TO");
+        }
+    }
+
+    return true;
+}
+
 void RecyclerCheckerConsumer::HandleTranslationUnit(ASTContext& context)
 {
     MainVisitor mainVisitor(_compilerInstance, context, _fix);
@@ -585,13 +646,17 @@ bool RecyclerCheckerAction::ParseArgs(
 {
     for (auto i = args.begin(); i != args.end(); i++)
     {
-        if (*i == "-verbose")
+        if (*i == "-fix")
         {
-            Log::SetLevel(Log::LogLevel::Verbose);
+            this->_fix = true;
         }
-        else if (*i == "-fix")
+        else if (*i == "-info")
         {
-            this->_fix = true;
+            Log::SetLevel(Log::LogLevel::Info);
+        }
+        else if (*i == "-verbose")
+        {
+            Log::SetLevel(Log::LogLevel::Verbose);
         }
         else
         {
@@ -599,6 +664,7 @@ bool RecyclerCheckerAction::ParseArgs(
                 << "ERROR: Unrecognized check-recycler option: " << *i << "\n"
                 << "Supported options:\n"
                 << "  -fix          Fix missing write barrier annotations"
+                << "  -info         Log info messages\n"
                 << "  -verbose      Log verbose messages\n";
             return false;
         }

+ 25 - 0
tools/RecyclerChecker/RecyclerChecker.h

@@ -40,6 +40,11 @@ private:
     bool _fix;      // whether user requested to fix missing annotations
     bool _fixed;    // whether this plugin committed any annotation fixes
 
+    // For emitting checker errors
+    DiagnosticsEngine& _diagEngine;
+    unsigned _diagUnbarrieredField;
+    unsigned _diagIllegalBarrierCast;
+
     bool _barrierTypeDefined;
     map<string, set<string>> _allocatorTypeMap;
     set<string> _pointerClasses;
@@ -51,6 +56,7 @@ public:
     MainVisitor(CompilerInstance& compilerInstance, ASTContext& context, bool fix);
 
     const ASTContext& getContext() const { return _context; }
+    const CompilerInstance& getCompilerInstance() const { return _compilerInstance; }
 
     bool VisitCXXRecordDecl(CXXRecordDecl* recordDecl);
     bool VisitFunctionDecl(FunctionDecl* functionDecl);
@@ -61,6 +67,9 @@ public:
     void Inspect();
     bool ApplyFix();
 
+    void ReportUnbarriedField(SourceLocation location);
+    void ReportIllegalBarrierCast(SourceLocation location);
+
 private:
     template <class Set, class DumpItemFunc>
     void dump(const char* name, const Set& set, const DumpItemFunc& func);
@@ -73,6 +82,8 @@ private:
 
     bool MatchType(const string& type, const char* source, const char** pSourceEnd);
     const char* GetFieldTypeAnnotation(QualType qtype);
+
+    void DiagReport(SourceLocation location, unsigned diagId);
 };
 
 class CheckAllocationsInFunctionVisitor:
@@ -87,12 +98,26 @@ public:
     bool VisitCXXNewExpr(CXXNewExpr* newExpression);
     bool VisitCallExpr(CallExpr* callExpr);
 
+#define IMPLEMENT_VISIT_CAST(Expr) \
+    bool Visit##Expr(Expr *cast) { return CommonVisitCastExpr(cast); }
+
+    IMPLEMENT_VISIT_CAST(CStyleCastExpr)
+    IMPLEMENT_VISIT_CAST(CXXFunctionalCastExpr)
+    IMPLEMENT_VISIT_CAST(CXXConstCastExpr)
+    IMPLEMENT_VISIT_CAST(CXXDynamicCastExpr)
+    IMPLEMENT_VISIT_CAST(CXXReinterpretCastExpr)
+    IMPLEMENT_VISIT_CAST(CXXStaticCastExpr)
+#undef IMPLEMENT_VISIT_CAST
+
 private:
     MainVisitor* _mainVisitor;
     FunctionDecl* _functionDecl;
 
     template <class A0, class A1, class T>
     void VisitAllocate(const A0& getArg0, const A1& getArg1, const T& getAllocType);
+
+    bool IsFieldPointer(const QualType& qtype, const char* alt = nullptr);
+    bool CommonVisitCastExpr(CastExpr *cast);
 };
 
 class RecyclerCheckerConsumer: public ASTConsumer