views:

135

answers:

2

Can anyone please identify is there any possible memory leaks in following code. I have tried with .Net Memory Profiler and it says "CreateEncryptor" and some other functions are leaving unmanaged memory leaks as I have confirmed this using Performance Monitors.

but there are already dispose, clear, close calls are placed wherever possible please advise me accordingly. its a been urgent.

public static string Encrypt(string plainText, string key)
    {
        //Set up the encryption objects
        byte[] encryptedBytes = null;
        using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(key)))
        {
            byte[] sourceBytes = Encoding.UTF8.GetBytes(plainText);
            using (ICryptoTransform ictE = acsp.CreateEncryptor())
            {
                //Set up stream to contain the encryption
                using (MemoryStream msS = new MemoryStream())
                {
                    //Perform the encrpytion, storing output into the stream
                    using (CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write))
                    {
                        csS.Write(sourceBytes, 0, sourceBytes.Length);
                        csS.FlushFinalBlock();

                        //sourceBytes are now encrypted as an array of secure bytes
                        encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer

                        csS.Close();
                    }

                    msS.Close();
                }
            }

            acsp.Clear();
        }

        //return the encrypted bytes as a BASE64 encoded string
        return Convert.ToBase64String(encryptedBytes);
    }
    private static AesCryptoServiceProvider GetProvider(byte[] key)
    {
        AesCryptoServiceProvider result = new AesCryptoServiceProvider();
        result.BlockSize = 128;
        result.KeySize = 256;
        result.Mode = CipherMode.CBC;
        result.Padding = PaddingMode.PKCS7;

        result.GenerateIV();
        result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        byte[] RealKey = GetKey(key, result);
        result.Key = RealKey;
        // result.IV = RealKey;
        return result;
    }

    private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
    {
        byte[] kRaw = suggestedKey;
        List<byte> kList = new List<byte>();

        for (int i = 0; i < p.LegalKeySizes[0].MaxSize; i += 8)
        {
            kList.Add(kRaw[(i / 8) % kRaw.Length]);
        }
        byte[] k = kList.ToArray();
        return k;
    }
+4  A: 

Update: After some more investigation I logged this as a bug on Microsoft connect. They have acknowledged the bug and have created a hotfix. (Obviously, this is a hotfix so the usual disclaimers apply. If you can, upgrading to .net 4.0 would probably be the preferred solution)


It seems that this code leaks in .net 3.5, but is works fine in .net 4.0.

I started in .net 4.0 and copied your code into a quick test app and called it 1,000,000 times, and the memory usage stayed constant at 22.4mb the whole time. I also tracked the GC heap sizes and handle counts, and they all stayed constant. As far as I can tell that code isn't leaking.

I then rebuilt the app under .net 3.5 and re-run the test and I got the exact leak you are describing. It started at around 24mb, and by the time it had made 100k calls, memory usage had doubled to over 50mb. Interestingly, it seemed to be the Gen2 heap that was increasing which suggests it is a managed memory leak rather than unmanaged handles/memory.

If possibly I would suggest you try to switch to .net 4.0.

My complete code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            String encryptedString;


            for (int j = 0; j < 1000; j++)
            {
                for (int i = 0; i < 1000; i++)
                {
                    encryptedString = Encrypt(String.Format("test string {0} {1}", j, i), "key");
                }
                Console.WriteLine("j = {0}", j);
            }

            Console.WriteLine("Finished");
            Console.ReadLine();

        }

        public static string Encrypt(string plainText, string key)
        {
            //Set up the encryption objects
            byte[] encryptedBytes = null;
            using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(key)))
            {
                byte[] sourceBytes = Encoding.UTF8.GetBytes(plainText);
                using (ICryptoTransform ictE = acsp.CreateEncryptor())
                {
                    //Set up stream to contain the encryption
                    using (MemoryStream msS = new MemoryStream())
                    {
                        //Perform the encrpytion, storing output into the stream
                        using (CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write))
                        {
                            csS.Write(sourceBytes, 0, sourceBytes.Length);
                            csS.FlushFinalBlock();

                            //sourceBytes are now encrypted as an array of secure bytes
                            encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer

                            csS.Close();
                        }

                        msS.Close();
                    }
                }

                acsp.Clear();
            }

            //return the encrypted bytes as a BASE64 encoded string
            return Convert.ToBase64String(encryptedBytes);
        }
        private static AesCryptoServiceProvider GetProvider(byte[] key)
        {
            AesCryptoServiceProvider result = new AesCryptoServiceProvider();
            result.BlockSize = 128;
            result.KeySize = 256;
            result.Mode = CipherMode.CBC;
            result.Padding = PaddingMode.PKCS7;

            result.GenerateIV();
            result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            byte[] RealKey = GetKey(key, result);
            result.Key = RealKey;
            // result.IV = RealKey;
            return result;
        }

        private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
        {
            byte[] kRaw = suggestedKey;
            List<byte> kList = new List<byte>();

            for (int i = 0; i < p.LegalKeySizes[0].MaxSize; i += 8)
            {
                kList.Add(kRaw[(i / 8) % kRaw.Length]);
            }
            byte[] k = kList.ToArray();
            return k;
        }

    }
}
Simon P Stevens
Thanks Stevens for extra effort. I would test it on 4.0
Mubashar Ahmad
Yes you are right its working on 4.0 so does this mean its a bug of framework 3.5???
Mubashar Ahmad
Well as there isn't any solution for this in netfx3.5 so marking it as answer, Thanks Simon for help.
Mubashar Ahmad
Thanks for strong follow up and Backing up :) Jazak-um-Allah (God bless you)
Mubashar Ahmad
A: 

Not really trying to hijack a thread but what is the major difference between your encrypt and this one?


/// 
        /// Encrypts a string
        /// 
        /// Text to be encrypted
        /// Password to encrypt with
        /// Salt to encrypt with
        /// Can be either SHA1 or MD5
        /// Number of iterations to do
        /// Needs to be 16 ASCII characters long
        /// Can be 128, 192, or 256
        /// An encrypted string
        public static string Encrypt(string PlainText, string Password,
            string Salt, string HashAlgorithm,
            int PasswordIterations, string InitialVector,
            int KeySize)
        {
            try
            {
                if (string.IsNullOrEmpty(PlainText))
                    return "";
                byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
                byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
                byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
                PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
                byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
                RijndaelManaged SymmetricKey = new RijndaelManaged();
                SymmetricKey.Mode = CipherMode.CBC;
                byte[] CipherTextBytes = null;
                using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
                {
                    using (MemoryStream MemStream = new MemoryStream())
                    {
                        using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
                        {
                            CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                            CryptoStream.FlushFinalBlock();
                            CipherTextBytes = MemStream.ToArray();
                            MemStream.Close();
                            CryptoStream.Close();

                            CryptoStream.Dispose();
                            MemStream.Dispose();
                        }
                    }
                    Encryptor.Dispose();
                }
                SymmetricKey.Clear();
                return Convert.ToBase64String(CipherTextBytes);
            }
            catch { throw; }
        }

I ran this one under 3.5 and it didn't budge past 22mb. My crypto skills are a little thin but I'm just wondering overall why one is better than the other, or if it is. Seems alot of ways to do the same things.

Quayludious