-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Validate invalid base type declarations in ILVerify #129118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
87dc394
0657062
a7ece37
828661d
74f03d2
60febc6
6265b35
e1ea8a3
7ddbf4b
64d7e01
22f2d56
873f126
fcba18c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,33 +146,21 @@ TypeSpecBlob ::= | |
|
|
||
| 3. PEVerify, the CLR runtime and C# compiler prior to VS 2015 report an error when encountering an encoded TypeSpec in the positions described above. | ||
|
|
||
| ### 2. `(CMOD_OPT | CMOD_REQ) <TypeSpec>` is permitted in practice | ||
| ### 2. `(CMOD_OPT | CMOD_REQ) <TypeSpec>` is invalid | ||
|
|
||
| In II.23.2.7, it is noted that CMOD_OPT or CMOD_REQD is followed | ||
| by a TypeRef or TypeDef metadata token, but TypeSpec tokens are | ||
| also allowed by ilasm, csc, peverify, and the CLR. | ||
| by a TypeRef or TypeDef metadata token. TypeSpec tokens in this | ||
| position should be treated as invalid metadata. | ||
|
|
||
| Note, in particular, that TypeSpecs are used there by C++/CLI to | ||
| represent strongly-typed boxing in C++/CLI. e.g. `Nullable<int>^` | ||
| in C++/CLI becomes | ||
| ``[mscorlib]System.ValueType modopt([mscorlib]System.Nullable`1<int>) modopt([mscorlib]System.Runtime.CompilerServices.IsBoxed)`` | ||
| in IL. | ||
|
|
||
| This tolerance adds a loophole to the rule above whereby cyclical | ||
| signatures are in fact possible, e.g.: | ||
|
|
||
| * `TypeSpec #1: PTR CMOD_OPT <TypeSpec #1> I4` | ||
|
|
||
| Such signatures can currently cause crashes in the runtime and various | ||
| tools, so if the spec is amended to permit TypeSpecs as modifiers, | ||
| then there should be a clarification that cycles are nonetheless not | ||
| permitted, and ideally readers would detect such cycles and handle the | ||
| error with a suitable message rather than a stack overflow. | ||
| Allowing TypeSpec custom modifier types creates a path for | ||
| recursive TypeSpec encodings that are complicated to detect. | ||
| Such metadata should be rejected rather than cycle-detected. | ||
|
|
||
| Related issues: | ||
|
|
||
| * https://github.com/dotnet/roslyn/issues/7971 | ||
| * https://github.com/dotnet/runtime/issues/4945 | ||
| * https://github.com/dotnet/runtime/pull/123819#discussion_r2810397721 | ||
|
|
||
| #### Proposed specification change | ||
|
|
||
|
|
@@ -185,9 +173,9 @@ for details | |
| with | ||
|
|
||
| > The CMOD_OPT or CMOD_REQD is followed by a metadata token that indexes a row in the TypeDef | ||
| table, TypeRef table, or TypeSpec table. However, these tokens are encoded and compressed – | ||
| see §II.23.2.8 for details. Furthermore, if a row in the TypeSpec table is indicated, | ||
| it must not create cycle. | ||
| table or TypeRef table. However, these tokens are encoded and compressed – | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not proposing any actual change anymore. Both original and proposed replacements are identical. If we want to propose any change here, it should be to add a sentence that explains why typespecs are not allowed.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. I added a sentence explaining that TypeSpec tokens are not valid in this position because allowing them could form recursive TypeSpec encodings. |
||
| see §II.23.2.8 for details. TypeSpec tokens are not valid in this position; this | ||
| restriction prevents custom modifiers from forming recursive TypeSpec encodings. | ||
|
|
||
| ### 3. Custom modifiers can go in more places than specified | ||
|
|
||
|
|
@@ -323,17 +311,6 @@ sense in `TypeSpec` are `(CLASS | VALUETYPE) TypeDefOrRef` since | |
| `TypeDefOrRef` tokens can be used directly and the indirection through | ||
| a `TypeSpec` would serve no purpose. | ||
|
|
||
| In the same way as `constrained.`, (assuming #2 is a spec bug and not | ||
| an ilasm/peverify/CLR quirk), custom modifiers can beget `TypeSpec`s | ||
| beyond what is allowed by II.23.2.14, e.g. `modopt(int32)` creates a | ||
| typespec with signature I4. | ||
|
|
||
| Even more obscurely, this gives us a way to use `VOID`, `TYPEDBYREF`, | ||
| `CMOD_OPT`, and `CMOD_REQ` at the root of a `TypeSpec`, which are not even | ||
| specified as valid at the root of a `Type`: `modopt(int32 modopt(int32))`, | ||
| `modopt(void)`, and `modopt(typedref)` all work in | ||
| practice. `CMOD_OPT` and `CMOD_REQ` at the root can also be obtained by putting | ||
| a modifier on the type used with `constrained.`. | ||
|
|
||
| ## Heap sizes | ||
|
|
||
|
|
@@ -1196,4 +1173,4 @@ In section II.23.1.15, the following row is added to the table: | |
|
|
||
| ## Implict argument coercion rules | ||
|
|
||
| Implicit argument coercion as defined in section III.1.6 does not match with existing practice in CLR runtimes. Notably, implicit argument coercion of an `int32` on the IL evaluation stack to a `native unsigned int` is a sign extending operation, not a zero-extending operation. | ||
| Implicit argument coercion as defined in section III.1.6 does not match with existing practice in CLR runtimes. Notably, implicit argument coercion of an `int32` on the IL evaluation stack to a `native unsigned int` is a sign extending operation, not a zero-extending operation. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| .assembly extern System.Runtime | ||
| { | ||
| } | ||
|
|
||
| .assembly BaseTypeTests | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit ObjectTypeSpecBase_InvalidType_InvalidBaseType | ||
| extends object | ||
| { | ||
| .method public hidebysig specialname rtspecialname instance void .ctor() cil managed | ||
| { | ||
| .maxstack 8 | ||
|
|
||
| ldarg.0 | ||
| call instance void [System.Runtime]System.Object::.ctor() | ||
| ret | ||
| } | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit ArrayTypeSpecBase_InvalidType_InvalidBaseType | ||
| extends int32[] | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit PointerTypeSpecBase_InvalidType_InvalidBaseType | ||
| extends int32* | ||
| { | ||
| } | ||
|
|
||
| .class interface public auto ansi abstract NilBaseInterface_ValidType_Valid | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit GenericBase`1<T> | ||
| extends [System.Runtime]System.Object | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit GenericClassTypeSpecBase_ValidType_Valid | ||
| extends class GenericBase`1<int32> | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit GenericOpenClassTypeSpecBase_ValidType_Valid`1<T> | ||
| extends class GenericBase`1<!0> | ||
| { | ||
| } | ||
|
|
||
| .class public sequential ansi sealed beforefieldinit ValueTypeBase | ||
| extends [System.Runtime]System.ValueType | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit ValueTypeBase_InvalidType_InvalidBaseType | ||
| extends ValueTypeBase | ||
| { | ||
| } | ||
|
|
||
| .class public sequential ansi sealed beforefieldinit GenericValueTypeBase`1<T> | ||
| extends [System.Runtime]System.ValueType | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit GenericValueTypeSpecBase_InvalidType_InvalidBaseType | ||
| extends valuetype GenericValueTypeBase`1<int32> | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.IL"> | ||
| <PropertyGroup> | ||
| <AssemblyName>$(MSBuildProjectName)</AssemblyName> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="$(MSBuildProjectName).il" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,7 +140,7 @@ | |
|
|
||
| ldloca.s V_0 | ||
| ldstr "Hello" | ||
| stobj string | ||
| stobj [System.Runtime]System.String | ||
| ret | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| .assembly extern System.Runtime | ||
| { | ||
| } | ||
|
|
||
| .assembly MalformedTypeSpecTests | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit GenericBase`1<T> | ||
| extends [System.Runtime]System.Object | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit CustomModifierTypeSpecBase_InvalidType_None | ||
| extends class GenericBase`1<int32 modopt(int32)> | ||
| { | ||
| } | ||
|
|
||
| .class public auto ansi beforefieldinit MethodSignatureTests | ||
| extends [System.Runtime]System.Object | ||
| { | ||
| .method public static hidebysig int32 modopt(int32) CustomModifierTypeSpecReturn_Invalid_None() cil managed | ||
| { | ||
| ldc.i4.0 | ||
| ret | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.IL"> | ||
| <PropertyGroup> | ||
| <AssemblyName>$(MSBuildProjectName)</AssemblyName> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="$(MSBuildProjectName).il" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Internal.TypeSystem.Ecma | ||
| { | ||
| public partial struct EcmaSignatureParser | ||
| { | ||
| partial void ReportInvalidTypeSpec() | ||
| { | ||
| ThrowHelper.ThrowBadImageFormatException(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Could you please reformat this to have similar text width, and add new line before "Related issues"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I’ve updated it.