Skip to content

make sure OpenSSL is initialized before Tls13Supported code runs #62037

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

Closed
wants to merge 1 commit into from

Conversation

wfurt
Copy link
Member

@wfurt wfurt commented Nov 25, 2021

This is regression caused by #46640. Tls13Supported can be called before static constructor runs. It was OK in 5.0 since the OpenSSL library was initialized via

__attribute__((constructor))
static void InitializeOpenSSLShim()

With 6.0, it will be initialized when Ssl us used but some properties like CiphersSuitePolicy can be used before in order to prepare everything needed.

@bartonjs suggested different fix on #61891. It works as expected but I did not like the fact that it removed the readonly attribute on Tls13Supported. I don't know if the impact is measurable but it feels like it would better to do little bit more one time instead of loosing some optimization on every SslStream.

In similar note, Comments on the change agreed that InitializeOpenSSLShim is idempotent. While that is true, loading symbols and library is externally visible and non-trivial amount of system calls and other operations. When somebody debugs loading, seeing the operation multiple times would not make it easier. I added simple lock to make the loading exactly once like we did in 5.0. Since this is static method, the extra cost should be negligible IMHO.

fixes #61891.

@wfurt wfurt added area-System.Net.Security os-linux Linux OS (any supported distro) labels Nov 25, 2021
@wfurt wfurt self-assigned this Nov 25, 2021
@ghost
Copy link

ghost commented Nov 25, 2021

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

Issue Details

This is regression caused by #46640. Tls13Supported can be called before static constructor runs. It was OK in 5.0 since the OpenSSL library was initialized via

__attribute__((constructor))
static void InitializeOpenSSLShim()

With 6.0, it will be initialized when Ssl us used but some properties like CiphersSuitePolicy can be used before in order to prepare everything needed.

@bartonjs suggested different fix on #61891. It works as expected but I did not like the fact that it removed the readonly attribute on Tls13Supported. I don't know if the impact is measurable but it feels like it would better to do little bit more one time instead of loosing some optimization on every SslStream.

In similar note, Comments on the change agreed that InitializeOpenSSLShim is idempotent. While that is true, loading symbols and library is externally visible and non-trivial amount of system calls and other operations. When somebody debugs loading, seeing the operation multiple times would not make it easier. I added simple lock to make the loading exactly once like we did in 5.0. Since this is static method, the extra cost should be negligible IMHO.

fixes #61891.

Author: wfurt
Assignees: wfurt
Labels:

area-System.Net.Security, os-linux

Milestone: -

//Call ssl specific initializer
Ssl.EnsureLibSslInitialized();
if (Interop.Crypto.ErrPeekLastError() != 0)
lock (_initLock)
Copy link
Member

Choose a reason for hiding this comment

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

None of this locking is necessary, or really even useful. It can run in parallel across (today) 4 different assemblies. Even after I'm done collapsing things it'll still be two. So the called code is (supposed to be) defensive against concurrency.

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 know it can run. But that will make it visible to strace or run with LD_DEBUG, would it not?
What I'm trying to argue is that we can do unnecessary file system operation and system calls.

Copy link
Member

Choose a reason for hiding this comment

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

The library load will only ever happen once, the P/Invoke into EnsureLibSslInitialized will only happen once per assembly (cctor guarantee), and the cascaded EnsureOpenSslInitialized will only happen once per assembly. If you do the change as currently structured but remove the locking then there might be two calls to EnsureLibSslInitialized (one from the cctor, one from the extra explicit call).

Restructuring things to move the field off this class will keep things simple looking while staying with the current initialize-count. Even just making a nested type will fix it... e.g. Interop.Ssl.Tls13Supported => Interop.Ssl.Capabilities.Tls13Supported.

Copy link
Member

Choose a reason for hiding this comment

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

Even just making a nested type will fix it... e.g. Interop.Ssl.Tls13Supported => Interop.Ssl.Capabilities.Tls13Supported

This makes sense to me.

private static partial int Tls13SupportedImpl();
internal static readonly bool Tls13Supported = Tls13SupportedImpl() != 0;
private static partial int Tls13SupportedNative();
internal static readonly bool Tls13Supported = Tls13SupportedImpl();
Copy link
Member

Choose a reason for hiding this comment

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

Rather than all this work, if the readonly-ness is still important, I'd suggest just moving the static field to a different type (just somewhere in System.Net.Security instead of in Common/src/Interop)

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 don't have any benchmarks. But it seems like doing something once is better than possibly insure some repeatedly.
Of course, crypt is CPU expensive so the benefit may come more from code generation/elimination IMHO.

@wfurt wfurt closed this Dec 17, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jan 17, 2022
@karelz karelz added this to the 7.0.0 milestone Apr 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security os-linux Linux OS (any supported distro)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CipherSuitesPolicy constructor throws PlatformNotSupportedException on Linux with OpenSSL 1.1.1
4 participants