kmaragon / Konscious.Security.Cryptography

MIT License
212 stars 20 forks source link

Getting OutOfMemory-Exception after generating about 6000 Argon2i hashes #12

Closed marcells closed 7 years ago

marcells commented 7 years ago

Hi,

I'm generating Argon2i hashes in a loop. After about 6000 passwords the GetBytes(int) method throws an OutOfMemoryException. The hashes are written to a database immediately. So there's no other state or memory consumed in the application.

Here's the exception:

System.AggregateException occurred
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=mscorlib
  StackTrace:
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at Konscious.Security.Cryptography.Argon2.GetBytes(Int32 bc)
   at WorkflowEngine.Common.Static.Argon2PasswordHasher.Hash(String password, Byte[] salt) in C:\SomeCode.cs:line 27

And the encapsulated inner exception:

"Exception of type 'System.OutOfMemoryException' was thrown."
   at Konscious.Security.Cryptography.Argon2Core.<InitializeLanes>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Konscious.Security.Cryptography.Argon2Core.<Hash>d__27.MoveNext()

I'm using this method. (salt is of size 16)

public static byte[] Hash(string password, byte[] salt)
{
    var passwordBytes = Encoding.UTF8.GetBytes(password);
    using (var argon2 = new Argon2i(passwordBytes)
                        {
                            DegreeOfParallelism = 16,
                            MemorySize = 8192,
                            Iterations = 40,
                            Salt = salt
                        })
    {
        return argon2.GetBytes(128);
    }
}
kmaragon commented 7 years ago

Not that it should matter but what OS is this on? I can't imagine how there could be a leak from this code in light of the fact that there are no statics in the library. But I wonder also how quickly you're calling this in succession.

Thanks

kmaragon commented 7 years ago

Never mind. I found the issue

marcells commented 7 years ago

Thanks a lot for the quick fix. :-) I'll check it today.

marcells commented 7 years ago

It looks good. The issue is resolved! Thanks again! :+1:

kmaragon commented 7 years ago

Thanks for finding that! Memory leaks in a .NET app :-1:

vveena12 commented 1 year ago

Hi,

I am using Argon2i hashing for storing user password. Thus, also using it while doing login into application. When many users simultanesously login into system, the GetBytes(int) method throws an OutOfMemoryException.

For identifying the problem, I did hashing parallerly on a thread. After about 35 or 40 occurrances, it starts throwing OutOfMemoryException. Below is my test code.

            System.Threading.Tasks.Parallel.For(0, 42, i =>
            {
                int a = cnt;

                byte[] buf = new byte[10];
                (new RNGCryptoServiceProvider()).GetBytes(buf);
                var salstr = Convert.ToBase64String(buf);

                var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password));

                argon2.Salt = Convert.FromBase64String(saltstr);
                argon2.DegreeOfParallelism = 8;
                argon2.Iterations = 2;
                argon2.MemorySize = 256000;

                var hash = argon2.GetBytes(32);
            });

Here's the exception:

--- Message: One or more errors occurred. --- Stack Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result() at Konscious.Security.Cryptography.Argon2.GetBytes(Int32 bc) in C:\projects\konscious-security-cryptography\Konscious.Security.Cryptography.Argon2\Argon2.cs:line 40 at ArgonHashingTest.KonsciousHash.<>cDisplayClass1_0.b0(Int32 i) in C:\Users\veenakh\source\repos\ArgonHashingTest\ArgonHashingTest\KonsciousHash.cs:line 107 at System.Threading.Tasks.Parallel.<>cDisplayClass17_0`1.b1() at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask) at System.Threading.Tasks.Task.<>c__DisplayClass176_0.b__0(Object )

--- Message: Exception of type 'System.OutOfMemoryException' was thrown. --- Stack Trace: at Konscious.Security.Cryptography.Argon2Lane..ctor(Int32 blockCount) in C:\projects\konscious-security-cryptography\Konscious.Security.Cryptography.Argon2\Argon2Lane.cs:line 9 at Konscious.Security.Cryptography.Argon2Core.d32.MoveNext() in C:\projects\konscious-security-cryptography\Konscious.Security.Cryptography.Argon2\Argon2Core.cs:line 163 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Konscious.Security.Cryptography.Argon2Core.d27.MoveNext() in C:\projects\konscious-security-cryptography\Konscious.Security.Cryptography.Argon2\Argon2Core.cs:line 34 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Konscious.Security.Cryptography.Argon2.<>c__DisplayClass2_0.<b__0>d.MoveNext() in C:\projects\konscious-security-cryptography\Konscious.Security.Cryptography.Argon2\Argon2.cs:line 40

SparkDustJoe commented 1 year ago

For everyone's benefit on this issue, I'll post my response from #56 (the values referenced are from @vveena12 's specific issue) "Is it possible that you're using settings that are just aggressively consuming memory? If I'm remembering correctly from the standard (C++ implementation) that Parallelism/Lanes uses the Memory Size TIMES Lanes, AND Memory Size is in Kibibytes, so in your case, 256000kib X 8 Lanes. If I'm understanding the parameters correctly, that's approx. 1.95Gibibytes PER INSTANCE! @kmaragon am I remembering that correctly? Based on a cursory look through the code, it's an array of 256000 (your value) * 128 ulong's (64 bits each), which is 250Mibibytes right there, then 8 lanes of that (based on the initialization of the ArgonLane class). If 45 people rapidly log in and memory isn't immediately released by the Garbage Collector, that's up to 88Gibibytes right there depending on how far apart they log in.

NOTE: Changing any parameters WILL change the resulting hash result, so that would cause future login attempts by established users to fail!"