Browse Source

Fix StObj.CheckTargetSlot assertion

pull/2766/head
Daniel Grunwald 3 years ago committed by Siegfried Pammer
parent
commit
9254abcf88
  1. 77
      ICSharpCode.Decompiler/IL/ILReader.cs

77
ICSharpCode.Decompiler/IL/ILReader.cs

@ -859,6 +859,8 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Conv_ovf_u_un: case ILOpCode.Conv_ovf_u_un:
return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned)); return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned));
case ILOpCode.Cpblk: case ILOpCode.Cpblk:
// This preserves the evaluation order because the ILAst will run
// destAddress; sourceAddress; size.
return new Cpblk(size: Pop(StackType.I4), sourceAddress: PopPointer(), destAddress: PopPointer()); return new Cpblk(size: Pop(StackType.I4), sourceAddress: PopPointer(), destAddress: PopPointer());
case ILOpCode.Div: case ILOpCode.Div:
return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Signed); return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Signed);
@ -871,6 +873,8 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Endfinally: case ILOpCode.Endfinally:
return new Leave(null); return new Leave(null);
case ILOpCode.Initblk: case ILOpCode.Initblk:
// This preserves the evaluation order because the ILAst will run
// address; value; size.
return new Initblk(size: Pop(StackType.I4), value: Pop(StackType.I4), address: PopPointer()); return new Initblk(size: Pop(StackType.I4), value: Pop(StackType.I4), address: PopPointer());
case ILOpCode.Jmp: case ILOpCode.Jmp:
return DecodeJmp(); return DecodeJmp();
@ -983,7 +987,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Or: case ILOpCode.Or:
return BinaryNumeric(BinaryNumericOperator.BitOr); return BinaryNumeric(BinaryNumericOperator.BitOr);
case ILOpCode.Pop: case ILOpCode.Pop:
FlushExpressionStack(); FlushExpressionStack(); // discard only the value, not the side-effects
Pop(); Pop();
return new Nop() { Kind = NopKind.Pop }; return new Nop() { Kind = NopKind.Pop };
case ILOpCode.Rem: case ILOpCode.Rem:
@ -1002,21 +1006,22 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Starg_s: case ILOpCode.Starg_s:
return Starg(ILParser.DecodeIndex(ref reader, opCode)); return Starg(ILParser.DecodeIndex(ref reader, opCode));
case ILOpCode.Stind_i1: case ILOpCode.Stind_i1:
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.SByte)); // target will run before value, thus preserving the evaluation order
return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.SByte));
case ILOpCode.Stind_i2: case ILOpCode.Stind_i2:
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int16)); return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int16));
case ILOpCode.Stind_i4: case ILOpCode.Stind_i4:
return new StObj(value: Pop(StackType.I4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int32)); return new StObj(value: Pop(StackType.I4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int32));
case ILOpCode.Stind_i8: case ILOpCode.Stind_i8:
return new StObj(value: Pop(StackType.I8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Int64)); return new StObj(value: Pop(StackType.I8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Int64));
case ILOpCode.Stind_r4: case ILOpCode.Stind_r4:
return new StObj(value: Pop(StackType.F4), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Single)); return new StObj(value: Pop(StackType.F4), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Single));
case ILOpCode.Stind_r8: case ILOpCode.Stind_r8:
return new StObj(value: Pop(StackType.F8), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Double)); return new StObj(value: Pop(StackType.F8), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Double));
case ILOpCode.Stind_i: case ILOpCode.Stind_i:
return new StObj(value: Pop(StackType.I), target: PopPointer(), type: compilation.FindType(KnownTypeCode.IntPtr)); return new StObj(value: Pop(StackType.I), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.IntPtr));
case ILOpCode.Stind_ref: case ILOpCode.Stind_ref:
return new StObj(value: Pop(StackType.O), target: PopPointer(), type: compilation.FindType(KnownTypeCode.Object)); return new StObj(value: Pop(StackType.O), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Stloc: case ILOpCode.Stloc:
case ILOpCode.Stloc_s: case ILOpCode.Stloc_s:
return Stloc(ILParser.DecodeIndex(ref reader, opCode)); return Stloc(ILParser.DecodeIndex(ref reader, opCode));
@ -1048,11 +1053,12 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Cpobj: case ILOpCode.Cpobj:
{ {
var type = ReadAndDecodeTypeReference(); var type = ReadAndDecodeTypeReference();
// OK, 'target' runs before 'value: ld'
var ld = new LdObj(PopPointer(), type); var ld = new LdObj(PopPointer(), type);
return new StObj(PopPointer(), ld, type); return new StObj(PopPointer(), ld, type);
} }
case ILOpCode.Initobj: case ILOpCode.Initobj:
return InitObj(PopPointer(), ReadAndDecodeTypeReference()); return InitObj(PopStObjTarget(), ReadAndDecodeTypeReference());
case ILOpCode.Isinst: case ILOpCode.Isinst:
return Push(new IsInst(Pop(StackType.O), ReadAndDecodeTypeReference())); return Push(new IsInst(Pop(StackType.O), ReadAndDecodeTypeReference()));
case ILOpCode.Ldelem: case ILOpCode.Ldelem:
@ -1080,6 +1086,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Ldelem_ref: case ILOpCode.Ldelem_ref:
return LdElem(compilation.FindType(KnownTypeCode.Object)); return LdElem(compilation.FindType(KnownTypeCode.Object));
case ILOpCode.Ldelema: case ILOpCode.Ldelema:
// LdElema will evalute the array before the indices, so we're preserving the evaluation order
return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference())); return Push(new LdElema(indices: Pop(), array: Pop(), type: ReadAndDecodeTypeReference()));
case ILOpCode.Ldfld: case ILOpCode.Ldfld:
{ {
@ -1149,7 +1156,8 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Stobj: case ILOpCode.Stobj:
{ {
var type = ReadAndDecodeTypeReference(); var type = ReadAndDecodeTypeReference();
return new StObj(value: Pop(type.GetStackType()), target: PopPointer(), type: type); // OK, target runs before value
return new StObj(value: Pop(type.GetStackType()), target: PopStObjTarget(), type: type);
} }
case ILOpCode.Throw: case ILOpCode.Throw:
return new Throw(Pop()); return new Throw(Pop());
@ -1240,6 +1248,24 @@ namespace ICSharpCode.Decompiler.IL
return new LdLoc(currentStack.Peek()); return new LdLoc(currentStack.Peek());
} }
/// <summary>
/// Pops a value/instruction from the evaluation stack.
/// Note that instructions popped from the stack must be evaluated in the order they
/// were pushed (so in reverse order of the pop calls!).
///
/// For instructions like 'conv' that pop a single element and then push their result,
/// it's fine to pop just one element as the instruction itself will end up on the stack,
/// thus maintaining the evaluation order.
/// For instructions like 'call' that pop multiple arguments, it's critical that
/// the evaluation order of the resulting ILAst will be reverse from the order of the push
/// calls.
/// For instructions like 'brtrue', it's fine to pop only a part of the stack because
/// ReadInstructions() will flush the evaluation stack before outputting the brtrue instruction.
///
/// Use FlushExpressionStack() to ensure that following Pop() calls do not return
/// instructions that involve side-effects. This way evaluation order is preserved
/// no matter which order the ILAst will execute the popped instructions in.
/// </summary>
ILInstruction Pop() ILInstruction Pop()
{ {
if (expressionStack.Count > 0) if (expressionStack.Count > 0)
@ -1362,6 +1388,17 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
ILInstruction PopStObjTarget()
{
// stobj has a special invariant (StObj.CheckTargetSlot)
// that prohibts inlining LdElema/LdFlda.
if (expressionStack.LastOrDefault() is LdElema or LdFlda)
{
FlushExpressionStack();
}
return PopPointer();
}
ILInstruction PopFieldTarget(IField field) ILInstruction PopFieldTarget(IField field)
{ {
switch (field.DeclaringType.IsReferenceType) switch (field.DeclaringType.IsReferenceType)
@ -1505,6 +1542,7 @@ namespace ICSharpCode.Decompiler.IL
var value = Pop(type.GetStackType()); var value = Pop(type.GetStackType());
var index = Pop(); var index = Pop();
var array = Pop(); var array = Pop();
// OK, evaluation order is array, index, value
return new StObj(new LdElema(type, array, index) { DelayExceptions = true }, value, type); return new StObj(new LdElema(type, array, index) { DelayExceptions = true }, value, type);
} }
@ -1588,6 +1626,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
arguments[0] = Pop(CallInstruction.ExpectedTypeForThisPointer(constrainedPrefix ?? method.DeclaringType)); arguments[0] = Pop(CallInstruction.ExpectedTypeForThisPointer(constrainedPrefix ?? method.DeclaringType));
} }
// arguments is in reverse order of the Pop calls, thus
// arguments is now in the correct evaluation order.
switch (method.DeclaringType.Kind) switch (method.DeclaringType.Kind)
{ {
case TypeKind.Array: case TypeKind.Array:
@ -1598,20 +1638,23 @@ namespace ICSharpCode.Decompiler.IL
if (method.Name == "Set") if (method.Name == "Set")
{ {
var target = arguments[0]; var target = arguments[0];
var value = arguments.Last();
var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray(); var indices = arguments.Skip(1).Take(arguments.Length - 2).ToArray();
var value = arguments.Last();
// preserves evaluation order target,indices,value
return new StObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, value, elementType); return new StObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, value, elementType);
} }
if (method.Name == "Get") if (method.Name == "Get")
{ {
var target = arguments[0]; var target = arguments[0];
var indices = arguments.Skip(1).ToArray(); var indices = arguments.Skip(1).ToArray();
// preserves evaluation order target,indices
return Push(new LdObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, elementType)); return Push(new LdObj(new LdElema(elementType, target, indices) { DelayExceptions = true }, elementType));
} }
if (method.Name == "Address") if (method.Name == "Address")
{ {
var target = arguments[0]; var target = arguments[0];
var indices = arguments.Skip(1).ToArray(); var indices = arguments.Skip(1).ToArray();
// preserves evaluation order target,indices
return Push(new LdElema(elementType, target, indices)); return Push(new LdElema(elementType, target, indices));
} }
Warn("Unknown method called on array type: " + method.Name); Warn("Unknown method called on array type: " + method.Name);
@ -1657,6 +1700,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
arguments[0] = Pop(); arguments[0] = Pop();
} }
// arguments is in reverse order of the Pop calls, thus
// arguments is now in the correct evaluation order.
var call = new CallIndirect( var call = new CallIndirect(
header.IsInstance, header.IsInstance,
header.HasExplicitThis, header.HasExplicitThis,
@ -1674,6 +1719,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var right = Pop(); var right = Pop();
var left = Pop(); var left = Pop();
// left will run before right, thus preserving the evaluation order
if ((left.ResultType == StackType.O || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType()) if ((left.ResultType == StackType.O || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType())
{ {
@ -1856,6 +1902,12 @@ namespace ICSharpCode.Decompiler.IL
StoreStackForOffset(targetILOffset, ref currentStack); StoreStackForOffset(targetILOffset, ref currentStack);
} }
/// <summary>
/// The expression stack holds ILInstructions that might have side-effects
/// that should have already happened (in the order of the pushes).
/// This method forces these instructions to be added to the instructionBuilder.
/// This is used e.g. to avoid moving side-effects past branches.
/// </summary>
private void FlushExpressionStack() private void FlushExpressionStack()
{ {
foreach (var inst in expressionStack) foreach (var inst in expressionStack)
@ -1902,6 +1954,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var right = Pop(); var right = Pop();
var left = Pop(); var left = Pop();
// left will run before right, thus preserving the evaluation order
if (@operator != BinaryNumericOperator.Add && @operator != BinaryNumericOperator.Sub) if (@operator != BinaryNumericOperator.Add && @operator != BinaryNumericOperator.Sub)
{ {
// we are treating all Refs as I, make the conversion explicit // we are treating all Refs as I, make the conversion explicit

Loading…
Cancel
Save