2014-10-27 12 views
7

Sto riscontrando un problema con un codice che mantengo. Il codice seguente ha un membro private static SHA1 (che è un IDisposable ma poiché è static, non dovrebbe mai essere finalizzato). Tuttavia, in condizioni di stress questo codice genera un'eccezione che suggerisce che è stato chiuso:Perché SHA1.ComputeHash non riesce con un carico elevato con molti thread?

Caught exception. Safe handle has been closed" 
Stack trace: Call stack where exception was thrown 
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success) 
at System.Security.Cryptography.Utils.HashData(SafeHashHandle hHash, Byte[] data, Int32 cbData, Int32 ibStart, Int32 cbSize) 
at System.Security.Cryptography.Utils.HashData(SafeHashHandle hHash, Byte[] data, Int32 ibStart, Int32 cbSize) 
at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer) 

Il codice in questione è:

internal class TokenCache 
{ 
    private static SHA1 _sha1 = SHA1.Create(); 

    private string ComputeHash(string password) 
    { 
     byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password); 
     return UTF8Encoding.UTF8.GetString(_sha1.ComputeHash(passwordBytes)); 
    } 

La mia domanda è, ovviamente, quello che potrebbe causare questo problema. La chiamata a SHA1.Create può fallire in silenzio (quante risorse di crittografia sono disponibili)? Ciò potrebbe essere causato dall'appunta- mento della appdomain?

Altre teorie?

+0

Che cosa ha a che fare con lo smaltimento? Inoltre, quale classe "SHA1" è quella? –

+1

Sei sicuro che la classe SHA1 sia protetta da thread? Sei in grado di afferrare la password che viene sottoposta a hash quando fallisce? – Rob

+0

@John Saunders, mi spiace che tu abbia ragione. Questo non ha nulla a che fare con Dispose. Pensavo che il finalizzatore su System.Security.Cryptography.SHA1CryptoServiceProvider avrebbe potuto essere attivato in qualche modo. http://msdn.microsoft.com/en-us/library/e7hyyd4e(v=vs.110).aspx – MvdD

risposta

23

Come per the documentation per la classe di base HashAlgorithm

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

Si consiglia di non condividere queste classi tra i thread in cui diversi thread cercano e chiamano ComputeHash nella stessa istanza, allo stesso tempo.

EDIT Questo è ciò che sta causando il tuo errore. Lo stress test sottostante produce una varietà di errori dovuti a più thread che chiamano ComputeHash sulla stessa istanza dell'algoritmo di hash. Il tuo errore è uno di questi.

Specificamente, ho visto i seguenti errori con questo test di stress:

  • System.Security.Cryptography.CryptographicException: Hash non valido per l'utilizzo nello stato specificato.
  • System.ObjectDisposedException: maniglia di sicurezza è stato chiuso

stress codice di test di esempio:

const int threadCount = 2; 
var sha1 = SHA1.Create(); 
var b = new Barrier(threadCount); 
Action start =() => { 
        b.SignalAndWait(); 
        for (int i = 0; i < 10000; i++) 
        { 
         var pwd = Guid.NewGuid().ToString(); 
         var bytes = Encoding.UTF8.GetBytes(pwd); 
         sha1.ComputeHash(bytes); 
        } 
       }; 
var threads = Enumerable.Range(0, threadCount) 
         .Select(_ => new ThreadStart(start)) 
         .Select(x => new Thread(x)) 
         .ToList(); 
foreach (var t in threads) t.Start(); 
foreach (var t in threads) t.Join(); 
+0

Contrattempo notevole, la maniglia sicura sembra a posto. Ma innegabile, buona risposta. –

+1

@HansPassant. Grazie. Direi che potrebbe essere il metodo 'SHA1CryptoServiceProvider.Initialize', che sembra eseguire un' Dispose senza thread 'e quindi ricreare il campo '_safeHashHandle'. –

+0

Hehe, bizarro, Initialize() viene chiamato * dopo * calcolo dell'hash. Deve essere una specie di sicurezza. Avete capito bene. –

Problemi correlati