}
}
- public Action<ErrorArgument[], VerifierError> ReportVerificationError
- {
- set;
- private get;
- }
+ public Action<ErrorArgument[], VerifierError> ReportVerificationError { set; private get; }
+
+ public bool SanityChecks { set; private get; }
public void Verify()
{
if (_currentBasicBlock != null)
_currentBasicBlock.IncrementErrorCount();
- var args = new ErrorArgument[]
- {
- new ErrorArgument("Offset", _currentInstructionOffset)
+ var args = new ErrorArgument[]
+ {
+ new ErrorArgument("Offset", _currentInstructionOffset)
};
ReportVerificationError(args, error);
}
void CheckIsNumeric(StackValue value)
{
- if (!Check(StackValueKind.Int32 <= value.Kind && value.Kind <= StackValueKind.Float,
+ if (!Check(StackValueKind.Int32 <= value.Kind && value.Kind <= StackValueKind.Float,
VerifierError.ExpectedNumericType, value))
{
AbortBasicBlockVerification();
void CheckIsArray(StackValue value)
{
- Check((value.Kind == StackValueKind.ObjRef) && ((value.Type == null) || value.Type.IsSzArray),
+ Check((value.Kind == StackValueKind.ObjRef) && ((value.Type == null) || value.Type.IsSzArray),
VerifierError.ExpectedArray /* , value */);
}
- void CheckIsAssignable(StackValue src, StackValue dst)
+ void CheckIsAssignable(StackValue src, StackValue dst, VerifierError error = VerifierError.StackUnexpected)
{
if (!IsAssignable(src, dst))
- VerificationError(VerifierError.StackUnexpected, src, dst);
+ VerificationError(error, src, dst);
}
private void CheckIsValidLeaveTarget(BasicBlock src, BasicBlock target)
ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion;
// Target is not enclosing source
- if (targetRegion.TryOffset > srcRegion.TryOffset ||
+ if (targetRegion.TryOffset > srcRegion.TryOffset ||
src.StartOffset >= targetRegion.TryOffset + targetRegion.TryLength)
{
// Target is not first instruction
ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion;
// If target is not associated try block, and not enclosing srcRegion
- if (target.TryIndex != src.HandlerIndex &&
- (targetRegion.TryOffset > srcRegion.HandlerOffset ||
+ if (target.TryIndex != src.HandlerIndex &&
+ (targetRegion.TryOffset > srcRegion.HandlerOffset ||
targetRegion.TryOffset + targetRegion.TryLength < srcRegion.HandlerOffset))
{
// If target is not first instruction of try, or not a direct sibling
ref var srcRegion = ref _exceptionRegions[src.TryIndex.Value].ILRegion;
ref var targetRegion = ref _exceptionRegions[target.TryIndex.Value].ILRegion;
// If target is inside source region
- if (srcRegion.TryOffset <= targetRegion.TryOffset &&
+ if (srcRegion.TryOffset <= targetRegion.TryOffset &&
target.StartOffset < srcRegion.TryOffset + srcRegion.TryLength)
{
// Only branching to first instruction of try-block is valid
}
/// <summary>
- /// Checks whether the given enclosed try block is a direct child try-region of
+ /// Checks whether the given enclosed try block is a direct child try-region of
/// the given enclosing try block.
/// </summary>
/// <param name="enclosingBlock">The block enclosing the try block given by <paramref name="enclosedBlock"/>.</param>
case TypeFlags.Char: return "Char";
case TypeFlags.SByte:
case TypeFlags.Byte: return "Byte";
- case TypeFlags.Int16:
+ case TypeFlags.Int16:
case TypeFlags.UInt16: return "Short";
case TypeFlags.Int32:
case TypeFlags.UInt32: return "Int32";
- case TypeFlags.Int64:
+ case TypeFlags.Int64:
case TypeFlags.UInt64: return "Long";
case TypeFlags.Single: return "Single";
case TypeFlags.Double: return "Double";
var exceptionType = ResolveTypeToken(r.ILRegion.ClassToken);
Check(!exceptionType.IsByRef, VerifierError.CatchByRef);
basicBlock.EntryStack[0] = StackValue.CreateObjRef(exceptionType);
+
+ if (SanityChecks && basicBlock.EntryStack[0] != StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)))
+ {
+ CheckIsAssignable(basicBlock.EntryStack[0], StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Exception)),
+ VerifierError.ThrowOrCatchOnlyExceptionType);
+ }
}
}
else
{
// Rules for non-virtual call to a non-final virtual method (ECMA III.3.19: Verifiability of 'call'):
- // Define:
+ // Define:
// The "this" pointer is considered to be "possibly written" if
// 1. Its address have been taken (LDARGA 0) anywhere in the method.
// (or)
// 2. It has been stored to (STARG.0) anywhere in the method.
// A non-virtual call to a non-final virtual method is only allowed if
- // 1. The this pointer passed to the callee is an instance of a boxed value type.
+ // 1. The this pointer passed to the callee is an instance of a boxed value type.
// (or)
// 2. The this pointer passed to the callee is the current method's this pointer.
// (and) The current method's this pointer is not "possibly written".
- // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to
- // virtual methods. (Luckily this does not affect .ctors, since they are not virtual).
- // This is stronger than is strictly needed, but implementing a laxer rule is significantly
+ // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to
+ // virtual methods. (Luckily this does not affect .ctors, since they are not virtual).
+ // This is stronger than is strictly needed, but implementing a laxer rule is significantly
// harder and more error prone.
if (method.IsVirtual && !method.IsFinal && !actualThis.IsBoxedValueType)
{
Check(!IsByRefLike(declaredThis), VerifierError.TailByRef, declaredThis);
// Tail calls on constrained calls should be illegal too:
- // when instantiated at a value type, a constrained call may pass the address of a stack allocated value
+ // when instantiated at a value type, a constrained call may pass the address of a stack allocated value
Check(constrained == null, VerifierError.TailByRef);
}
}
{
if (next.IsThisInitialized && !_isThisInitialized)
{
- // Next block has 'this' initialized, but current state has not
+ // Next block has 'this' initialized, but current state has not
// therefore next block must be reverified with 'this' uninitialized
if (next.State == BasicBlock.ImportState.WasVerified && next.ErrorCount == 0)
next.State = BasicBlock.ImportState.Unmarked;
CheckIsObjRef(value);
+ if (SanityChecks && value != StackValue.CreateObjRef(GetWellKnownType(WellKnownType.Object)))
+ {
+ CheckIsAssignable(value, StackValue.CreateFromType(GetWellKnownType(WellKnownType.Exception)),
+ VerifierError.ThrowOrCatchOnlyExceptionType);
+ }
+
EmptyTheStack();
}
Check(!IsByRefLike(targetType), VerifierError.BoxByRef, targetType);
- Check(type.IsPrimitive || targetType.Kind == StackValueKind.ObjRef ||
+ Check(type.IsPrimitive || targetType.Kind == StackValueKind.ObjRef ||
type.IsGenericParameter || type.IsValueType, VerifierError.ExpectedValClassObjRefVariable);
Check(type.CheckConstraints(), VerifierError.UnsatisfiedBoxOperand);
public string[] InputFilePath { get; set; }
public string[] Reference { get; set; }
public string SystemModule { get; set; }
+ public bool SanityChecks { get; set; }
public string[] Include { get; set; }
public FileInfo IncludeFile { get; set; }
public string[] Exclude { get; set; }
command.AddArgument(new Argument<string[]>("input-file-path", "Input file(s)") { Arity = new ArgumentArity(1, Int32.MaxValue) });
command.AddOption(new Option<string[]>(new[] { "--reference", "-r" }, "Reference metadata from the specified assembly"));
command.AddOption(new Option<string>(new[] { "--system-module", "-s" }, "System module name (default: mscorlib)"));
+ command.AddOption(new Option<bool>(new[] { "--sanity-checks", "-c" }, "Check for valid constructs that are likely mistakes"));
command.AddOption(new Option<string[]>(new[] { "--include", "-i" }, "Use only methods/types/namespaces, which match the given regular expression(s)"));
command.AddOption(new Option<FileInfo>(new[] { "--include-file" }, "Same as --include, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly());
command.AddOption(new Option<string[]>(new[] { "--exclude", "-e" }, "Skip methods/types/namespaces, which match the given regular expression(s)"));
private int Run()
{
- _verifier = new Verifier(this, GetVerifierOptions());
+ _verifier = new Verifier(this, new VerifierOptions
+ {
+ IncludeMetadataTokensInErrorMessages = _options.Tokens,
+ SanityChecks = _options.SanityChecks
+ });
_verifier.SetSystemModuleName(new AssemblyName(_options.SystemModule ?? "mscorlib"));
int numErrors = 0;
}
}
- private VerifierOptions GetVerifierOptions()
- {
- return new VerifierOptions { IncludeMetadataTokensInErrorMessages = _options.Tokens };
- }
-
private void PrintVerifyMethodsResult(VerificationResult result, EcmaModule module, string pathOrModuleName)
{
Write("[IL]: Error [");
}
}
}
- catch
+ catch
{
Write("Error while getting method signature");
}
/// <summary>
/// This method returns the fully qualified method name by concatenating assembly, type and method name.
- /// This method exists to avoid additional assembly resolving, which might be triggered by calling
+ /// This method exists to avoid additional assembly resolving, which might be triggered by calling
/// MethodDesc.ToString().
/// </summary>
private string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)