Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit aa00dad

Browse files
authored
JIT: Fix bug in finally cloning caused by unsound callfinally reordering
Port of #18348 to release/2.1 We need to make sure that if we reorder a callfinally during finally cloning that the callfinally is actually the one being targeted by the last block in the try range. Closes #18332. Linked issue has some more detailed notes.
1 parent 373b10e commit aa00dad

File tree

3 files changed

+122
-17
lines changed

3 files changed

+122
-17
lines changed

src/jit/flowgraph.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24574,32 +24574,44 @@ void Compiler::fgCloneFinally()
2457424574
// We better have found at least one call finally.
2457524575
assert(firstCallFinallyBlock != nullptr);
2457624576

24577-
// If there is more than one callfinally, move the one we are
24578-
// going to retarget to be first in the callfinally range.
24577+
// If there is more than one callfinally, we'd like to move
24578+
// the one we are going to retarget to be first in the callfinally,
24579+
// but only if it's targeted by the last block in the try range.
2457924580
if (firstCallFinallyBlock != normalCallFinallyBlock)
2458024581
{
24581-
JITDUMP("Moving callfinally BB%02u to be first in line, before BB%02u\n", normalCallFinallyBlock->bbNum,
24582-
firstCallFinallyBlock->bbNum);
24583-
24584-
BasicBlock* const firstToMove = normalCallFinallyBlock;
24585-
BasicBlock* const lastToMove = normalCallFinallyBlock->bbNext;
2458624582
BasicBlock* const placeToMoveAfter = firstCallFinallyBlock->bbPrev;
2458724583

24588-
fgUnlinkRange(firstToMove, lastToMove);
24589-
fgMoveBlocksAfter(firstToMove, lastToMove, placeToMoveAfter);
24584+
if ((placeToMoveAfter->bbJumpKind == BBJ_ALWAYS) &&
24585+
(placeToMoveAfter->bbJumpDest == normalCallFinallyBlock))
24586+
{
24587+
JITDUMP("Moving callfinally BB%02u to be first in line, before BB%02u\n",
24588+
normalCallFinallyBlock->bbNum, firstCallFinallyBlock->bbNum);
24589+
24590+
BasicBlock* const firstToMove = normalCallFinallyBlock;
24591+
BasicBlock* const lastToMove = normalCallFinallyBlock->bbNext;
24592+
24593+
fgUnlinkRange(firstToMove, lastToMove);
24594+
fgMoveBlocksAfter(firstToMove, lastToMove, placeToMoveAfter);
2459024595

2459124596
#ifdef DEBUG
24592-
// Sanity checks
24593-
fgDebugCheckBBlist(false, false);
24594-
fgVerifyHandlerTab();
24597+
// Sanity checks
24598+
fgDebugCheckBBlist(false, false);
24599+
fgVerifyHandlerTab();
2459524600
#endif // DEBUG
2459624601

24597-
assert(nextBlock == lastBlock->bbNext);
24602+
assert(nextBlock == lastBlock->bbNext);
2459824603

24599-
// Update where the callfinally range begins, since we might
24600-
// have altered this with callfinally rearrangement, and/or
24601-
// the range begin might have been pretty loose to begin with.
24602-
firstCallFinallyRangeBlock = normalCallFinallyBlock;
24604+
// Update where the callfinally range begins, since we might
24605+
// have altered this with callfinally rearrangement, and/or
24606+
// the range begin might have been pretty loose to begin with.
24607+
firstCallFinallyRangeBlock = normalCallFinallyBlock;
24608+
}
24609+
else
24610+
{
24611+
JITDUMP("Can't move callfinally BB%02u to be first in line"
24612+
" -- last finally block BB%02u doesn't jump to it\n",
24613+
normalCallFinallyBlock->bbNum, placeToMoveAfter->bbNum);
24614+
}
2460324615
}
2460424616
}
2460524617

@@ -24772,6 +24784,9 @@ void Compiler::fgCloneFinally()
2477224784
{
2477324785
// We can't retarget this call since it
2477424786
// returns somewhere else.
24787+
JITDUMP("Can't retarget callfinally in BB%02u as it jumps to BB%02u, not BB%02u\n",
24788+
currentBlock->bbNum, postTryFinallyBlock->bbNum, normalCallFinallyReturn->bbNum);
24789+
2477524790
retargetedAllCalls = false;
2477624791
}
2477724792
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
8+
internal class Foo : IDisposable
9+
{
10+
public void Dispose()
11+
{
12+
}
13+
}
14+
15+
class GitHub_18332
16+
{
17+
// In Aargh there is a finally with two distinct exit paths.
18+
// Finally cloning may choose the non-fall through ("wibble") exit
19+
// path to clone, and then will try to incorrectly arrange for
20+
// that path to become the fall through.
21+
public static string Aargh()
22+
{
23+
using (var foo = new Foo())
24+
{
25+
foreach (var i in new List<int>())
26+
{
27+
try
28+
{
29+
Console.WriteLine("here");
30+
}
31+
catch (Exception)
32+
{
33+
return "wibble";
34+
}
35+
}
36+
37+
foreach (var i in new List<int>())
38+
{
39+
}
40+
}
41+
42+
return "wobble";
43+
}
44+
45+
public static int Main(string[] args)
46+
{
47+
string expected = "wobble";
48+
string actual = Aargh();
49+
if (actual != expected)
50+
{
51+
Console.WriteLine($"FAIL: Aargh() returns '{actual}' expected '{expected}'");
52+
return 0;
53+
}
54+
return 100;
55+
}
56+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
8+
<SchemaVersion>2.0</SchemaVersion>
9+
<ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
10+
<OutputType>Exe</OutputType>
11+
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
12+
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
13+
</PropertyGroup>
14+
<!-- Default configurations to help VS understand the configurations -->
15+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
16+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
17+
<ItemGroup>
18+
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
19+
<Visible>False</Visible>
20+
</CodeAnalysisDependentAssemblyPaths>
21+
</ItemGroup>
22+
<PropertyGroup>
23+
<DebugType></DebugType>
24+
<Optimize>True</Optimize>
25+
</PropertyGroup>
26+
<ItemGroup>
27+
<Compile Include="GitHub_18332.cs" />
28+
</ItemGroup>
29+
<ItemGroup>
30+
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
31+
</ItemGroup>
32+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
33+
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
34+
</Project>

0 commit comments

Comments
 (0)