Как отловить heap corruption?

Тема в разделе "WASM.WIN32", создана пользователем M0rg0t, 5 июн 2020.

Метки:
  1. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Есть прога, которая хорошо работает в 32 бит, пересобираю ее на 64 бита. Помимо прочего, всплывают ошибки heap corruption при вызове HeapFree. Я понимаю, что где-то перезаписывается граница массива и при освобождении все падает. Но вручную сложно искать, мб есть какие-то инструменты, профилировщики, логгеры для памяти?
     
  2. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Я бы для начала попробовал статические анализаторы, типа cppcheck и скаченной с торрентов pvs studio. Ты удивишься, сколько они косяков находят, о которых ты даже не думал. В частности для порта с х86 на х64 могут помочь.
     
    M0rg0t нравится это.
  3. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Это тот софт, что с белым конем? Спасибо, он вроде как даже бесплатно доступен, если комент добавить. Правда, не юзал ни разу.
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    С единорогом, блюющим радугой, да.
     
  5. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    M0rg0t,

    Там множество механизмов, отладочных да что угодно. при каждой манипуляции памятью может выполнять полный анализ вызова - блоков, cтека и тп.
     
  6. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Подниму тему. Есть стандартный код, выделение памяти через HeapAlloc,освобождение через HeapFree, и вот он иногда падает на heapfree. Причина - перезапись границ буфера. Хотел это логировать, но почему-то обработчики исключений не ловят его нифига. Точнее так:

    Код (ASM):
    1. .486
    2. .model flat,stdcall
    3. option casemap:none
    4.  
    5. seh1 proto pExcept:dword
    6.  
    7. include \masm32\include\windows.inc
    8. include \masm32\macros\macros.asm
    9. uselib kernel32,user32
    10.  
    11. .data
    12.  
    13. .data?
    14. hH dd ?
    15. lpMem dd ?
    16.  
    17.  
    18. .code
    19. start:
    20. invoke SetUnhandledExceptionFilter,offset seh1
    21.  
    22. invoke HeapCreate,HEAP_GENERATE_EXCEPTIONS, 10000, 0
    23.     mov hH,eax
    24.  
    25. invoke HeapAlloc,hH, 0, 32
    26.     mov lpMem,eax
    27.    
    28. mov edi,lpMem
    29. xor eax,eax
    30. mov al,'A'
    31. mov ecx,91
    32. rep stosb
    33.  
    34. invoke HeapFree,hH, 0, lpMem
    35.  
    36. exit
    37.  
    38. seh1 proc pExcept:dword
    39. invoke OutputDebugStringA,chr$("seh..")
    40. ret
    41. seh1 endp
    42.  
    43. end start
    Асм ловит все норм, но когда аналогичный код на Си, то обработчик не мой, а ntdll.__except_handler4 . VEH также падает туда. Где студия хранит все это и как отключить?
     
  7. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    M0rg0t, в плюсах есть конструкция catch(...) которая ловит все. Вы ее пробовали?
     
  8. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    catch ловит только плюсовые исключения, брошенные через throw. Исключения, брошенные процессором или вручную через RtlRaiseException, она не поймает. Зато поймает __try..__except.
    Но если надо ловить в контексте произвольных потоков в чужом коде, то только SetUnhandledExceptionFilter/AddVectoredExceptionHandler.

    M0rg0t, покажи сишный вариант и проверь все статусы у функций, что они возвращают. Попробуй кинуть исключение вручную через RtlRaiseException и посмотри, поймаешь ли ты его.
     
    TermoSINteZ нравится это.
  9. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    TermoSINteZ, не пробовал, ибо пишу на чистом Си, и зачастую без CRT, т.е. мне доступен только винапи SEH / VEH.

    HoShiMin, RaiseException ловлю успешно. По статусам - все функции вроде работают норм, т.е. HeapCreate, Alloc возвращают валидные значения, а падает ес-но на HeapFree, только падает не в тот обработчик, что надо.

    Код (C):
    1. LONG WINAPI seh1(__in  struct _EXCEPTION_POINTERS* ExceptionInfo)
    2. {
    3.     char buf[64];
    4.     wsprintfA(buf, "err 0x%x in 0x%x", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
    5.     OutputDebugStringA(buf);
    6.  
    7.     HANDLE hF = CreateFileW(L"1.dmp", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    8.     MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hF, 0x00000002, NULL, NULL, NULL);
    9.  
    10.  
    11.     return EXCEPTION_EXECUTE_HANDLER;
    12. }
    13.  
    14. void test_heap()
    15. {
    16.     HANDLE hH = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 10000, 0); //test
    17.  
    18.     LPBYTE lpMem = HeapAlloc(hH, 0, 32);
    19.     memset(lpMem, 'A', 33);
    20.  
    21.     HeapFree(hH, 0, lpMem);
    22.  
    23. }
    24.  
    25.  
    26.  
    27. int wmain(_In_ int argc, _In_reads_(argc) _Pre_z_ wchar_t** argv, _In_z_ wchar_t** envp)
    28. {
    29.     SetUnhandledExceptionFilter(seh1);
    30.     //AddVectoredExceptionHandler(1, seh1);
    31.     //RaiseException(0x123456, 0, 0, NULL);
    32.     test_heap();
    33.     return 0;
    34. }
     
  10. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    хм. а ведь и правда - это неуловимое исключение, то есть есть определенные ошибки работы менеджера памяти кучи, который считает, что исключение критическое, и прибивает программу игнорируя ваши установленные исключения
    Цитата
    И да, это еще и зависит от ОС на которой вы запускаете.
     
  11. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    но масм ведь как-то ловит. Вот почему много лет не хотел переходить на Си, и не хочу на плюсы и прочие расты - там компилятор считает, что он умнее человека.
    Как же тогда быть с этим хип корапшн? Пробовал статические анализаторы, но увы, часто бывает что буфер выделила 1 функция, его передали в другую, третью, и где-то оно падает.
     
  12. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.995
    Студии под рукой нет, из-под гцц приведенный исходник дает срабатывание обработчика. Наверное ответ кроется в самом бинарнике из-под студии, где обработчик не устанавливается вовсе, потому что студия.
     
    M0rg0t нравится это.
  13. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    f13nd, да, студия "особенный" компилятор, но вроде чистый код, т.е. компилировал с флагами без црт и прочего.

    В общем, Thetrik нашел решение - надо указывать MajorOperation System в 4 (как делает масм, студия ставит 6), тогда почему-то все работает как надо.
     
  14. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    M0rg0t, на линуксе с помощью санитайзеров все ловится. В винде - может интел Vtune сможет увидеть проблемы. Но обычно я пишу код так, чтоб можно было всегда проследить кому сколько куда выделилось и освободилось.
    если затрешь по-больше чем на 1 байт , то не поймает )
    Проверял на win10 x64.

    Код на фасме

    Код (ASM):
    1.  
    2. format PE64 GUI
    3. entry start
    4.  
    5. include 'fasmw\\INCLUDE\\win64a.inc'
    6.  
    7. section '.text' code readable executable
    8. start:
    9.       sub      rsp,8*5
    10.  
    11.       lea rcx, [msgst]
    12.       call     [OutputDebugStringA]
    13.  
    14.       lea       rcx,[seh1]
    15.       call      [SetUnhandledExceptionFilter]
    16.  
    17.       xor r8, r8
    18.       mov rdx, 10000
    19.       mov rcx, 4
    20.       call [HeapCreate]
    21.  
    22.       mov [hH],rax
    23.  
    24.       mov r8, 32
    25.       mov rdx, 0
    26.       mov rcx, [hH]
    27.       call [HeapAlloc]
    28.       mov [lpMem],rax
    29.  
    30.       mov rdi,rax
    31.       xor rax,rax
    32.       mov al,'A'
    33.       mov rcx,33 ; try 91 too ^_^
    34.       rep stosb
    35.  
    36.       mov r8, [lpMem]
    37.       mov rdx, 0
    38.       mov rcx, [hH]
    39.       call [HeapFree]
    40.  
    41.       lea rcx, [msg]
    42.       call [OutputDebugStringA]
    43.  
    44.       xor  rax, rax
    45.       call [ExitProcess]
    46.  
    47. seh1:
    48.       lea rcx, [msge]
    49.       call [OutputDebugStringA]
    50.       ret
    51.  
    52. section '.data' data readable writeable
    53.   hH dq ?
    54.   lpMem dq ?
    55.   msg db 'seh',0
    56.   msgst db 'start seh test',0
    57.   msge db 'end test',0
    58.  
    59. section 'idata' import data readable writeable
    60.   dd 0,0,0,RVA kernel_name,RVA kernel_table
    61.   dd 0,0,0,RVA user_name,RVA user_table
    62.   dd 0,0,0,0,0
    63.  
    64.   kernel_table:
    65.     GetLastError dq RVA _GetLastError
    66.     LoadLibraryA dq RVA _LoadLibraryA
    67.     ExitProcess dq RVA _ExitProcess
    68.     SetUnhandledExceptionFilter dq RVA _SetUnhandledExceptionFilter
    69.     HeapCreate dq RVA _HeapCreate
    70.     HeapAlloc dq RVA _HeapAlloc
    71.     HeapFree dq RVA _HeapFree
    72.     OutputDebugStringA dq RVA _OutputDebugStringA
    73.     dq 0
    74.   user_table:
    75.     MessageBoxA dq RVA _MessageBoxA
    76.     dq 0
    77.  
    78.   kernel_name db 'KERNEL32.DLL',0
    79.   user_name db 'USER32.DLL',0
    80.  
    81.   _GetLastError dw 0
    82.     db 'GetLastError',0
    83.   _LoadLibraryA dw 0
    84.     db 'LoadLibraryA',0
    85.   _ExitProcess dw 0
    86.     db 'ExitProcess',0
    87.   _SetUnhandledExceptionFilter dw 0
    88.     db 'SetUnhandledExceptionFilter',0
    89.   _HeapCreate dw 0
    90.     db 'HeapCreate',0
    91.   _HeapAlloc dw 0
    92.     db 'HeapAlloc', 0
    93.   _HeapFree dw 0
    94.     db 'HeapFree', 0
    95.   _OutputDebugStringA dw 0
    96.     db 'OutputDebugStringA', 0
    97.   _MessageBoxA dw 0
    98.     db 'MessageBoxA',0
    99.  
     
    GRAFik нравится это.
  15. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    TermoSINteZ, у меня аналогично win10x64, и все ловит. Т.е. прога не падает, пишет только лог в dbgview.
    Странно это всё.
    --- Сообщение объединено, 10 июл 2022 ---
    TermoSINteZ, а хотя да,- ставлю 33 , ловит; ставлю 91, не ловит. В чем суть? Что хип выделяется блоками какого-то размера, и до него можно безопасно перезаписывать границу?