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

Resolve ILLink warnings in System.Security.Cryptography.X509Certificates #47345

Merged
merged 2 commits into from
Jan 26, 2021

Conversation

eerhardt
Copy link
Member

Contributes to #45623

@ghost
Copy link

ghost commented Jan 22, 2021

Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq
See info in area-owners.md if you want to be subscribed.

Issue Details

Contributes to #45623

Author: eerhardt
Assignees: -
Labels:

area-System.Security

Milestone: -

Comment on lines -124 to -131
[DynamicDependency("#ctor(System.Net.Http.HttpMessageHandler)", "System.Net.Http.HttpClient", "System.Net.Http")]
[DynamicDependency("#ctor", "System.Net.Http.SocketsHttpHandler", "System.Net.Http")]
[DynamicDependency("#ctor", "System.Net.Http.HttpRequestMessage", "System.Net.Http")]
[DynamicDependency("set_PooledConnectionIdleTimeout", "System.Net.Http.SocketsHttpHandler", "System.Net.Http")]
[DynamicDependency("RequestUri", "System.Net.Http.HttpRequestMessage", "System.Net.Http")]
[DynamicDependency("Send", "System.Net.Http.HttpClient", "System.Net.Http")]
[DynamicDependency("Content", "System.Net.Http.HttpResponseMessage", "System.Net.Http")]
[DynamicDependency("ReadAsStream", "System.Net.Http.HttpContent", "System.Net.Http")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these are now identified by the linker because of the helper type annotation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only

[DynamicDependency("#ctor", "System.Net.Http.HttpRequestMessage", "System.Net.Http")]

is now identified by the linker because of the helper type annotation below.

The rest are unnecessary because the linker is able to directly recognize the types and ctors/properties/methods being used in this method. When it can't recognize the Reflection usage, it warns (like it was previously with httpRequestMessageType).

{
HttpRequestMessageType = httpRequestMessageType;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that the best way to prevent linker warnings is to add more overhead at runtime (and actually increase binary size with this additional class).

Did you consider just suppressing the warning? It was safe previously and just issuing warnings because the linker couldn't figure that out, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppressing it is kind of hard because one of the warnings comes from inside the lambda method. So placing the suppression attribute would need to suppress more than necessary.

And in general, the recommendation from @vitek-karas and @MichalStrehovsky has been to favor [DAM] attributes over DynamicDependency and Suppress. The reasoning being is that the linker can verify you've done it correctly. Suppressing the warning could lead to bugs in the future if the code is changed and new warnings get suppressed.

Another approach I thought of, but didn't try was to store HttpRequestMessageType on a static field with the [DAM] annotation. I shied away from it because of nullability hoops I'd have to jump through. That would only add an extra reference/pointer statically, instead of a whole new type and object instance here. Thoughts on that approach?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach would be to do the closure manually - that would let you add the necessary annotation to the field. The compiler here generates a type and so on for the closure for the returned lambda. That could be done manually... It would avoid the second new type (only the closure type would remain). But it would make the code less readable/maintainable.

You could also simplify the helper type - make it a struct and make the type a field - to reduce the metadata size impact.

I can't see how linker could figure this out without cross-method analysis unfortunately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would grab a ConstructorInfo for the default constructor in the main method body and just Invoke it in the lambda. No DynamicallyAccessed annotations necessary and no extra closures. Illink should be able to figure everything out.

ConstructorInfo is a good replacement for Activator unless the type in question is a valuetype with no explicit default ctor (in which case we can't get a ConstructorInfo, but the type can still be CreateInstance'd).

I wish C# had a way to annotate the closure though :(.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to use ConstructorInfo instead.

This has the drawback of not being able to take advantage of the perf improvements we've made recently in Activator.CreateInstance (#32520). But we are hitting an HTTP endpoint, so the perf here won't really be noticeable. Also, when we add Activator.CreateFactory (#36194), we can update this code to use the new API.

{
HttpRequestMessageType = httpRequestMessageType;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach would be to do the closure manually - that would let you add the necessary annotation to the field. The compiler here generates a type and so on for the closure for the returned lambda. That could be done manually... It would avoid the second new type (only the closure type would remain). But it would make the code less readable/maintainable.

You could also simplify the helper type - make it a struct and make the type a field - to reduce the metadata size impact.

I can't see how linker could figure this out without cross-method analysis unfortunately.

Use ConstructorInfo to create HttpRequestMessage instances.
@eerhardt
Copy link
Member Author

Test failures are #47374.

@eerhardt eerhardt merged commit fdc8148 into dotnet:master Jan 26, 2021
@eerhardt eerhardt deleted the X509CertsILLinkWarnings branch January 26, 2021 00:04
@ghost ghost locked as resolved and limited conversation to collaborators Feb 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants