How to benefit from heap tagging by DLL?
Asked Answered
S

1

6

How do I use and benefit from the GFlags setting Enable heap tagging by DLL?

I know how to activate the setting for a process, but I did not find useful information in the output of !heap -t in WinDbg. I was expecting some output like this:

0:000> !heap -t
Index   Address   Allocated by 
1:      005c0000  MyDll.dll
2:      006b0000  AnotherDll.dll

so that I can identify which heap was created by which DLL and then e.g. identify the source of a memory leak.

Is this a misunderstanding of the term "heap tagging by DLL" or do I need some more commands to get to the desired result?

My research so far:

  • I googled for a tutorial on this topic, but I couldn't find a detailed description
  • I read WinDbg's .hh !heap but it's not explained there in detail as well. Tag is only used in !heap -b
Sailer answered 24/4, 2014 at 8:57 Comment(0)
Z
6

again a very late answer

to benefit from HeapTagging you need to create a tag first in your code.
as far as i know (that is upto xp-sp3) there were no Documented APIS to Create a tag

(I havent mucked with heap since then so i am not aware of latest apis in os > vista Rewrites were done to heap manager so probably many of the ^^^features^^^ that i post below might have been corrected or bettered or bugs removed )

in xp-sp3 you can use undocumented RtlCreateTagHeap to create a new tag to either Process Heap or Private Heap

and after you create tha tag you need to set the global flag 8000 | 800

htg - Enable heap tagging
htd - Enable heap tagging by DLL

and theoratically all allocs and frees must get tagged .

but practically only allocations > 512 kB gets tagged in xp-sp3 with these basic steps

it either is a bug or a feature that limits tagging to allocations and frees > 512 kB
HeapAlloc goes through ZwAllocateVirtualMemory in case of Allocations > 512 kB in 32 bit process refer HeapCreate / HeapAlloc Documentation in msdn

and as a debuging aid you can patch ntdll.dll on the fly to enable tagging for all Allocations and frees .

below is a sample code that demonstrates the tagging and how to view it all in windbg

compile using cl /Zi /analyze /W4 <src> /link /RELEASE

use windbg to execute the app and watch tagging with !heap * -t command

#include <windows.h>
#include <stdio.h>

//heaptags are kinda broken or they are intentionally 
//given only to allocations > 512 kb // allocation > 512 kb
//go through VirtualAlloc Route for Heap created with maxsize 
//set to 0 uncomment ALLOCSIZE 0xfdfd2 and recompile to watch 
// tagging increase by 100% with ALLOCSIZE  0xfdfd1 only 50 allocs 
// and frees that are > 512 kB will be tagged these magic numbers 
// are related to comment in HeapCreate Documentation that state 
// slightly less than 512 kB will be allocated for 32 bit process 
// tagging can be dramatically increased by patching ntdll when 
// stopped on system breakpoint patch 7c94b8a4 (xpsp3 ntdll.dll) 
// use the below command in windbg for finding the offset of pattern
// command must be in single line no line breaks
// .foreach /pS 4 /ps 4 ( place  { !grep -i -e call -c 
// "# call*RtlpUpdateTagEntry 7c900000 l?20000" } ) { ub place }
// the instruction we are searching to patch is 
//7c94b8a1 81e3ff0fffff    and     ebx,0FFFF0FFFh 
// patch 0f to 00 at system breakpoint with eb 7c94b8a1+3 00 

#define BUFFERSIZE 100
#define ALLOCSIZE  0xfdfd1
//#define ALLOCSIZE  0xfdfd2

typedef int ( __stdcall *g_RtlCreateTagHeap) ( 
    HANDLE hHeap ,
    void * unknown, 
    wchar_t * BaseString, 
    wchar_t * TagString 
    );

void HeapTagwithHeapAllocPrivate()
{
    PCHAR pch[BUFFERSIZE] = {};
    HANDLE hHeap    = 0;
    ULONG tag1      = 0;
    ULONG tag2      = 0;
    ULONG tag3      = 0;
    ULONG tag4      = 0;
    ULONG tag5      = 0;
    g_RtlCreateTagHeap RtlCreateTagHeap = 0;
    HMODULE hMod = LoadLibrary("ntdll.dll");
    if(hMod)
    {
        RtlCreateTagHeap = (g_RtlCreateTagHeap) 
            GetProcAddress( hMod,"RtlCreateTagHeap");
    }
    if (hHeap == 0)
    {
        hHeap = HeapCreate(0,0,0);
        if (RtlCreateTagHeap != NULL)
        {
            tag1 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag1");
            tag2 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag2"); 
            tag3 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag3");
            tag4 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag4");
        }
    }
    HANDLE DefHeap = GetProcessHeap();
    if ( (RtlCreateTagHeap != NULL)  && (DefHeap != NULL ))
    {
        tag5 = RtlCreateTagHeap (DefHeap,0,L"HeapTag!",L"MyTag5");
        for ( int i = 0; i < BUFFERSIZE ; i++ )
        {
            pch[i]= (PCHAR) HeapAlloc( DefHeap,HEAP_ZERO_MEMORY| tag5, 1 );
            HeapFree(DefHeap,NULL,pch[i]);
        }

    }
    if(hHeap)
    {
        for ( int i = 0; i < BUFFERSIZE ; i++ )
        {
            pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag1, 1 );
            //lets leak all allocs patch ntdll to see the tagging details
            //HeapFree(hHeap,NULL,pch[i]);
        }
        for ( int i = 0; i < BUFFERSIZE ; i++ )
        {
            pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag2, 100 );
            // lets leak 40% allocs patch ntdll to see the tagging details
            if(i >= 40)
                HeapFree(hHeap,NULL,pch[i]);
        }
        // slightly less than 512 kb no tagging
        for ( int i = 0; i < BUFFERSIZE / 2 ; i++ ) 
        {
            pch[i]= (PCHAR) HeapAlloc( 
                hHeap,HEAP_ZERO_MEMORY| tag3, ALLOCSIZE / 2 );
        }
        // > 512 kb  default tagging 
        for ( int i = BUFFERSIZE / 2; i < BUFFERSIZE ; i++ ) 
        {
            pch[i]= (PCHAR) HeapAlloc( 
                hHeap,HEAP_ZERO_MEMORY | tag4 ,ALLOCSIZE );
        }
        for (int i =0 ; i < BUFFERSIZE ; i++)
        {
            HeapFree(hHeap,NULL,pch[i]);
        }
    }
}
void _cdecl main()
{
    HeapTagwithHeapAllocPrivate();
}

the compiled exe to be run with windbg as below

DEFAULT execution and inspection
**only 50 tags will be visible all of them are > 512 kB Allocations

cdb -c "g;!heap * -t;q" newheaptag.exe | grep Tag**

heaptag:\>cdb -c "g;!heap * -t;q" newheaptag.exe | grep Tag
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
0004: HeapTag!MyTag4             50       50      0        0

patching ntdll on system breakpoint should make all tags visible

eb = write byte patch and run the exe on exit inspect heaps with tags cdb -c "eb 7c94b8a1+3 00;g;!heap * -t;q" newheaptag.exe | grep Tag

heaptag:\>cdb -c "eb 7c94b8a1+3 00;g;!heap * -t;q" newheaptag.exe | grep Tag
 Tag  Name                   Allocs    Frees   Diff  Allocated
0012: HeapTag!MyTag5            100      100      0        0  <-our tag in process heap
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
0001: HeapTag!MyTag1            100        0    100     3200  <--- leak all
0002: HeapTag!MyTag2            100       60     40     5120  <--- leak 40 %
0003: HeapTag!MyTag3             50       50      0        0  <--- clean < 512 kB
0004: HeapTag!MyTag4             50       50      0        0  <----clean > 512 kB
Zed answered 19/5, 2014 at 21:1 Comment(2)
It's ok to wait a longer time for harder questions :-) I'll go ahead and try it (Windows 7). The concept itself sounds not so useful, because someone needs to think about tagging heaps in advance. Usually I am thinking about heap tagging only in case I already have a problem.Sailer
the concept is quiet useful you make it a habit to code robust :) in kernel you are forced to do it by default..... ExAllocatePool is Deprecated you are advised to use ExAllocatePoolWithTag and so you tag in advance which the helper utilities use to zero in that is all in kernel it is streamlined in user mode it isnt streamlined yet if you try it on any os > xp upto win 8.1 as of date post back the resultsZed

© 2022 - 2024 — McMap. All rights reserved.