使用加密后,在终结器线程中获取"ReleaseHandleFailed"MDA

Tam*_*lei 4 c# cryptography

我在循环中第二次运行此代码后获得了MDA (使用不同的file参数:

byte[] encryptedData = File.ReadAllBytes(file); // before this line it throws, see exception below
long dataOffset;

using (var stream = new MemoryStream(encryptedData))
using (var reader = new BinaryReader(stream))
{
    // ... read header information which is not encrypted
}


using (var stream = new MemoryStream(encryptedData))
{
    stream.Seek(dataOffset, SeekOrigin.Begin);

    using (var aesAlg = new AesCryptoServiceProvider())
    using (var decryptor = aesAlg.CreateDecryptor(key, iv))
    using (var csDecrypt = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
    using (var reader = new BinaryReader(csDecrypt))
    {
        decrypted = reader.ReadBytes((int)(encryptedData.Length - dataOffset));
    }
}
Run Code Online (Sandbox Code Playgroud)

MDA如下:

"Microsoft.Win32.SafeHandles.SafeCapiKeyHandle"类型的SafeHandle或CriticalHandle无法正确释放值为0x000000001BEA9B50的句柄.这通常表示通过其他方式错误地释放了句柄(例如使用DangerousGetHandle提取句柄并直接关闭它或在其周围构建另一个SafeHandle.)

堆栈跟踪没有太多信息:

mscorlib.dll!System.Runtime.InteropServices.SafeHandle.Dispose(bool disposing)+ 0x10 bytes mscorlib.dll!System.Runtime.InteropServices.SafeHandle.Finalize()+ 0x1a bytes

我怀疑其中一个流或CryptoServiceProvider由于某种原因未发布.除此之外,代码运行良好,并做到了预期.MDA在控件到达方法的第一行之前发生.

我怎么能正确地做到这一点?问题的根本原因是什么?

Han*_*ant 7

很明显,终结器线程正在最终确定已经处理好的SafeHandle.这是AesCryptoServiceProvider.Dispose(bool)方法的实现:

protected override void Dispose(bool disposing)
{
    try {
        if (disposing) {
            if (this.m_key != null) this.m_key.Dispose();
            if (this.m_cspHandle != null) this.m_cspHandle.Dispose();
        }
    }
    finally {
        base.Dispose(disposing);
    }
}
Run Code Online (Sandbox Code Playgroud)

三个错误:

  • 处理后,它不会将m_key字段设置为null
  • GC.SuppressFinalize()不会被调用,甚至不会被.NET 3.5中的基类调用
  • SafeCapiKeyHandle类不会使其ReleaseHandle()方法中存储的句柄无效.

所有三个错误的组合足以触发此MDA.它仍然在.NET 4.0中被窃听,但至少GC.SuppressFinalize由SymmetricAlgorithm.Dispose(bool)调用,因此不会使用终结器.

鼓励看到框架大师搞砸了.您可以在connect.microsoft.com上报告问题.要阻止调试器唠叨这一点,请使用Debug + Exceptions,Managed Debugging Assistants,取消ReleaseHandleFailed.默认情况下,这个是未被攻击的,这肯定是你第一个注意到这个bug的原因.

我认为第三个错误使这成为一个关键问题顺便说一句,从技术上讲,这个错误可能会导致循环句柄值被关闭.虽然赔率非常小.相当讽刺的是,鉴于这是一个安全的句柄类.