Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] cleanup tests in a few ways #109357

Merged
merged 20 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ IRuntimeTypeSystem IContractFactory<IRuntimeTypeSystem>.CreateContract(Target ta
ulong methodDescAlignment = target.ReadGlobal<ulong>(Constants.Globals.MethodDescAlignment);
return version switch
{
1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable, methodDescAlignment),
1 => new RuntimeTypeSystem_1(target, new RuntimeTypeSystemHelpers.TypeValidation(target), freeObjectMethodTable, methodDescAlignment),
_ => default(RuntimeTypeSystem),
};
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,77 +15,6 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
internal static class NonValidated
{

// This doesn't need as many properties as MethodTable because we don't want to be operating on
// a NonValidatedMethodTable for too long
internal struct MethodTable
{
private readonly Target _target;
private readonly Target.TypeInfo _type;
internal TargetPointer Address { get; init; }

private MethodTableFlags? _methodTableFlags;

internal MethodTable(Target target, TargetPointer methodTablePointer)
{
_target = target;
_type = target.GetTypeInfo(DataType.MethodTable);
Address = methodTablePointer;
_methodTableFlags = null;
}

private MethodTableFlags GetOrCreateFlags()
{
if (_methodTableFlags == null)
{
// note: may throw if the method table Address is corrupted
MethodTableFlags flags = new MethodTableFlags
{
MTFlags = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags)].Offset),
MTFlags2 = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags2)].Offset),
BaseSize = _target.Read<uint>(Address + (ulong)_type.Fields[nameof(MethodTableFlags.BaseSize)].Offset),
};
_methodTableFlags = flags;
}
return _methodTableFlags.Value;
}

internal MethodTableFlags Flags => GetOrCreateFlags();

internal TargetPointer EEClassOrCanonMT => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(EEClassOrCanonMT)].Offset);
internal TargetPointer EEClass => GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass");
internal TargetPointer CanonMT
{
get
{
if (GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.CanonMT)
{
return new TargetPointer((ulong)EEClassOrCanonMT & ~(ulong)EEClassOrCanonMTBits.Mask);
}
else
{
throw new InvalidOperationException("not a canonical method table");
}
}
}
}

internal struct EEClass
{
public readonly Target _target;
private readonly Target.TypeInfo _type;

internal TargetPointer Address { get; init; }

internal EEClass(Target target, TargetPointer eeClassPointer)
{
_target = target;
Address = eeClassPointer;
_type = target.GetTypeInfo(DataType.EEClass);
}

internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset);
}

internal struct MethodDesc
{
private readonly Target _target;
Expand All @@ -106,113 +35,6 @@ internal MethodDesc(Target target, Data.MethodDesc desc, Data.MethodDescChunk ch
internal bool HasNonVtableSlot => HasFlag(MethodDescFlags.HasNonVtableSlot);
}

internal static MethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer)
{
return new MethodTable(target, methodTablePointer);
}

internal static EEClass GetEEClassData(Target target, TargetPointer eeClassPointer)
{
return new EEClass(target, eeClassPointer);
}

}

/// <summary>
/// Validates that the given address is a valid MethodTable.
/// </summary>
/// <remarks>
/// If the target process has memory corruption, we may see pointers that are not valid method tables.
/// We validate by looking at the MethodTable -> EEClass -> MethodTable relationship (which may throw if we access invalid memory).
/// And then we do some ad-hoc checks on the method table flags.
private bool ValidateMethodTablePointer(NonValidated.MethodTable umt)
{
try
{
if (!ValidateThrowing(umt))
{
return false;
}
if (!ValidateMethodTableAdHoc(umt))
{
return false;
}
}
catch (System.Exception)
{
// TODO(cdac): maybe don't swallow all exceptions? We could consider a richer contract that
// helps to track down what sort of memory corruption caused the validation to fail.
// TODO(cdac): we could also consider a more fine-grained exception type so we don't mask
// programmer mistakes in cdacreader.
return false;
}
return true;
}

// This portion of validation may throw if we are trying to read an invalid address in the target process
private bool ValidateThrowing(NonValidated.MethodTable methodTable)
{
// For non-generic classes, we can rely on comparing
// object->methodtable->class->methodtable
// to
// object->methodtable
//
// However, for generic instantiation this does not work. There we must
// compare
//
// object->methodtable->class->methodtable->class
// to
// object->methodtable->class
TargetPointer eeClassPtr = GetClassThrowing(methodTable);
if (eeClassPtr != TargetPointer.Null)
{
NonValidated.EEClass eeClass = NonValidated.GetEEClassData(_target, eeClassPtr);
TargetPointer methodTablePtrFromClass = eeClass.MethodTable;
if (methodTable.Address == methodTablePtrFromClass)
{
return true;
}
if (methodTable.Flags.HasInstantiation || methodTable.Flags.IsArray)
{
NonValidated.MethodTable methodTableFromClass = NonValidated.GetMethodTableData(_target, methodTablePtrFromClass);
TargetPointer classFromMethodTable = GetClassThrowing(methodTableFromClass);
return classFromMethodTable == eeClassPtr;
}
}
return false;
}

private bool ValidateMethodTableAdHoc(NonValidated.MethodTable methodTable)
{
// ad-hoc checks; add more here as needed
if (!methodTable.Flags.IsInterface && !methodTable.Flags.IsString)
{
if (methodTable.Flags.BaseSize == 0 || !_target.IsAlignedToPointerSize(methodTable.Flags.BaseSize))
{
return false;
}
}
return true;
}

internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
{
return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask);
}
private TargetPointer GetClassThrowing(NonValidated.MethodTable methodTable)
{
TargetPointer eeClassOrCanonMT = methodTable.EEClassOrCanonMT;

if (GetEEClassOrCanonMTBits(eeClassOrCanonMT) == EEClassOrCanonMTBits.EEClass)
{
return methodTable.EEClass;
}
else
{
TargetPointer canonicalMethodTablePtr = methodTable.CanonMT;
NonValidated.MethodTable umt = NonValidated.GetMethodTableData(_target, canonicalMethodTablePtr);
return umt.EEClass;
}
}

private TargetPointer GetMethodDescChunkPointerThrowing(TargetPointer methodDescPointer, Data.MethodDesc umd)
Expand Down
Loading
Loading