Add config to force OpenSsl error queue cleanup before Encrypt/Decrypt - #29186
Conversation
This is a escape valve in case the optimization of removing the calls to ERR_clear_error() for each SSL_write/SSL_read causes troubles. There will be a performance impact but depending on the scenario it can be a desired trade-off.
|
@pjanotti fixing the milestone - it will go into master = 2.2. If we want it to 2.1, we need to cherry pick it + it would be good to have a tracking bug. Can you please create one? |
|
Done, #29188 created. |
|
@dotnet/dnceng OSX x64 seems stuck https://ci3.dot.net/job/dotnet_corefx/job/master/job/osx-TGroup_netcoreapp+CGroup_Debug+AGroup_x64+TestOuter_false_prtest/11570/consoleText after successful build. @dotnet-bot test OSX x64 Debug Build |
|
@pjanotti Not stuck, just backed up. |
|
Ops, then I didn't help by asking for another build... |
| if (!AppContext.TryGetSwitch("System.Net.Security.SslStream.ForceOpenSslErrorQueueCleanupBeforeEncryptDecrypt", out bool forceErrorQueueCleanup)) | ||
| { | ||
| // AppContext wasn't used, try the environment variable. | ||
| string envVar = Environment.GetEnvironmentVariable("DOTNET_FORCE_OPENSSL_ERROR_QUEUE_CLEANUP_BEFORE_ENCRYPT_DECRYPT"); |
There was a problem hiding this comment.
In general I think we should keep the appctx switch name and the env var name to be as similar as possible, e.g.
System.Net.Security.SslStream.ForceClearOpenSslErrorQueue
and
DOTNET_SYSTEM_NET_SECURITY_SSLSTREAM_FORCECLEAROPENSSLERRORQUEUE
|
|
||
| extern "C" int32_t CryptoNative_SslWrite(SSL* ssl, const void* buf, int32_t num) | ||
| { | ||
| if (g_forceOpenSslErrorQueueCleanupBeforeWriteRead) |
There was a problem hiding this comment.
Does this check/branch have any impact on the case where it's not set? i.e. the default case where the user doesn't set the flag? Presumably it's a drop in the bucket compared to the comparison, but it'd be good to know for sure.
There was a problem hiding this comment.
The performance with the config (default, optimization on) is indistinguishable compared to master - same test indicates that when the optimization is off there is a 5 to 15% extra time to complete the test (test based on your ConcurrentHttpsGet test, but I had to run it under Xunit since I couldn't get Benchmark.NET working against my local build).
| private unsafe static readonly Ssl.SslCtxSetAlpnCallback s_alpnServerCallback = AlpnServerSelectCallback; | ||
| private static readonly IdnMapping s_idnMapping = new IdnMapping(); | ||
|
|
||
| static OpenSsl() |
There was a problem hiding this comment.
Before this change, the compiler could make the Interop.OpenSsl class beforefieldinit. Adding this explicit static cctor is going to stop that from happening, which can have perf implications.
There was a problem hiding this comment.
Good point, perhaps initialize a field with a side effect? Let me know if you have any suggestions
|
How far are we from having audited all of our calls? This may be naive, but it seems like the right answer is to audit all of the calls, make sure we're doing the right thing, and then we don't need a flag like this. If someone else in the process is using OpenSSL incorrectly, that's then their problem. cc: @bartonjs |
Issue that is tracking the work to enable more OSX is https://github.com/dotnet/core-eng/issues/3284 |
I'm starting this work, first I wanted to address the ones that the debugger showed that were explicit putting errors in the queue in order to reduce noise. I should have a ball-park estimate for the amount of work by tomorrow. We have to audit our calls but this is playing defense and having an option if something passes our combing. |
bartonjs
left a comment
There was a problem hiding this comment.
Aside from the style comments, I question the purpose.
If the purpose is: "Leaked error states cause exceptions to be thrown during successful cases, and this helps applications move onto 2.1", then OK.
If the purpose is "the wrong exception is thrown, but it was throwing anyways, this makes it be the right exception" then I don't know it's useful enough to warrant a) change, and b) maintenance of that change.
| extern "C" int32_t CryptoNative_SslRead(SSL* ssl, void* buf, int32_t num) | ||
| { | ||
| if (g_forceOpenSslErrorQueueCleanupBeforeWriteRead) | ||
| ERR_clear_error(); |
There was a problem hiding this comment.
Please add the explicit braces. The only things in this file (and probably most of this library) which use an unbraced if are stacked if->returns
| out int hashKeySize); | ||
|
|
||
| [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ForceOpenSslErrorQueueCleanupBeforeWriteRead")] | ||
| internal static extern void ForceOpenSslErrorQueueCleanupBeforeWriteRead(); |
There was a problem hiding this comment.
OpenSsl in the name is a bit redundant
| // Controls if ERR_clear_error is going to be called before SSL_write/SSL_read | ||
| static bool g_forceOpenSslErrorQueueCleanupBeforeWriteRead = false; | ||
|
|
||
| extern "C" void CryptoNative_ForceOpenSslErrorQueueCleanupBeforeWriteRead() |
There was a problem hiding this comment.
All of our extern functions should also be defined in the corresponding header.
There was a problem hiding this comment.
No problem, I will follow it for consistency, but I'm a bit curious about the purpose of it. Do you know why?
Yes, this is the issue being addressed here. We don't need this if we can be almost sure that we ourselves don't leave any errors on the queue. My initial count shows that we will have to review about 250 native calls, hopefully most of them should be trivial, but anyway this needs to be done carefully (both to not to regress perf nor lose error information). |
|
Failures on Ubuntu 14.04 per inspection of a random set seems related to sockets: @dotnet-bot test Outerloop Linux x64 Release Build |
dotnet#29186) * Add config to force OpenSsl error queue cleanup before Encrypt/Decrypt This is a escape valve in case the optimization of removing the calls to ERR_clear_error() for each SSL_write/SSL_read causes troubles. There will be a performance impact but depending on the scenario it can be a desired trade-off. Fixes #29188
…t/Decrypt (dotnet#29186)" This reverts commit 2750fdd.
dotnet/corefx#29186) * Add config to force OpenSsl error queue cleanup before Encrypt/Decrypt This is a escape valve in case the optimization of removing the calls to ERR_clear_error() for each SSL_write/SSL_read causes troubles. There will be a performance impact but depending on the scenario it can be a desired trade-off. Fixes dotnet/corefx#29188 Commit migrated from dotnet/corefx@2750fdd
…t/Decrypt (dotnet/corefx#29186)" (dotnet/corefx#29528) This reverts commit dotnet/corefx@2750fdd. Commit migrated from dotnet/corefx@3f5d26c
[EDIT: updating the names to the ones that were actually merged]
This is an escape valve in case the optimization of removing the calls to ERR_clear_error() for each SSL_write/SSL_read causes troubles. There will be a performance impact but depending on the scenario it can be a desired trade-off.
The AppContext switch is "System.Net.Security.SslStream.ForceClearOpenSslErrorQueue"
The env var is "DOTNET_SYSTEM_NET_SECURITY_SSLSTREAM_FORCECLEAROPENSSLERRORQUEUE"