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 @@ -135,14 +135,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ConstructExceptionTable();
newBody = AnalyzeMoveNext(function);
}
catch (SymbolicAnalysisFailedException)
catch (SymbolicAnalysisFailedException ex)
{
function.Warnings.Add($"yield-return decompiler failed: {ex.Message}");
return;
}
context.Step("Replacing body with MoveNext() body", function);
function.IsIterator = true;
function.StateMachineCompiledWithMono = isCompiledWithMono;
var oldBody = function.Body;
function.Body = newBody;
// register any locals used in newBody
function.Variables.AddRange(newBody.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct());
@ -172,14 +174,28 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -172,14 +174,28 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
newBody.SortBlocks(deleteUnreachableBlocks: true);
function.CheckInvariant(ILPhase.Normal);
if (isCompiledWithMono)
try
{
CleanSkipFinallyBodies(function);
if (isCompiledWithMono)
{
CleanSkipFinallyBodies(function);
}
else
{
DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(function);
}
}
else
catch (SymbolicAnalysisFailedException ex)
{
DecompileFinallyBlocks();
ReconstructTryFinallyBlocks(function);
// Revert the yield-return transformation
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);
@ -336,7 +352,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -336,7 +352,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var block = body as Block;
if (body is BlockContainer blockContainer && blockContainer.Blocks.Count == 1)
{
block = blockContainer.Blocks.Single() as Block;
block = blockContainer.Blocks.Single();
}
return block;
}
@ -431,11 +447,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -431,11 +447,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{
var metadata = context.PEFile.Metadata;
if (method.IsNil)
throw new SymbolicAnalysisFailedException();
throw new SymbolicAnalysisFailedException("Method not found");
var methodDef = metadata.GetMethodDefinition(method);
if (!methodDef.HasBody())
throw new SymbolicAnalysisFailedException();
throw new SymbolicAnalysisFailedException($"Method {methodDef.Name} has no body");
GenericContext genericContext = context.Function.GenericContext;
genericContext = new GenericContext(
@ -465,7 +481,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -465,7 +481,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".get_Current", StringComparison.Ordinal));
Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body);
if (body == null)
throw new SymbolicAnalysisFailedException();
throw new SymbolicAnalysisFailedException("get_Current has no body");
if (body.Instructions.Count == 1)
{
// release builds directly return the current field
@ -1052,6 +1068,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -1052,6 +1068,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{
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
decompiledFinallyMethods.Add(method, (newState, function));
}
@ -1202,6 +1224,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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
int? GetNewState(Block block)
{

Loading…
Cancel
Save