我目前正在尝试追踪C ++服务中的泄漏,该服务加载了使用windbg加载.NET库的VB6模块,截至目前,感觉我已经非常接近答案,但似乎无法锁定它。
从!heap -s开始,我确定了有问题的堆:
0:000> !heap -s
LFH Key : 0x48d335eb
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 03d20000 - 03d20000 (size 00000000)
007a0000 00000002 1457472 1447856 1457472 7003 1888 94 1 e LFH
鉴于此,我确定了堵塞它的原因:
0:000> !heap -stat -h 007a0000
heap @ 007a0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
298 cb166 - 20ec2090 (76.28)
启用gflags后,我会识别并调整分配的调用方式:
0:000> !heap -flt s 298
<snip>
604473b8 0055 0055 [00] 604473c0 00298 - (busy)
<snip>
0:000> !heap -p -a 604473c0
address 604473c0 found in
_HEAP @ 007a0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
604473b8 0059 0000 [00] 604473c0 00298 - (busy)
772a34e5 ntdll!RtlAllocateHeap+0x0000021d
74c54568 rsaenh!ContAlloc+0x00000017
74c7aa8f rsaenh!CopyKey+0x00000030
74c7abd4 rsaenh!CPDuplicateKey+0x0000008b
75584c4b advapi32!CryptDuplicateKey+0x0000007c
一旦我知道分配来自对CryptDuplicateKey的调用,我就会在这个调用的地方进行搜索。我们从不在代码中直接调用它,因此我再次使用WinDBG来确定如何调用它。
0:198> bp advapi32!CryptDuplicateKey+0x0000007c
0:198> g
Breakpoint 0 hit
eax=212bc380 ebx=22222222 ecx=26d0ddc8 edx=74c7ab49 esi=2f90bc10 edi=00000001
eip=75584c48 esp=26d0dd9c ebp=26d0dde8 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ADVAPI32!CryptDuplicateKey+0x79:
75584c48 ff5624 call dword ptr [esi+24h] ds:002b:2f90bc34={rsaenh!CPDuplicateKey (74c7ab49)}
0:034> !clrstack
OS Thread Id: 0x12f24 (34)
ESP EIP
26d0de40 75584c48 [NDirectMethodFrameStandaloneCleanup: 26d0de40] System.Security.Cryptography.CapiNative+UnsafeNativeMethods.CryptDuplicateKey(Microsoft.Win32.SafeHandles.SafeCapiKeyHandle, IntPtr, Int32, Microsoft.Win32.SafeHandles.SafeCapiKeyHandle ByRef)
26d0de58 742aa535 Microsoft.Win32.SafeHandles.SafeCapiKeyHandle.Duplicate()
26d0de68 742ac382 System.Security.Cryptography.CapiSymmetricAlgorithm..ctor(Int32, Int32, Microsoft.Win32.SafeHandles.SafeCspHandle, Microsoft.Win32.SafeHandles.SafeCapiKeyHandle, Byte[], System.Security.Cryptography.CipherMode, System.Security.Cryptography.PaddingMode, System.Security.Cryptography.EncryptionMode)
26d0de98 742ab5b2 System.Security.Cryptography.AesCryptoServiceProvider.CreateDecryptor(Microsoft.Win32.SafeHandles.SafeCapiKeyHandle, Byte[])
26d0deb4 742ab521 System.Security.Cryptography.AesCryptoServiceProvider.CreateDecryptor(Byte[], Byte[])
26d0def0 1097028b CryptoLib.CryptoObject.DecryptMessage(System.String, System.String ByRef)
由于CryptoLib是我们的代码,我现在已经准备好泄漏的来源。显然,CreateDecryptor正在调用DuplicateKey,它正在泄漏大小为298的分配,所以我的思考过程是我们可能没有正确调用dispose。但是,当我在CryptoLib中检查这个区域的实现时,我发现:
using (var aes = new AesCryptoServiceProvider()) {
aes.Key = vbKey
aes.IV = vbIV
byte[] decryptBuffer;
using (var mstream = new MemoryStream()) {
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) {
using (var cstream = new CryptoStream(mstream, decryptor, CryptoStreamMode.Write) { {
cstream.Write(messagebuffer, 0, messagebuffer.Length);
cstream.FlushFinalBlock();
}
}
decryptBuffer = mstream.ToArray();
}
decryptedMessage = Encoding.GetEncoding(1252).GetString(decryptBuffer);
}
展望CreateDecryptor,看起来这一切都应该正确处理,然而,应用程序仍在从CreateDecryptor调用中收集1.1 GB的垃圾。
查看documentation for this,实现似乎是正确的,我似乎无法确定这些本机对象泄漏的原因。
图书馆另有表现,我只是无法弄清楚为什么这些对象没有处理掉。