Compartilhar via


Exemplo 12: usando a verificação de heap de página para localizar um bug

A série de comandos a seguir demonstra como usar os recursos de verificação de heap de página de GFlags e o depurador NTSD para detectar um erro no uso de memória de heap. Neste exemplo, o programador suspeita que um aplicativo fictício, pheap-buggy.exe, tenha um erro de heap e prossiga por meio de uma série de testes para identificar o erro.

Para obter detalhes sobre o NTSD, consulte Depuração usando CDB e NTSD.

Etapa 1: Habilitar a verificação de heap de página padrão

O comando a seguir habilita a verificação de heap de página padrão para pheap-buggy.exe:

gflags /p /enable pheap-buggy.exe

Etapa 2: verificar se o heap de página está habilitado

O comando a seguir lista os arquivos de imagem para os quais a verificação de heap de página está habilitada:

gflags /p

Em resposta, o GFlags exibe a lista de programas a seguir. Nesta exibição, os rastreamentos indicam a verificação de heap de página padrão e os rastreamentos completos indicam a verificação de heap de página inteira. Nesse caso, pheap-buggy.exe é listado com rastreamentos, indicando que a verificação de heap de página padrão está habilitada, conforme pretendido.

pheap-buggy.exe: page heap enabled with flags (traces )

Etapa 3: Executar o depurador

O comando a seguir executa a função CorruptAfterEnd de pheap-buggy.exe no NTSD com os parâmetros -g (ignorar ponto de interrupção inicial) e -x (definir interrupção de segunda chance em exceções de violação de acesso):

ntsd -g -x pheap-buggy CorruptAfterEnd

Quando o aplicativo falha, o NTSD gera a seguinte exibição, o que indica que ele detectou um erro no pheap-buggy.exe:

===========================================================
VERIFIER STOP 00000008: pid 0xAA0: corrupted suffix pattern

        00C81000 : Heap handle 
        00D81EB0 : Heap block 
        00000100 : Block size 
#         00000000 :
===========================================================

Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00d81eb0 ecx=77f7e257 edx=0006fa18 esi=00000008 edi=00c81000
eip=77f7e098 esp=0006fc48 ebp=0006fc5c iopl=0         nv up ei pl zr na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77f7e098 cc               int     3

As informações de cabeçalho incluem o endereço do heap com o bloco corrompido (00C81000 : identificador heap), o endereço do bloco corrompido (00D81EB0 : bloco Heap) e o tamanho da alocação (00000100 : tamanho do bloco).

A mensagem "padrão de sufixo corrompido" indica que o aplicativo violou o padrão de integridade de dados inserido por GFlags após o final do pheap-buggy.exe alocação de heap.

Etapa 4: Exibir a pilha de chamadas

Na próxima etapa, use os endereços relatados pelo NTSD para localizar a função que causou o erro. Os dois comandos seguintes ativam o despejo de número de linha no depurador e exibem a pilha de chamadas com números de linha.

C:\>.lines

Line number information will be loaded 

C:\>kb

ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0006fc5c 77f9e6dd 00000008 77f9e3e8 00c81000 ntdll!DbgBreakPoint
0006fcd8 77f9f3c8 00c81000 00000004 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x2879
0006fcfc 77f9f5bb 00c81000 01001002 00000010 ntdll!RtlpNtEnumerateSubKey+0x3564
0006fd4c 77fa261e 00c80000 01001002 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x3757
0006fdc0 77fc0dc2 00c80000 01001002 00d81eb0 ntdll!RtlpNtEnumerateSubKey+0x67ba
0006fe78 77fbd87b 00c80000 01001002 00d81eb0 ntdll!RtlSizeHeap+0x16a8
0006ff24 010013a4 00c80000 01001002 00d81eb0 ntdll!RtlFreeHeap+0x69
0006ff3c 01001450 00000000 00000001 0006ffc0 pheap-buggy!TestCorruptAfterEnd+0x2b [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 185]
0006ff4c 0100157f 00000002 00c65a68 00c631d8 pheap-buggy!main+0xa9 [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 69]
0006ffc0 77de43fe 00000000 00000001 7ffdf000 pheap-buggy!mainCRTStartup+0xe3 [crtexe.c @ 349]
0006fff0 00000000 0100149c 00000000 78746341 kernel32!DosPathToSessionPathA+0x204

Como resultado, o depurador exibe a pilha de chamadas para pheap-buggy.exe com números de linha. A exibição da pilha de chamadas mostra que o erro ocorreu quando a função TestCorruptAfterEnd no pheap-buggy.exe tentou liberar uma alocação em 0x00c80000 chamando HeapFree, um redirecionamento para RtlFreeHeap.

A causa mais provável desse erro é que o programa escreveu após o final do buffer alocado nessa função.

Etapa 5: Habilitar a verificação de heap de página inteira

Ao contrário da verificação de heap de página padrão, a verificação de heap de página inteira pode capturar o uso indevido desse buffer de heap assim que ele ocorrer. O comando a seguir habilita a verificação de heap de página inteira para pheap-buggy.exe:

gflags /p /enable pheap-buggy.exe /full

Etapa 6: verificar se o heap de página inteira está habilitado

O comando a seguir lista os programas para os quais a verificação de heap de página está habilitada:

gflags /p

Em resposta, o GFlags exibe a lista de programas a seguir. Nesta exibição, os rastreamentos indicam a verificação de heap de página padrão e os rastreamentos completos indicam a verificação de heap de página inteira. Nesse caso, pheap-buggy.exe é listado com rastreamentos completos, indicando que a verificação de heap de página inteira está habilitada, conforme pretendido.

pheap-buggy.exe: page heap enabled with flags (full traces )

Etapa 7: executar o depurador novamente

O comando a seguir executa a função CorruptAfterEnd de pheap-buggy.exe no depurador NTSD com os parâmetros -g (ignorar ponto de interrupção inicial) e -x (definir interrupção de segunda chance em exceções de violação de acesso):

ntsd -g -x pheap-buggy CorruptAfterEnd

Quando o aplicativo falha, o NTSD gera a seguinte exibição, o que indica que ele detectou um erro no pheap-buggy.exe:

Page heap: process 0x5BC created heap @ 00880000 (00980000, flags 0x3)
ModLoad: 77db0000 77e8c000   kernel32.dll
ModLoad: 78000000 78046000   MSVCRT.dll
Page heap: process 0x5BC created heap @ 00B60000 (00C60000, flags 0x3)
Page heap: process 0x5BC created heap @ 00C80000 (00D80000, flags 0x3)
Access violation - code c0000005 (first chance)
Access violation - code c0000005 (!!! second chance !!!)
eax=00c86f00 ebx=00000000 ecx=77fbd80f edx=00c85000 esi=00c80000 edi=00c16fd0
eip=01001398 esp=0006ff2c ebp=0006ff4c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000206
pheap-buggy!TestCorruptAfterEnd+1f:
01001398 889801010000     mov     [eax+0x101],bl          ds:0023:00c87001=??

Com a verificação de heap de página inteira habilitada, o depurador interrompe uma violação de acesso. Para localizar o local preciso da violação de acesso, ative o despejo de número de linha e exiba o rastreamento da pilha de chamadas.

O rastreamento de pilha de chamadas numerada é exibido da seguinte maneira:

ChildEBP RetAddr  Args to Child
0006ff3c 01001450 00000000 00000001 0006ffc0 pheap-buggy!TestCorruptAfterEnd+0x1f [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 184]
0006ff4c 0100157f 00000002 00c16fd0 00b70eb0 pheap-buggy!main+0xa9 [d:\nttest\base\testsrc\kernel\rtl\pageheap\pheap-buggy.cxx @ 69]
0006ffc0 77de43fe 00000000 00000001 7ffdf000 pheap-buggy!mainCRTStartup+0xe3 [crtexe.c @ 349]
WARNING: Stack unwind information not available. Following frames may be wrong.
0006fff0 00000000 0100149c 00000000 78746341 kernel32!DosPathToSessionPathA+0x204

O rastreamento de pilha mostra que o problema ocorre na linha 184 do pheap-buggy.exe. Como a verificação de heap de página inteira está habilitada, a pilha de chamadas começa no código do programa, não em uma DLL do sistema. Como resultado, a violação foi capturada onde aconteceu, em vez de quando o bloco de heap foi liberado.

Etapa 8: localizar o erro no código

Uma inspeção rápida revela a causa do problema: o programa tenta gravar no 257º byte (0x101) de um buffer de 256 bytes (0x100), um erro comum fora de um.

*((PCHAR)Block + 0x100) = 0;