Skip to content

Commit 6f23d04

Browse files
[release/9.0] Fix invalid handle bug happening when TypeBuilder type used in exception catch clause (#106704)
* Handle TypeBuilder exception type in catch clause * Remove PersistedAssemblyBuilder.IsDynamic from ref instead of overriding it --------- Co-authored-by: Buyaa Namnan <[email protected]>
1 parent 91ae788 commit 6f23d04

File tree

4 files changed

+213
-10
lines changed

4 files changed

+213
-10
lines changed

src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ public sealed class PersistedAssemblyBuilder : System.Reflection.Emit.AssemblyBu
479479
{
480480
public PersistedAssemblyBuilder(System.Reflection.AssemblyName name, System.Reflection.Assembly coreAssembly, System.Collections.Generic.IEnumerable<System.Reflection.Emit.CustomAttributeBuilder>? assemblyAttributes = null) { }
481481
public override string? FullName { get { throw null; } }
482-
public override bool IsDynamic { get { throw null; } }
483482
public override System.Reflection.Module ManifestModule { get { throw null; } }
484483
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")]
485484
protected override System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name) { throw null; }

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ internal sealed class ILGeneratorImpl : ILGenerator
3030
private int _localCount;
3131
private Dictionary<Label, LabelInfo> _labelTable = new(2);
3232
private List<KeyValuePair<object, BlobWriter>> _memberReferences = new();
33-
private List<ExceptionBlock> _exceptionStack = new();
33+
private List<ExceptionBlock> _exceptionStack = new(); // tracks the exception nesting
34+
private List<ExceptionHandlerInfo> _exceptionBlocks = new(); // keeps all ExceptionHandler blocks
3435
private Dictionary<SymbolDocumentWriter, List<SequencePoint>> _documentToSequencePoints = new();
3536

3637
internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size)
@@ -54,6 +55,32 @@ internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size)
5455
internal Scope Scope => _scope;
5556
internal Dictionary<SymbolDocumentWriter, List<SequencePoint>> DocumentToSequencePoints => _documentToSequencePoints;
5657

58+
internal void AddExceptionBlocks()
59+
{
60+
foreach(ExceptionHandlerInfo eb in _exceptionBlocks)
61+
{
62+
switch (eb.Kind)
63+
{
64+
case ExceptionRegionKind.Catch:
65+
_cfBuilder.AddCatchRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
66+
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd), _moduleBuilder.GetTypeHandle(eb.ExceptionType!));
67+
break;
68+
case ExceptionRegionKind.Filter:
69+
_cfBuilder.AddFilterRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
70+
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd), GetMetaLabel(eb.FilterStart));
71+
break;
72+
case ExceptionRegionKind.Fault:
73+
_cfBuilder.AddFaultRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
74+
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd));
75+
break;
76+
case ExceptionRegionKind.Finally:
77+
_cfBuilder.AddFinallyRegion(GetMetaLabel(eb.TryStart), GetMetaLabel(eb.TryEnd),
78+
GetMetaLabel(eb.HandlerStart), GetMetaLabel(eb.HandlerEnd));
79+
break;
80+
}
81+
}
82+
}
83+
5784
public override int ILOffset => _il.Offset;
5885

5986
public override void BeginCatchBlock(Type? exceptionType)
@@ -91,8 +118,8 @@ public override void BeginCatchBlock(Type? exceptionType)
91118

92119
currentExBlock.HandleStart = DefineLabel();
93120
currentExBlock.HandleEnd = DefineLabel();
94-
_cfBuilder.AddCatchRegion(GetMetaLabel(currentExBlock.TryStart), GetMetaLabel(currentExBlock.TryEnd),
95-
GetMetaLabel(currentExBlock.HandleStart), GetMetaLabel(currentExBlock.HandleEnd), _moduleBuilder.GetTypeHandle(exceptionType));
121+
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Catch, currentExBlock.TryStart,
122+
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd, default, exceptionType));
96123
MarkLabel(currentExBlock.HandleStart);
97124
}
98125

@@ -124,9 +151,9 @@ public override void BeginExceptFilterBlock()
124151
currentExBlock.FilterStart = DefineLabel();
125152
currentExBlock.HandleStart = DefineLabel();
126153
currentExBlock.HandleEnd = DefineLabel();
127-
_cfBuilder.AddFilterRegion(GetMetaLabel(currentExBlock.TryStart), GetMetaLabel(currentExBlock.TryEnd),
128-
GetMetaLabel(currentExBlock.HandleStart), GetMetaLabel(currentExBlock.HandleEnd), GetMetaLabel(currentExBlock.FilterStart));
129154
currentExBlock.State = ExceptionState.Filter;
155+
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Filter, currentExBlock.TryStart,
156+
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd, currentExBlock.FilterStart));
130157
MarkLabel(currentExBlock.FilterStart);
131158
// Stack depth for "filter" starts at one.
132159
_currentStackDepth = 1;
@@ -166,8 +193,8 @@ public override void BeginFaultBlock()
166193

167194
currentExBlock.HandleStart = DefineLabel();
168195
currentExBlock.HandleEnd = DefineLabel();
169-
_cfBuilder.AddFaultRegion(GetMetaLabel(currentExBlock.TryStart), GetMetaLabel(currentExBlock.TryEnd),
170-
GetMetaLabel(currentExBlock.HandleStart), GetMetaLabel(currentExBlock.HandleEnd));
196+
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Fault, currentExBlock.TryStart,
197+
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd));
171198
currentExBlock.State = ExceptionState.Fault;
172199
MarkLabel(currentExBlock.HandleStart);
173200
// Stack depth for "fault" starts at zero.
@@ -197,8 +224,8 @@ public override void BeginFinallyBlock()
197224
MarkLabel(currentExBlock.TryEnd);
198225
currentExBlock.HandleStart = DefineLabel();
199226
currentExBlock.HandleEnd = finallyEndLabel;
200-
_cfBuilder.AddFinallyRegion(GetMetaLabel(currentExBlock.TryStart), GetMetaLabel(currentExBlock.TryEnd),
201-
GetMetaLabel(currentExBlock.HandleStart), GetMetaLabel(currentExBlock.HandleEnd));
227+
_exceptionBlocks.Add(new ExceptionHandlerInfo(ExceptionRegionKind.Finally, currentExBlock.TryStart,
228+
currentExBlock.TryEnd, currentExBlock.HandleStart, currentExBlock.HandleEnd));
202229
currentExBlock.State = ExceptionState.Finally;
203230
MarkLabel(currentExBlock.HandleStart);
204231
// Stack depth for "finally" starts at zero.
@@ -835,6 +862,31 @@ internal sealed class ExceptionBlock
835862
public ExceptionState State;
836863
}
837864

865+
internal struct ExceptionHandlerInfo
866+
{
867+
public readonly ExceptionRegionKind Kind;
868+
public readonly Label TryStart, TryEnd, HandlerStart, HandlerEnd, FilterStart;
869+
public Type? ExceptionType;
870+
871+
public ExceptionHandlerInfo(
872+
ExceptionRegionKind kind,
873+
Label tryStart,
874+
Label tryEnd,
875+
Label handlerStart,
876+
Label handlerEnd,
877+
Label filterStart = default,
878+
Type? catchType = null)
879+
{
880+
Kind = kind;
881+
TryStart = tryStart;
882+
TryEnd = tryEnd;
883+
HandlerStart = handlerStart;
884+
HandlerEnd = handlerEnd;
885+
FilterStart = filterStart;
886+
ExceptionType = catchType;
887+
}
888+
}
889+
838890
internal enum ExceptionState
839891
{
840892
Undefined,

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ private void WriteMethods(List<MethodBuilderImpl> methods, List<GenericTypeParam
357357
if (il != null)
358358
{
359359
FillMemberReferences(il);
360+
il.AddExceptionBlocks();
360361
StandaloneSignatureHandle signature = il.LocalCount == 0 ? default :
361362
_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetLocalSignature(il.Locals, this)));
362363
offset = AddMethodBody(method, il, signature, methodBodyEncoder);

src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,157 @@ public void SimpleTryCatchBlock()
10761076
}
10771077
}
10781078

1079+
[Fact]
1080+
public void TryCatchWithTypeBuilderException()
1081+
{
1082+
using (TempFile file = TempFile.Create())
1083+
{
1084+
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
1085+
ModuleBuilder mb = ab.DefineDynamicModule("MyModule");
1086+
TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public);
1087+
TypeBuilder exceptionType = mb.DefineType("MyException", TypeAttributes.Public, typeof(Exception));
1088+
MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]);
1089+
ILGenerator ilGenerator = method.GetILGenerator();
1090+
ilGenerator.BeginExceptionBlock();
1091+
ilGenerator.Emit(OpCodes.Ldarg_0);
1092+
ilGenerator.Emit(OpCodes.Ldarg_1);
1093+
ilGenerator.Emit(OpCodes.Add);
1094+
ilGenerator.BeginCatchBlock(exceptionType);
1095+
ilGenerator.Emit(OpCodes.Ldc_I4_0);
1096+
ilGenerator.EndExceptionBlock();
1097+
ilGenerator.Emit(OpCodes.Ret);
1098+
tb.CreateType();
1099+
exceptionType.CreateType();
1100+
ab.Save(file.Path);
1101+
1102+
using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver()))
1103+
{
1104+
Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path);
1105+
Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType");
1106+
MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody();
1107+
Assert.Equal(1, body.ExceptionHandlingClauses.Count);
1108+
Assert.Equal("MyException", body.ExceptionHandlingClauses[0].CatchType.FullName);
1109+
Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags);
1110+
}
1111+
}
1112+
}
1113+
1114+
[Fact]
1115+
public void TryMultipleCatchFinallyBlocks()
1116+
{
1117+
using (TempFile file = TempFile.Create())
1118+
{
1119+
PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb);
1120+
MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]);
1121+
FieldBuilder fb = tb.DefineField("Field", typeof(int), FieldAttributes.Public | FieldAttributes.Static);
1122+
Type dBZException = typeof(DivideByZeroException);
1123+
TypeBuilder myExceptionType = ab.GetDynamicModule("MyModule").DefineType("MyException", TypeAttributes.Public, typeof(Exception));
1124+
myExceptionType.CreateType();
1125+
Type exception = typeof(Exception);
1126+
Type overflowException = typeof(OverflowException);
1127+
ILGenerator ilGenerator = method.GetILGenerator();
1128+
LocalBuilder local = ilGenerator.DeclareLocal(typeof(int));
1129+
Label exBlock = ilGenerator.BeginExceptionBlock();
1130+
Label check100 = ilGenerator.DefineLabel();
1131+
Label leave = ilGenerator.DefineLabel();
1132+
ilGenerator.Emit(OpCodes.Ldarg_0);
1133+
ilGenerator.Emit(OpCodes.Ldarg_1);
1134+
ilGenerator.Emit(OpCodes.Div);
1135+
ilGenerator.Emit(OpCodes.Stloc_0);
1136+
ilGenerator.Emit(OpCodes.Ldloc_0);
1137+
ilGenerator.Emit(OpCodes.Brtrue, check100);
1138+
ilGenerator.ThrowException(myExceptionType);
1139+
ilGenerator.MarkLabel(check100);
1140+
ilGenerator.Emit(OpCodes.Ldarg_1);
1141+
ilGenerator.Emit(OpCodes.Ldc_I4, 100);
1142+
ilGenerator.Emit(OpCodes.Bne_Un, leave);
1143+
ilGenerator.ThrowException(overflowException);
1144+
ilGenerator.MarkLabel(leave);
1145+
ilGenerator.BeginCatchBlock(dBZException);
1146+
ilGenerator.EmitWriteLine("Error: division by zero");
1147+
ilGenerator.Emit(OpCodes.Ldc_I4_M1);
1148+
ilGenerator.Emit(OpCodes.Stloc_0);
1149+
ilGenerator.BeginCatchBlock(myExceptionType);
1150+
ilGenerator.EmitWriteLine("Error: MyException");
1151+
ilGenerator.Emit(OpCodes.Ldc_I4_S, 2);
1152+
ilGenerator.Emit(OpCodes.Stloc_0);
1153+
ilGenerator.BeginCatchBlock(exception);
1154+
ilGenerator.EmitWriteLine("Error: generic Exception");
1155+
ilGenerator.Emit(OpCodes.Ldc_I4_S, 3);
1156+
ilGenerator.Emit(OpCodes.Stloc_0);
1157+
ilGenerator.BeginFinallyBlock();
1158+
ilGenerator.EmitWriteLine("Finally block");
1159+
ilGenerator.Emit(OpCodes.Ldc_I4_S, 30);
1160+
ilGenerator.Emit(OpCodes.Stsfld, fb);
1161+
ilGenerator.EndExceptionBlock();
1162+
ilGenerator.Emit(OpCodes.Ldloc_0);
1163+
ilGenerator.Emit(OpCodes.Ret);
1164+
tb.CreateType();
1165+
ab.Save(file.Path);
1166+
1167+
TestAssemblyLoadContext tlc = new TestAssemblyLoadContext();
1168+
Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path);
1169+
Type typeFromDisk = assemblyFromDisk.GetType("MyType");
1170+
MethodInfo methodFromDisk = typeFromDisk.GetMethod("Method");
1171+
MethodBody body = methodFromDisk.GetMethodBody();
1172+
Assert.Equal(4, body.ExceptionHandlingClauses.Count);
1173+
Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags);
1174+
Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags);
1175+
Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags);
1176+
Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[3].Flags);
1177+
Assert.Equal(dBZException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName);
1178+
Assert.Equal("MyException", body.ExceptionHandlingClauses[1].CatchType.FullName);
1179+
Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[2].CatchType.FullName);
1180+
/*
1181+
public class MyException : Exception { }
1182+
1183+
public class MyType
1184+
{
1185+
public static int Field;
1186+
public static int Method(int a, int b)
1187+
{
1188+
int res;
1189+
try{
1190+
res = a/b;
1191+
if (res == 0)
1192+
throw new MyException();
1193+
if (b == 100)
1194+
throw new OverflowException();
1195+
}
1196+
catch(DivideByZeroException)
1197+
{
1198+
Console.WriteLine("Divide by zero caught");
1199+
res = -1;
1200+
}
1201+
catch(MyException)
1202+
{
1203+
Console.WriteLine("MyException caught");
1204+
res = 2;
1205+
}
1206+
catch(Exception)
1207+
{
1208+
Console.WriteLine("Divide by zero!");
1209+
res = 3;
1210+
}
1211+
finally
1212+
{
1213+
Console.WriteLine("Finally block");
1214+
Field = 30;
1215+
}
1216+
return res;
1217+
}
1218+
}*/
1219+
FieldInfo field = typeFromDisk.GetField("Field");
1220+
Assert.Equal(0, field.GetValue(null));
1221+
Assert.Equal(5, methodFromDisk.Invoke(null, new object[] { 50, 10 }));
1222+
Assert.Equal(30, field.GetValue(null));
1223+
Assert.Equal(-1, methodFromDisk.Invoke(null, new object[] { 1, 0 }));
1224+
Assert.Equal(2, methodFromDisk.Invoke(null, new object[] { 0, 1 }));
1225+
Assert.Equal(3, methodFromDisk.Invoke(null, new object[] { 1000, 100 }));
1226+
tlc.Unload();
1227+
}
1228+
}
1229+
10791230
[Fact]
10801231
public void TryMultipleCatchBlocks()
10811232
{

0 commit comments

Comments
 (0)