Skip to content
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

Attribute to tag methods that implicitly depend on its caller #41690

Closed
abelbraaksma opened this issue Sep 1, 2020 · 11 comments
Closed

Attribute to tag methods that implicitly depend on its caller #41690

abelbraaksma opened this issue Sep 1, 2020 · 11 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime
Milestone

Comments

@abelbraaksma
Copy link
Contributor

abelbraaksma commented Sep 1, 2020

Description

The reference assemblies typically expose the same meta-info as implementation assemblies, but DynamicSecurityMethod, or reqsecobj is not exposed and only visible in the implementation assembly.

As an example, check GetCallingAssembly or GetExecutingAssembly.

Is this a deliberate limitation of the reference assemblies? It appears to me that this information should be present and the 70 or so BCL methods (see #11698 (comment), but probably less now after dotnet/coreclr#21825) that have reqsecobj should also set DynamicSecurityMethod in their reference assemblies.

If there is no backwards compat issue or other requirement that prevents inclusion of reqsecobj in the signatures, I propose we add it, so that compilers can rely on these signatures to be accurate.

Other information

This information is relevant for compilers, like here in F# (dotnet/fsharp#9283 (comment)), where we should not inline GetExecutingAssembly, but since the compiler uses the reference assemblies as a guide, it cannot properly make that decision.

Note that this is an inlining decision at compile time, irrelevant on the inlining decision of the JIT, which doesn't have this problem as it'll see the implementation assembly.

A workaround was proposed by @dsyme (see dotnet/fsharp#9283 (comment)) to hard-code the BCL methods that have reqsecobj and/or StackCrawlMark so that

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI untriaged New issue has not been triaged by the area owner labels Sep 1, 2020
@jkotas jkotas added area-Meta area-System.Runtime api-suggestion Early API idea and discussion, it is NOT ready for implementation and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI area-Meta labels Sep 1, 2020
@jkotas
Copy link
Member

jkotas commented Sep 1, 2020

DynamicSecurityMethod attribute was used for Code Access Security originally. CoreCLR reused it as an internal implementation detail for a different purpose that has nothing to do with security.

If we were to make it a public concept, I think we should give it a new name that captures the intent properly.

@jkotas
Copy link
Member

jkotas commented Sep 1, 2020

There may be alternative designs possible: Add CallerObjectAttribute that can be applied to [Assembly/Type/MethodBase] argument. The compiler is then required to fill in the caller object, similar to how it fills in names for the existing CallerMemberNameAttribute and other similar types. This design would also fix the bad performance of these methods (e.g. #41575).

@abelbraaksma
Copy link
Contributor Author

@jkotas, I think you misunderstand my proposal here. This is not about making it available to the general public, nor to change its meaning.

It's about an inconsistency in the attribute not being present in the reference assemblies of dotnet, while the implementation assembly does carry it.

This pseudocustom attribute is emitted as a reserved method flag. While it was originally used for security, it has also been applied to BCL methods that require an intact stack, and therefore cannot, and should not be cross module optimized.

The absence of this flag in the reference assembly, as noted by @dsyme, leads to incorrect compiled IL by compilers that do compile time inlining like F#.

In short, I think all method flags in the reference assembly of runtime .NET Core libraries should be equal to the flags in the implementation assembly.

@abelbraaksma abelbraaksma changed the title DynamicSecurityMethod aka reqsecobj is not exposed in the reference assemblies Method flag 'reqsecobj' when present in BCL implementation assemblies is not exposed in the method flags of the reference assemblies Sep 1, 2020
@abelbraaksma
Copy link
Contributor Author

abelbraaksma commented Sep 1, 2020

@jkotas, just to emphasize: this is not an api-suggestion, but a bug, or oversight in the current Base Class Libraries.

I can see two ways to fix this:

  • Add NoInlining to the methods in BCL (both in reference and impl. assemblies) that should never be inlined (this is a no-op for JIT as these methods already aren't inlined because of reqsecobj present)
  • Add reqsecobj to the reference assembly in places where it doesn't align with the impl. assemblies. Again, this won't be seen by JIT, and we can take care of this in the F# compiler by looking at the methodsig of the reference assembly as we currently do.

Example:

Take GetExecutingAssembly. The reference assembly, i.e. C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.dll has:

.method public hidebysig static class System.Reflection.Assembly 
        GetExecutingAssembly() cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  ldnull
  IL_0001:  throw
} // end of method Assembly::GetExecutingAssembly

While the implementation assembly has:

.method public hidebysig reqsecobj static class System.Reflection.Assembly 
        GetExecutingAssembly () cil managed 
{
	// Method begins at RVA 0x159270
	// Code size 10 (0xa)
	.maxstack 1
	.locals (
		[0] valuetype System.Threading.StackCrawlMark
	)

	IL_0000: ldc.i4.1
	IL_0001: stloc.0
	IL_0002: ldloca.s 0
	IL_0004: call class System.Reflection.RuntimeAssembly System.Reflection.Assembly::GetExecutingAssembly(valuetype System.Threading.StackCrawlMark&)
	IL_0009: ret
}

Note the difference in the signature, the first misses the reqsecobj, which should not be inlined.

-.method public hidebysig static class System.Reflection.Assembly 
+.method public hidebysig reqsecobj static class System.Reflection.Assembly 

@jkotas
Copy link
Member

jkotas commented Sep 1, 2020

It's about an inconsistency in the attribute not being present in the reference assemblies of dotnet, while the implementation assembly does carry it.

We have number of other attributes that are present in the implementation assemblies, but that are not present in reference assemblies.

this is not an api-suggestion

The DynamicSecurityMethod attribute that this is partially about is not in the public .NET 5 API surface. We treat all additions to the public surface as API suggestions.

@dsyme
Copy link

dsyme commented Sep 1, 2020

@abelbraaksma I agree with @jkotas - this is really an internal implementation flag.

There may be alternative designs possible: Add CallerObjectAttribute that can be applied to [Assembly/Type/MethodBase] argument. The compiler is then required to fill in the caller object, similar to how it fills in names for the existing CallerMemberNameAttribute and other similar types. This design would also fix the bad performance of these methods (e.g. #41575).

This is a good suggestion

@abelbraaksma
Copy link
Contributor Author

abelbraaksma commented Sep 1, 2020

The DynamicSecurityMethod attribute that this is partially about is not in the public .NET 5 API surface. We treat all additions to the public surface as API suggestions.

Aha, gotcha. But this one is currently present internally(but compiled as method flag, not attribute), and since this is a reserved method attribute, we don't need to add the attribute itself to a method in the reference assembly, but only the reqsecobj, method flag 0x8000. I don't mean to expose it.

[System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static Assembly GetExecutingAssembly()
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return GetExecutingAssembly(ref stackMark);
}

We have number of other attributes that are present in the implementation assemblies, but that are not present in reference assemblies.

I wasn't aware. But now I see that NoInlining is also not in the reference assemblies... It starts to dawn on me ;).

@abelbraaksma
Copy link
Contributor Author

This is a good suggestion

@dsyme, I'm not sure I follow, how would that help the current situation? If it does, that would be great, of course.

@dsyme
Copy link

dsyme commented Sep 3, 2020

@dsyme, I'm not sure I follow, how would that help the current situation? If it does, that would be great, of course.

It wouldn't, AFAIU it would be a new API in .NET

For F# we still need to do something about knowing what methods should not be inlined

@jkotas jkotas removed the untriaged New issue has not been triaged by the area owner label Sep 21, 2020
@jkotas jkotas added this to the Future milestone Sep 21, 2020
@jkotas jkotas changed the title Method flag 'reqsecobj' when present in BCL implementation assemblies is not exposed in the method flags of the reference assemblies Attribute to tag methods that implicitly depend on its caller Sep 21, 2020
@abelbraaksma
Copy link
Contributor Author

Possibly relevant to this discussion is how CoreCLR currently fixed this for crossgen: dotnet/coreclr#27050.

The issue for source-code inlining is similar, except that we don't have the same info as the CLR during compiling (as noted in the discussion above). This is not only about inlining, but also about tail-calling when done during the compile phase (vs the JIT phase).

@jkotas
Copy link
Member

jkotas commented Jul 30, 2021

I have created formal API proposal https://github.com/dotnet/runtime/issues/56601

@jkotas jkotas closed this as completed Jul 30, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Runtime
Projects
None yet
Development

No branches or pull requests

4 participants