private int VerifyObject()

in src/Microsoft.Diagnostics.Runtime/ClrHeap.cs [1258:1417]


        private int VerifyObject(SyncBlockContainer syncBlocks, ClrSegment seg, ClrObject obj, Span<ObjectCorruption> result)
        {
            if (result.Length == 0)
                throw new ArgumentException($"{nameof(result)} must have at least one element.");


            // Is the object address pointer aligned?
            if ((obj.Address & ((uint)_memoryReader.PointerSize - 1)) != 0)
            {
                result[0] = new(obj, 0, ObjectCorruptionKind.ObjectNotPointerAligned);
                return 1;
            }

            if (!obj.IsFree)
            {
                // Can we read the method table?
                if (!_memoryReader.Read(obj.Address, out ulong mt))
                {
                    result[0] = new(obj, 0, ObjectCorruptionKind.CouldNotReadMethodTable);
                    return 1;
                }

                // Is the method table we read valid?
                if (!Helpers.IsValidMethodTable(mt))
                {
                    result[0] = new(obj, 0, ObjectCorruptionKind.InvalidMethodTable);
                    return 1;
                }
                else if (obj.Type is null)
                {
                    // This shouldn't happen if VerifyMethodTable above returns success, but we'll make sure.
                    result[0] = new(obj, 0, ObjectCorruptionKind.InvalidMethodTable);
                    return 1;
                }
            }

            // Any previous failures are fatal, we can't keep verifying the object.  From here, though, we'll
            // attempt to report any and all failures we encounter.
            int index = 0;

            // Check object size
            int intSize = obj.Size > int.MaxValue ? int.MaxValue : (int)obj.Size;
            if (obj + obj.Size > seg.ObjectRange.End || (!obj.IsFree && obj.Size > seg.MaxObjectSize))
                if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, _memoryReader.PointerSize, ObjectCorruptionKind.ObjectTooLarge)))
                    return index;

            // If we are inspecting a free object, the rest of this method is not needed.
            if (obj.IsFree)
                return index;

            // Validate members
            bool verifyMembers;
            try
            {
                // Type can't be null, we checked above.  The compiler just get lost in the IsFree checks.
                verifyMembers = obj.Type!.ContainsPointers && ShouldVerifyMembers(seg, obj);

                // If the object is an array and too large, it likely means someone wrote over the size
                // field of our array.  Trying to verify the members of the array will generate a ton
                // of noisy failures, so we'll avoid doing that.
                if (verifyMembers && obj.Type.IsArray)
                {
                    for (int i = 0; i < index; i++)
                    {
                        if (result[i].Kind == ObjectCorruptionKind.ObjectTooLarge)
                        {
                            verifyMembers = false;
                            break;
                        }
                    }
                }
            }
            catch (IOException)
            {
                if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, 0, ObjectCorruptionKind.CouldNotReadCardTable)))
                    return index;

                verifyMembers = false;
            }

            if (verifyMembers)
            {
                GCDesc gcdesc = obj.Type!.GCDesc;
                if (gcdesc.IsEmpty)
                    if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, 0, ObjectCorruptionKind.CouldNotReadGCDesc)))
                        return index;

                ulong freeMt = seg.SubHeap.Heap.FreeType.MethodTable;
                byte[] buffer = ArrayPool<byte>.Shared.Rent(intSize);
                int read = _memoryReader.Read(obj, new Span<byte>(buffer, 0, intSize));
                if (read != intSize)
                    if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, read >= 0 ? read : 0, ObjectCorruptionKind.CouldNotReadObject)))
                        return index;

                foreach ((ulong objRef, int offset) in gcdesc.WalkObject(buffer, intSize))
                {
                    if ((objRef & ((uint)_memoryReader.PointerSize - 1)) != 0)
                    {
                        if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, offset, ObjectCorruptionKind.ObjectReferenceNotPointerAligned)))
                            break;
                    }

                    if (!_memoryReader.Read(objRef, out ulong mt) || !Helpers.IsValidMethodTable(mt))
                    {
                        if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, offset, ObjectCorruptionKind.InvalidObjectReference)))
                            break;
                    }
                    else if ((mt & ~1ul) == freeMt)
                    {
                        if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, offset, ObjectCorruptionKind.FreeObjectReference)))
                            break;
                    }
                }

                ArrayPool<byte>.Shared.Return(buffer);
                if (index >= result.Length)
                    return index;
            }

            // Object header validation tests:
            uint objHeader = _memoryReader.Read<uint>(obj - sizeof(uint));

            // Validate SyncBlock
            SyncBlock? blk = syncBlocks.TryGetSyncBlock(obj);
            if ((objHeader & SyncBlockHashOrSyncBlockIndex) != 0 && (objHeader & SyncBlockHashCodeIndex) == 0)
            {
                uint sblkIndex = objHeader & SyncBlockIndexMask;
                int clrIndex = blk?.Index ?? -1;

                if (sblkIndex == 0)
                {
                    if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, -sizeof(uint), ObjectCorruptionKind.SyncBlockZero, -1, clrIndex)))
                        return index;
                }
                else if (sblkIndex != clrIndex)
                {
                    if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, -sizeof(uint), ObjectCorruptionKind.SyncBlockMismatch, (int)sblkIndex, clrIndex)))
                        return index;
                }
            }
            else if (blk is not null)
            {
                if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, -sizeof(uint), ObjectCorruptionKind.SyncBlockMismatch, -1, blk.Index)))
                    return index;
            }

            // Validate Thinlock
            (ulong Thread, int Recursion) thinlock = Helpers.GetThinLock(objHeader);
            if (thinlock.Thread != 0)
            {
                ClrRuntime runtime = seg.SubHeap.Heap.Runtime;
                if (!runtime.Threads.Any(th => th.Address == thinlock.Thread))
                {
                    if (!AddCorruptionAndContinue(result, ref index, new ObjectCorruption(obj, -4, ObjectCorruptionKind.InvalidThinlock)))
                        return index;
                }
            }

            return index;
        }