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

Crash on WindowsCryptographicException on net60 GA #61417

Closed
theolivenbaum opened this issue Nov 10, 2021 · 20 comments
Closed

Crash on WindowsCryptographicException on net60 GA #61417

theolivenbaum opened this issue Nov 10, 2021 · 20 comments
Assignees

Comments

@theolivenbaum
Copy link

Description

System.AggregateException: One or more errors occurred. (Unknown error (0xc1000008))
---> Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Unknown error (0xc1000008)
at Internal.Cryptography.HashProviderCng.AppendHashData(ReadOnlySpan`1 source)
at Internal.Cryptography.HashProvider.AppendHashData(Byte[] data, Int32 offset, Int32 count)
at System.Security.Cryptography.SHA256.Implementation.HashCore(Byte[] array, Int32 ibStart, Int32 cbSize)
at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer)
at Host.CLI.Program.<>c__DisplayClass9_2.<b__3>d.MoveNext() in C:\work\curiosity\curiosity-ai-website\tools\host\Program.cs:line 280
--- End of stack trace from previous location ---
at Host.CLI.Program.BuildAsync(String path, String buildPath, String templatePath, String translationPath) in C:\work\curiosity\curiosity-ai-website\tools\host\Program.cs:line 296
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Host.CLI.Delayed.g__CheckBacklog|5_1() in C:\work\curiosity\curiosity-ai-website\tools\host\Delayed.cs:line 71
at Host.CLI.Delayed.<>c.b__5_0(Object _) in C:\work\curiosity\curiosity-ai-website\tools\host\Delayed.cs:line 49
at System.Threading.TimerQueueTimer.<>c.<.cctor>b__27_0(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.TimerQueueTimer.CallCallback(Boolean isThreadPool)
at System.Threading.TimerQueueTimer.Fire(Boolean isThreadPool)
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.TimerQueue.AppDomainTimerCallback(Int32 id)

Reproduction Steps

The app in question is a simple web host, accessing the page via the edge browser.

Expected behavior

No crash

Actual behavior

Process crashes hard with an uncaught exception

Regression?

Yes. The issue never happen till I installed the net60 GA SDK installed this morning.

Known Workarounds

No response

Configuration

Window x64

dotnet --list-sdks

2.1.526 [C:\Program Files\dotnet\sdk]
2.1.617 [C:\Program Files\dotnet\sdk]
2.1.701 [C:\Program Files\dotnet\sdk]
2.1.818 [C:\Program Files\dotnet\sdk]
2.2.101 [C:\Program Files\dotnet\sdk]
2.2.301 [C:\Program Files\dotnet\sdk]
3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk]
3.0.100 [C:\Program Files\dotnet\sdk]
3.1.200 [C:\Program Files\dotnet\sdk]
3.1.301 [C:\Program Files\dotnet\sdk]
3.1.415 [C:\Program Files\dotnet\sdk]
5.0.104 [C:\Program Files\dotnet\sdk]
5.0.209 [C:\Program Files\dotnet\sdk]
5.0.303 [C:\Program Files\dotnet\sdk]
5.0.403 [C:\Program Files\dotnet\sdk]
6.0.100-rc.1.21458.32 [C:\Program Files\dotnet\sdk]
6.0.100 [C:\Program Files\dotnet\sdk]  <---- App is running on this now

Other information

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Security untriaged New issue has not been triaged by the area owner labels Nov 10, 2021
@ghost
Copy link

ghost commented Nov 10, 2021

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

Issue Details

Description

System.AggregateException: One or more errors occurred. (Unknown error (0xc1000008))
---> Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Unknown error (0xc1000008)
at Internal.Cryptography.HashProviderCng.AppendHashData(ReadOnlySpan`1 source)
at Internal.Cryptography.HashProvider.AppendHashData(Byte[] data, Int32 offset, Int32 count)
at System.Security.Cryptography.SHA256.Implementation.HashCore(Byte[] array, Int32 ibStart, Int32 cbSize)
at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer)
at Host.CLI.Program.<>c__DisplayClass9_2.<b__3>d.MoveNext() in C:\work\curiosity\curiosity-ai-website\tools\host\Program.cs:line 280
--- End of stack trace from previous location ---
at Host.CLI.Program.BuildAsync(String path, String buildPath, String templatePath, String translationPath) in C:\work\curiosity\curiosity-ai-website\tools\host\Program.cs:line 296
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Host.CLI.Delayed.g__CheckBacklog|5_1() in C:\work\curiosity\curiosity-ai-website\tools\host\Delayed.cs:line 71
at Host.CLI.Delayed.<>c.b__5_0(Object _) in C:\work\curiosity\curiosity-ai-website\tools\host\Delayed.cs:line 49
at System.Threading.TimerQueueTimer.<>c.<.cctor>b__27_0(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.TimerQueueTimer.CallCallback(Boolean isThreadPool)
at System.Threading.TimerQueueTimer.Fire(Boolean isThreadPool)
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.TimerQueue.AppDomainTimerCallback(Int32 id)

Reproduction Steps

The app in question is a simple web host, accessing the page via the edge browser.

Expected behavior

No crash

Actual behavior

Process crashes hard with an uncaught exception

Regression?

Yes. The issue never happen till I installed the net60 GA SDK installed this morning.

Known Workarounds

No response

Configuration

Window x64

dotnet --list-sdks

2.1.526 [C:\Program Files\dotnet\sdk]
2.1.617 [C:\Program Files\dotnet\sdk]
2.1.701 [C:\Program Files\dotnet\sdk]
2.1.818 [C:\Program Files\dotnet\sdk]
2.2.101 [C:\Program Files\dotnet\sdk]
2.2.301 [C:\Program Files\dotnet\sdk]
3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk]
3.0.100 [C:\Program Files\dotnet\sdk]
3.1.200 [C:\Program Files\dotnet\sdk]
3.1.301 [C:\Program Files\dotnet\sdk]
3.1.415 [C:\Program Files\dotnet\sdk]
5.0.104 [C:\Program Files\dotnet\sdk]
5.0.209 [C:\Program Files\dotnet\sdk]
5.0.303 [C:\Program Files\dotnet\sdk]
5.0.403 [C:\Program Files\dotnet\sdk]
6.0.100-rc.1.21458.32 [C:\Program Files\dotnet\sdk]
6.0.100 [C:\Program Files\dotnet\sdk]  <---- App is running on this now

Other information

No response

Author: theolivenbaum
Assignees: -
Labels:

area-System.Security, untriaged

Milestone: -

@theolivenbaum
Copy link
Author

If it helps, here is a crash dump

@vcsjones
Copy link
Member

vcsjones commented Nov 10, 2021

A few questions that might help tracking down the issue:

  1. Does this happen repeatedly, or was it a one-off crash? What about on multiple machines?

  2. Is it specific to your application, or does attempting to hash something in a brand-new application cause an exception as well? e.g. can you run this in a console application?

    using System;
    using System.Security.Cryptography;
    
    using SHA256 sha = SHA256.Create();
    Console.WriteLine(Convert.ToHexString(sha.ComputeHash(new byte[32])));

    If it crashes in your application, but not the sample above, is it possible for you reduce your application code to sample code that does reproduce the issue?

  3. What version of Windows is this? (you can run winver to get the exact version).

@jaylagorio
Copy link

@vcsjones It looks like I'm running into the same thing, also after an update to net60 after the app worked fine in net50. It's intermittent and unpredictable, and in my case it's in a 3rd-party library but with a similar upper portion of the stack trace:

at Internal.Cryptography.HashProviderCng.AppendHashData(ReadOnlySpan`1 source)
   at Internal.Cryptography.HashProvider.AppendHashData(Byte[] data, Int32 offset, Int32 count)
   at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer)
   at Kraken.Net.KrakenAuthenticationProvider.AddAuthenticationToHeaders(String uri, HttpMethod method, Dictionary`2 parameters, Boolean signed, HttpMethodParameterPosition parameterPosition, ArrayParametersSerialization arraySerialization)

Windows 10 v21H2 (OS Build 19044.1348)

@vcsjones
Copy link
Member

A few guesses, since I don't have a lot to go on.

  1. Assuming it is this project: https://github.com/JKorf/Kraken.Net/
  2. And it's throwing at this line: https://github.com/JKorf/Kraken.Net/blob/0802f64af669bd77209218517063303750aadc79/Kraken.Net/KrakenAuthenticationProvider.cs#L59

I suspect it is might be because the HMACSHA512 encryptor field is being accessed from multiple threads. Instances of HMACSHA512 are not thread safe.

I can reproduce this exception by using an HMACSHA512 instance between threads:

using System;
using System.Threading;
using System.Security.Cryptography;

using HMACSHA512 hmac = new HMACSHA512();
Thread thread1 = new Thread(static obj => {
    while (true)
    {
        ((HMACSHA512)obj).ComputeHash(new byte[1]);
    }
});
Thread thread2 = new Thread(static obj => {
    while (true)
    {
        ((HMACSHA512)obj).ComputeHash(new byte[1]);
    }
});

thread1.Start(hmac);
thread2.Start(hmac);
thread1.Join();
thread2.Join();

That will quickly give me the same issue as described in the original post.

At this point I can only suggest that you confirm with the library author that no instance of a hash or HMAC algorithm is being used by two or more threads, concurrently.

If that is indeed the case, I would recommend creating a new instance of the hash / HMAC algorithm per-thread, or just always as-needed. Alternatively, using the HMACXYZ.HashData one-shot static methods that were introduced in .NET 5 for digests, and .NET 6 for HMACs.

@theolivenbaum
Copy link
Author

Hi @vcsjones thanks for the suggestion, I'll investigate if this is the case on my side. It should have been a single thread accessing it, but I'll double check it

@bartonjs bartonjs added needs more info and removed untriaged New issue has not been triaged by the area owner labels Dec 2, 2021
@ghost
Copy link

ghost commented Dec 2, 2021

This issue has been marked needs more info since it may be missing important information. Please refer to our contribution guidelines for tips on how to report issues effectively.

@alanmcgovern
Copy link
Contributor

alanmcgovern commented Dec 4, 2021

@vcsjones I have a repro case for a similar, if not identical, issue:

This breaks:

var buffer = new byte[1024];
var hasher = SHA1.Create ();
hasher.TransformBlock (buffer, 0, buffer.Length, buffer, 0);
//hasher.TransformFinalBlock (Array.Empty<byte> (), 0, 0);
hasher.Initialize ();
hasher.TransformBlock (buffer, 0, buffer.Length, buffer, 0);
hasher.TransformFinalBlock (Array.Empty<byte> (), 0, 0);
GC.KeepAlive (hasher.Hash);
hasher.Initialize ();
hasher.TransformBlock (buffer, 0, buffer.Length, buffer, 0);

If you want things to break in .NET6 just leave the 4th line commented. If you want it to not break, uncomment line 4.

If you re-initialize a hasher after calling TransformBlock and before calling TransformFinalBlock, you corrupt the hasher and it fails.

EDIT: Thanks to @EvgeniyZ-ru for bringing this issue to my attention, and working with me to provide the information I needed to distill a testcase :)

@alanmcgovern
Copy link
Contributor

alanmcgovern commented Dec 4, 2021

I took a few minutes and reduced it a bit further:

var buffer = new byte[1024];
var hasher = SHA1.Create ();
hasher.TransformBlock (buffer, 0, buffer.Length, buffer, 0);
hasher.Initialize ();
hasher.TransformFinalBlock (Array.Empty<byte> (), 0, 0);
hasher.Initialize ();
hasher.TransformFinalBlock (Array.Empty<byte> (), 0, 0);

alanmcgovern added a commit to alanmcgovern/monotorrent that referenced this issue Dec 4, 2021
If you call Initialize on a hash algorithm, after you've invoked
TransformBlock but before you've invoked TransformFinalBlock, the
instance corrupts and mysteriously fails to transform blocks later
on.

Works around dotnet/runtime#61417
Addresses #483
@vcsjones vcsjones self-assigned this Dec 4, 2021
@vcsjones
Copy link
Member

vcsjones commented Dec 4, 2021

Thanks for the test case, I can reproduce it now. I will look at this now.

@vcsjones
Copy link
Member

vcsjones commented Dec 4, 2021

I have a PR open to address the issue uncovered by @alanmcgovern's test case.

This won't fix @jaylagorio's reported instance - sharing an instance between threads will still lead to the same issue of the CNG object getting in to an incorrect state.

This particular issue is new in .NET 6 because prior to .NET 6, the Initialize was a no-op. See #51172 for additional background.

@theolivenbaum can you confirm if this is the issue you were running in to as well? Are you using Initialize anywhere in your code?

@vcsjones
Copy link
Member

vcsjones commented Dec 4, 2021

I just noticed on @theolivenbaum's stack System.AggregateException.

This to me implies a possibility that the hash object is being used concurrently from different Tasks.

It's not definitive evidence, but a possibility.

@jaylagorio
Copy link

@vcsjones Thanks so much for looking into this and thanks even more for submitting a PR to the library I'm using! Fingers crossed for a new release soon.

@theolivenbaum
Copy link
Author

Not sure if related @vcsjones, but I'm also using SHA256.ComputeHash(...), and it throws randomly this:

SafeHandle cannot be null. (Parameter 'pHandle')
   at System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument, ExceptionResource resource)
   at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
   at Interop.BCrypt.BCryptHashData(SafeBCryptHashHandle hHash, Byte& pbInput, Int32 cbInput, Int32 dwFlags)
   at Internal.Cryptography.HashProviderCng.AppendHashData(ReadOnlySpan`1 source)
   at Internal.Cryptography.HashProvider.AppendHashData(Byte[] data, Int32 offset, Int32 count)
   at System.Security.Cryptography.SHA256.Implementation.HashCore(Byte[] array, Int32 ibStart, Int32 cbSize)
   at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer)
   at Host.CLI.Program.<>c__DisplayClass9_2.<<BuildAsync>b__3>d.MoveNext() in C:\tldo\website\tools\host\Program.cs:line 280
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Host.CLI.Program.<BuildAsync>d__9.MoveNext() in .\host\Program.cs:line 296

@theolivenbaum
Copy link
Author

The code is approximately this:

var files = new []{ "path to file", ... }; //list of files
await Task.WhenAll(files.Select(async f => await DoSomethingWithFile(f, Sha256.ComputeHash(await File.ReadAllBytesAsync(f)))));

@theolivenbaum
Copy link
Author

It also throws this on subsequent calls:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Unknown error (0xc1000008)
   at Internal.Cryptography.HashProviderCng.AppendHashData(ReadOnlySpan`1 source)
   at Internal.Cryptography.HashProvider.AppendHashData(Byte[] data, Int32 offset, Int32 count)
   at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer)

@vcsjones
Copy link
Member

vcsjones commented Dec 7, 2021

The code is approximately this:

var files = new []{ "path to file", ... }; //list of files
await Task.WhenAll(files.Select(async f => await DoSomethingWithFile(f, Sha256.ComputeHash(await File.ReadAllBytesAsync(f)))));

It looks like you are using an instance of SHA256 concurrently. You have a property, field, or local instance named Sha256 that is concurrently (Task.WhenAll) computing the file hashes.

If you are targeting net5.0 or higher, you can change it to this:

var files = new []{ "path to file", ... }; //list of files
await Task.WhenAll(files.Select(async f => await DoSomethingWithFile(f, SHA256.HashData(await File.ReadAllBytesAsync(f)))));

HashData was introduced in .NET 5. It is a static method and it is thread safe.

@theolivenbaum
Copy link
Author

True, I thought I was already using the static method 🤦‍♂️

@vcsjones
Copy link
Member

To round things out here:

  1. The issue reported by @theolivenbaum and @jaylagorio was due to misusing an instance between threads. @theolivenbaum did switching to the static one-shot alleviate your exceptions?
  2. The issue reported by @alanmcgovern was fixed, and has been backported to .NET 6. I believe the fix will be in the 6.0.2 release.

@theolivenbaum
Copy link
Author

Hi @vcsjones, I've not observed the issue anymore! Nice to hear it uncovered another issue nevertheless! Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants