Browse Source

Fix #2480: Avoid yield-return decompilation if there are unrecognized state assignments in a finally method.

pull/2566/head
Daniel Grunwald 4 years ago
parent
commit
cedd8a6bf6
  1. 49
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

49
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -135,14 +135,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ConstructExceptionTable(); ConstructExceptionTable();
newBody = AnalyzeMoveNext(function); newBody = AnalyzeMoveNext(function);
} }
catch (SymbolicAnalysisFailedException) catch (SymbolicAnalysisFailedException ex)
{ {
function.Warnings.Add($"yield-return decompiler failed: {ex.Message}");
return; return;
} }
context.Step("Replacing body with MoveNext() body", function); context.Step("Replacing body with MoveNext() body", function);
function.IsIterator = true; function.IsIterator = true;
function.StateMachineCompiledWithMono = isCompiledWithMono; function.StateMachineCompiledWithMono = isCompiledWithMono;
var oldBody = function.Body;
function.Body = newBody; function.Body = newBody;
// register any locals used in newBody // register any locals used in newBody
function.Variables.AddRange(newBody.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct()); function.Variables.AddRange(newBody.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct());
@ -172,14 +174,28 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newBody.SortBlocks(deleteUnreachableBlocks: true); newBody.SortBlocks(deleteUnreachableBlocks: true);
function.CheckInvariant(ILPhase.Normal); function.CheckInvariant(ILPhase.Normal);
if (isCompiledWithMono) try
{ {
CleanSkipFinallyBodies(function); if (isCompiledWithMono)
{
CleanSkipFinallyBodies(function);
}
else
{
DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(function);
}
} }
else catch (SymbolicAnalysisFailedException ex)
{ {
DecompileFinallyBlocks(); // Revert the yield-return transformation
ReconstructTryFinallyBlocks(function); context.Step("Transform failed, roll it back", function);
function.IsIterator = false;
function.StateMachineCompiledWithMono = false;
function.Body = oldBody;
function.Variables.RemoveDead();
function.Warnings.Add($"yield-return decompiler failed: {ex.Message}");
return;
} }
context.Step("Translate fields to local accesses", function); context.Step("Translate fields to local accesses", function);
@ -336,7 +352,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var block = body as Block; var block = body as Block;
if (body is BlockContainer blockContainer && blockContainer.Blocks.Count == 1) if (body is BlockContainer blockContainer && blockContainer.Blocks.Count == 1)
{ {
block = blockContainer.Blocks.Single() as Block; block = blockContainer.Blocks.Single();
} }
return block; return block;
} }
@ -431,11 +447,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{ {
var metadata = context.PEFile.Metadata; var metadata = context.PEFile.Metadata;
if (method.IsNil) if (method.IsNil)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException("Method not found");
var methodDef = metadata.GetMethodDefinition(method); var methodDef = metadata.GetMethodDefinition(method);
if (!methodDef.HasBody()) if (!methodDef.HasBody())
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException($"Method {methodDef.Name} has no body");
GenericContext genericContext = context.Function.GenericContext; GenericContext genericContext = context.Function.GenericContext;
genericContext = new GenericContext( genericContext = new GenericContext(
@ -465,7 +481,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".get_Current", StringComparison.Ordinal)); && metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".get_Current", StringComparison.Ordinal));
Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body); Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body);
if (body == null) if (body == null)
throw new SymbolicAnalysisFailedException(); throw new SymbolicAnalysisFailedException("get_Current has no body");
if (body.Instructions.Count == 1) if (body.Instructions.Count == 1)
{ {
// release builds directly return the current field // release builds directly return the current field
@ -1052,6 +1068,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{ {
body.EntryPoint.Instructions.RemoveAt(0); body.EntryPoint.Instructions.RemoveAt(0);
} }
// Avoid yield-return decompilation if there are unrecognized state assignments in a finally method.
foreach (var inst in body.Descendants)
{
if (IsStateAssignment(inst))
throw new SymbolicAnalysisFailedException($"Unknown state transition in {function.Name ?? "finally"} at IL_{inst.StartILOffset:x4}");
}
function.ReleaseRef(); // make body reusable outside of function function.ReleaseRef(); // make body reusable outside of function
decompiledFinallyMethods.Add(method, (newState, function)); decompiledFinallyMethods.Add(method, (newState, function));
} }
@ -1202,6 +1224,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
} }
bool IsStateAssignment(ILInstruction inst)
{
return inst.MatchStFld(out var target, out var field, out _)
&& target.MatchLdThis()
&& field.MemberDefinition.Equals(stateField);
}
// Gets the state that is transitioned to at the start of the block // Gets the state that is transitioned to at the start of the block
int? GetNewState(Block block) int? GetNewState(Block block)
{ {

Loading…
Cancel
Save