Heap corruption - кто виноват?
От: LWhisper  
Дата: 18.08.17 17:06
Оценка:
Всем привет, нужна помощь знающих камрадов.

Проблема:
Корраптится куча, приложение падает.

Инфраструктура:
Железо: Виртуальная машина VMware на двух независимых ESXi с якобы живой и неповрежденной памятью
ОС: Microsoft Windows Server 2016 Standard (10.0.14393)
Процесс: Windows-служба x64 под .NET 4.5.2, запущенная под системной учёткой
Отсутствуют антивирусы и подозрительные драйверы.

Падения происходят раз в несколько дней. Традиционно для коррапта кучи — в произвольных местах, при обращении к памяти.
Проблема не поддаётся воспроизведению и стреляет у одного человека из десятков тысяч.

Поковырял WinDbg, куча действительно повреждена:

0:027> !VerifyHeap
object 0000021701b6f728: bad member 0000021701B7C9F8 at 0000021701B6F738
Last good object: 0000021701B6F710.
...
Could not request method table data for object 0000021701B7C9F8 (MethodTable: 39E70380864F9B7C).
Last good object: 0000021701B7C9D0.


Выяснил, что жертвой стали элементы одного из массивов:

> !DumpArray /d 0000021701942910
Name:        RootNamespace.FileSession[]
MethodTable: 00007ff87d5d6de0
EEClass:     00007ff8d8498af0
Size:        272(0x110) bytes
Array:       Rank 1, Number of elements 31, Type CLASS
Element Methodtable: 00007ff87b627750
[0] 0000021701bd0660
[1] 0000021701bd06a8
[2] 0000021701bd06f0
[3] 0000021701bd0738

> dq 0000021701942910
00000217`01942910  00007ff8`7d5d6de0 00000000`0000001f
00000217`01942920  00000217`01bd0660 00000217`01bd06a8
00000217`01942930  00000217`01bd06f0 00000217`01bd0738
00000217`01942940  00000217`01bd0780 00000217`01bd07c8
00000217`01942950  00000217`01bd0810 00000217`01bd0858
00000217`01942960  00000217`01bd08a0 00000217`01bd0900
00000217`01942970  00000217`01bd0948 00000217`01bd0990
00000217`01942980  00000217`01bd09d8 00000217`01bd0a20

Вначале идёт таблица методов, количество элементов (31 штука) и далее указатели на элементы массива.

Посмотрим что лежит на месте первого элемента:
> !objsize 00000217`01bd0660 
sizeof(0000021701bd0660) = 72 (0x48) bytes (RootNamespace.FileSession)

> dq 00000217`01bd0660 L9
00000217`01bd0660  00007ff8`7b627750 00000000`00000001
00000217`01bd0670  486b85bd`e6c79316 c3d8f969`c90b70ba
00000217`01bd0680  4b059f8e`1e4ddd83 a9c00cf5`7d3ca0a7
00000217`01bd0690  46de7a50`833a6808 6dfea71f`5f3810b8
00000217`01bd06a0  00000000`00000000



Теперь плохой массив:
!DumpArray /d 0000021701b6f728
Name:        RootNamespace.FileSession[]
MethodTable: 00007ff87d5d6de0
EEClass:     00007ff8d8498af0
Size:        272(0x110) bytes
Array:       Rank 1, Number of elements 31, Type CLASS
Element Methodtable: 00007ff87b627750
[0] 0000021701b7c9f8
[1] 0000021701b7ca40
[2] 0000021701b7ca88
[3] 0000021701b7cad0


И его первый элемент:
> dq 0000021701b7c9f8 L9
00000217`01b7c9f8  39e70380`864f9b7e 64017283`9c955ef7
00000217`01b7ca08  e7694b13`d6b9417a 7049840e`f4aaffbd
00000217`01b7ca18  37a6b685`02f4fade da40d413`1f15d547
00000217`01b7ca28  f47180d7`d905900a 67649a05`b2eb5deb
00000217`01b7ca38  0d708b24`098c06b4


По адресу 00000217`01b7c9f8 лежит не элемент массива, а обыкновенный мусор.

Но если мы отмотаем память немного назад, то обнаружим...

00000217`01b7c9e0  00000000`00000000 00000002`00000002
00000217`01b7c9f0  07745b33`00000000 39e70380`864f9b7e
00000217`01b7ca00  64017283`9c955ef7 e7694b13`d6b9417a
00000217`01b7ca10  7049840e`f4aaffbd 37a6b685`02f4fade
00000217`01b7ca20  da40d413`1f15d547 f47180d7`d905900a
00000217`01b7ca30  67649a05`b2eb5deb 0d708b24`098c06b4
00000217`01b7ca40  ce6ca3df`a30a02f1 9d9f32ae`eaa97bb7
00000217`01b7ca50  36b0ac8a`91d534b1 f58dbd9a`2c6c8055
00000217`01b7ca60  c2b5f5c5`d2d982c5 d87fcea8`953f75a3
00000217`01b7ca70  a1f50c94`313b216a 2651d93a`7f1b2eb0
00000217`01b7ca80  b42d9e5f`265a92d4 d8bbde2c`7c5b1071
00000217`01b7ca90  efae2f4d`07e993e6 998584a8`44f82ef3
00000217`01b7caa0  85a5e61d`7d80cae7 1703c807`bf3c62a4
00000217`01b7cab0  2a1677d9`30847e64 75d48062`37544d6c
00000217`01b7cac0  ee2e1bcf`c2f920aa 84fa15c7`817649aa
00000217`01b7cad0  fa891630`c36247b3 cfb37b15`a2c20424
00000217`01b7cae0  588cf96f`8d9a9b55 5abbb836`66dab3e4
00000217`01b7caf0  dd245fd8`cff527e0 305d965e`88702ad4
00000217`01b7cb00  bc2c5fa8`60c0a7c4 da78741f`3b160725
00000217`01b7cb10  10e18e91`03b009e6 289665d2`a2f45daa
00000217`01b7cb20  987ff2be`b8bca1be 45a2969e`922a2e06
00000217`01b7cb30  67face47`2fa1cee6 005fd79e`81aa3a4f
00000217`01b7cb40  30813582`50286f3b 64624dfb`1a442845
00000217`01b7cb50  66f8af79`e3d568d1 c45acd5d`a0e8a5a6


...что мусор начинается имнно с того места, где раньше находился первый элемент массива.


Меня терзают смутные сомнения, что память, выделенная этому объекту была помечена, как незанятая и просто была переиспользована другим компонентом без каких-либо плясок с unsafe и выходом за границы буфера. Или GC попытался дефрагментировать память, и сделать ему это не удалось.

В связи с этим, у меня несколько вопросов:

1) На забугорных ресурсах кому-то помогает отключение параллелинга в GC

<configuration>
   <runtime>
      <gcConcurrent enabled="false"/>
   </runtime>
</configuration>


Если это не эффект плацебо, значит существуют какие-то гонки между потоками сборщика мусора, которые могут приводить к коррапту кучи? С чем они связаны? От чего зависят? ОС, окружение, железо?

2) Может ли на это как-то повлиять наличие или отсутствие флажка gcServer?

3) Как можно посмотреть — в какие захваченные регионы входит данный фрагмент памяти, кто его аллоцировал, кто держит (поимо !gcroot)?

Подозрительным является то, что "мусор" начинается ровно по границе массива. >_>


Ещё один камень в сторону GC. Другой дамп:


> !VerifyHeap
Could not request method table data for object 000001C88304B5E8 (MethodTable: C4C1934C7B7ABE70).
Last good object: 000001C88304B5C0.

> !objsize 000001C88304B5C0
sizeof(000001c88304b5c0) = 40 (0x28) bytes (System.String)

> db 000001C88304B5C0 L 40
000001c8`8304b5c0  98 de a8 d8 f8 7f 00 00-07 00 00 00 6d 00 6d 00  ............m.m.
000001c8`8304b5d0  2d 00 33 00 30 00 33 00-31 00 00 00 00 00 00 00  -.3.0.3.1.......
000001c8`8304b5e0  00 00 00 00 00 00 00 00-70 be 7a 7b 4c 93 c1 c4  ........p.z{L...
000001c8`8304b5f0  d3 5e e6 47 9c 83 18 2d-c4 8e d0 93 2d 49 4e 72  .^.G...-....-INr

> !gcroot 000001C88304B5C0
Found 0 unique roots (run '!GCRoot -all' to see all roots).
> !gcroot 000001C88304B5E8 
Found 0 unique roots (run '!GCRoot -all' to see all roots).

Ready for finalization 0 objects (000001c8d0102cb0->000001c8d0102cb0)


Ссылок на эти объекты нет, в очереди финализатора их нет, зато есть мусор в чужой памяти.
Отредактировано 18.08.2017 17:42 LWhisper . Предыдущая версия . Еще …
Отредактировано 18.08.2017 17:07 LWhisper . Предыдущая версия .
.net windbg debug memory gc heap
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.