Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Why does my kernel driver crash with BAD_POOL_CALLER?

user555user555 Member Posts: 8

I'm learning how to write Windows kernel drivers, however I got stuck on something not so obvious to me.

My driver uses PsSetLoadImageNotifyRoutineto register a callback that is called on every image (dll) load. Once a image load is registered I put it into a linked list, signal an event and have a user mode application retrieve the information using DeviceIoControl.

The problem I'm having is that the driver crashes with BAD_POOL_CALLER. According to windbg the problem is located in my DispatchIoctlroutine. The stacktrace points to nt!ExFreePool however I don't see who else would have freed the PIMAGE_CALLBACK_INFO2structure.

I also tried to remove the ExFreePool call but got just another memory error.

Why does my kernel driver crash with BAD_POOL_CALLER?

VOID ImageCallback(
    IN PUNICODE_STRING FullImageName,
    IN HANDLE ProcessId,
    IN PIMAGE_INFO ImageInfo
)
{
    UNREFERENCED_PARAMETER(ImageInfo);

    PDEVICE_EXTENSION extension;
    //
    // Assign extension variable
    //
    extension = (PDEVICE_EXTENSION)g_pDeviceObject->DeviceExtension;

    PIMAGE_CALLBACK_INFO2 info = (PIMAGE_CALLBACK_INFO2)ExAllocatePoolWithTag(NonPagedPool, sizeof(IMAGE_CALLBACK_INFO2), 1);
    if (info == NULL)
    {
        DbgPrint("STATUS_INSUFFICIENT_RESOURCES");
        return;
    }
    DbgPrint("FullImageName: %wZ\n", FullImageName);
    info->FullImageName = FullImageName;

    DbgPrint("ProcessId: %d\n", ProcessId);
    info->ProcessId = ProcessId;

    PushEntryList(&SingleHead, &(info->LinkField));

    KeSetEvent(extension->ProcessEvent, 0, FALSE);
    KeClearEvent(extension->ProcessEvent);
}

NTSTATUS DispatchIoctl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
)
{
    NTSTATUS               ntStatus = STATUS_UNSUCCESSFUL;
    PIO_STACK_LOCATION     irpStack = IoGetCurrentIrpStackLocation(Irp);
    PDEVICE_EXTENSION      extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    PIMAGE_CALLBACK_INFO   pImageCallbackInfo;

    UNREFERENCED_PARAMETER(extension);

    //
    // These IOCTL handlers are the set and get interfaces between
    // the driver and the user mode app
    //
    switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
    {
    case IOCTL_PROCOBSRV_ACTIVATE_MONITORING:
    {
        ntStatus = ActivateMonitoringHanlder(Irp);
        break;
    }

    case IOCTL_PROCOBSRV_GET_IMAGEINFO:
    {
        if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
            sizeof(IMAGE_CALLBACK_INFO))
        {
            pImageCallbackInfo = (PIMAGE_CALLBACK_INFO)Irp->AssociatedIrp.SystemBuffer;

            PSINGLE_LIST_ENTRY SingleListEntry;
            SingleListEntry = PopEntryList(&SingleHead);

            if (SingleListEntry != NULL)
            {
                PIMAGE_CALLBACK_INFO2 info = (PIMAGE_CALLBACK_INFO2)CONTAINING_RECORD(SingleListEntry, IMAGE_CALLBACK_INFO2, LinkField);

                if (info->FullImageName != NULL)
                {
                    DbgPrint("FullImageName: %wZ\n", info->FullImageName);
                    RtlCopyMemory(pImageCallbackInfo->FullImageName, info->FullImageName->Buffer, info->FullImageName->Length);
                    RtlFreeUnicodeString(info->FullImageName);
                }
                pImageCallbackInfo->ProcessId = info->ProcessId;

                ExFreePool(info);
            }

            ntStatus = STATUS_SUCCESS;
        }
        break;
    }

    default:
        break;
    }

    Irp->IoStatus.Status = ntStatus;
    //
    // Set number of bytes to copy back to user-mode
    //
    if (ntStatus == STATUS_SUCCESS)
        Irp->IoStatus.Information =
        irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    else
        Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return ntStatus;
}

Comments

  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,683

    You should always post the !analyze -v output if you want help with a crash...

    But in this case the failure seems pretty obvious. The FullImageName argument is not yours to free. If you want to pass it to your device control handler you need to make your own deep copy that you then subsequently free.

    -scott
    OSR

  • user555user555 Member Posts: 8
    edited March 2019

    Nice catch! That solved that problem. I removed the RtlFreeUnicodeString call. Now I get another one.

    *******************************************************************************
    *                                                                             *
    *                        Bugcheck Analysis                                    *
    *                                                                             *
    *******************************************************************************
    
    BAD_POOL_HEADER (19)
    The pool is already corrupt at the time of the current request.
    This may or may not be due to the caller.
    The internal pool links must be walked to figure out a possible cause of
    the problem, and then special pool applied to the suspect tags or the driver
    verifier to a suspect driver.
    Arguments:
    Arg1: 0000000000000003, the pool freelist is corrupt.
    Arg2: fffff80003c582d0, the pool entry being checked.
    Arg3: 0000000000000000, the read back flink freelist value (should be the same as 2).
    Arg4: 6c734d46020c0008, the read back blink freelist value (should be the same as 2).
    
    Debugging Details:
    ------------------
    
    
    KEY_VALUES_STRING: 1
    
    
    STACKHASH_ANALYSIS: 1
    
    TIMELINE_ANALYSIS: 1
    
    
    DUMP_CLASS: 1
    
    DUMP_QUALIFIER: 401
    
    BUILD_VERSION_STRING:  7601.24354.amd64fre.win7sp1_ldr_escrow.190108-1700
    
    DUMP_TYPE:  1
    
    BUGCHECK_P1: 3
    
    BUGCHECK_P2: fffff80003c582d0
    
    BUGCHECK_P3: 0
    
    BUGCHECK_P4: 6c734d46020c0008
    
    BUGCHECK_STR:  0x19_3
    
    DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT
    
    CURRENT_IRQL:  2
    
    ANALYSIS_VERSION: 10.0.17763.1 amd64fre
    
    LAST_CONTROL_TRANSFER:  from fffff80003c38253 to fffff80003af0ba0
    
    STACK_TEXT:  
    fffff880`0c281778 fffff800`03c38253 : 00000000`00000019 00000000`00000003 fffff800`03c582d0 00000000`00000000 : nt!KeBugCheckEx
    fffff880`0c281780 fffff800`03a9dc55 : 00000000`00000000 00000000`0000000c 00000000`00000001 fffffa80`00000000 : nt!ExFreePool+0x4fb
    fffff880`0c281870 fffff800`03f1615f : fffffa80`06dfb810 00000000`00000000 fffffa80`06de9070 fffff880`03300180 : nt!ExAllocatePoolWithQuotaTag+0x55
    fffff880`0c2818c0 fffff800`03da7cc6 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0xadf
    fffff880`0c281a00 fffff800`03afebd3 : fffffa80`06e256d0 00000000`1c60e288 fffff880`0c281a88 00000000`1c60dd78 : nt!NtDeviceIoControlFile+0x56
    fffff880`0c281a70 00000000`77b098fa : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
    00000000`1c60dd28 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x77b098fa
    
    
    THREAD_SHA1_HASH_MOD_FUNC:  b66b709f62a2d09a97da274ebc94b18351cb84bc
    
    THREAD_SHA1_HASH_MOD_FUNC_OFFSET:  48cf334b3ecd1316141b830817191d5780668ba8
    
    THREAD_SHA1_HASH_MOD:  ee8fcf1fb60cb6e3e2f60ddbed2ec02b5748a693
    
    FOLLOWUP_IP: 
    nt!ExFreePool+4fb
    fffff800`03c38253 cc              int     3
    
    FAULT_INSTR_CODE:  d634ccc
    
    SYMBOL_STACK_INDEX:  1
    
    SYMBOL_NAME:  nt!ExFreePool+4fb
    
    FOLLOWUP_NAME:  Pool_corruption
    
    IMAGE_NAME:  Pool_Corruption
    
    DEBUG_FLR_IMAGE_TIMESTAMP:  0
    
    IMAGE_VERSION:  6.1.7601.24354
    
    MODULE_NAME: Pool_Corruption
    
    STACK_COMMAND:  .thread ; .cxr ; kb
    
    FAILURE_BUCKET_ID:  X64_0x19_3_nt!ExFreePool+4fb
    
    BUCKET_ID:  X64_0x19_3_nt!ExFreePool+4fb
    
    PRIMARY_PROBLEM_CLASS:  X64_0x19_3_nt!ExFreePool+4fb
    
    TARGET_TIME:  2019-03-04T00:20:12.000Z
    
    OSBUILD:  7601
    
    OSSERVICEPACK:  1000
    
    SERVICEPACK_NUMBER: 0
    
    OS_REVISION: 0
    
    SUITE_MASK:  272
    
    PRODUCT_TYPE:  1
    
    OSPLATFORM_TYPE:  x64
    
    OSNAME:  Windows 7
    
    OSEDITION:  Windows 7 WinNt (Service Pack 1) TerminalServer SingleUserTS
    
    OS_LOCALE:  
    
    USER_LCID:  0
    
    OSBUILD_TIMESTAMP:  2019-01-09 03:35:55
    
    BUILDDATESTAMP_STR:  190108-1700
    
    BUILDLAB_STR:  win7sp1_ldr_escrow
    
    BUILDOSVER_STR:  6.1.7601.24354.amd64fre.win7sp1_ldr_escrow.190108-1700
    
    ANALYSIS_SESSION_ELAPSED_TIME:  904
    
    ANALYSIS_SOURCE:  KM
    
    FAILURE_ID_HASH_STRING:  km:x64_0x19_3_nt!exfreepool+4fb
    
    FAILURE_ID_HASH:  {508e5570-3f70-aa7e-0a8b-e9a016213682}
    
    Followup:     Pool_corruption
    ---------
    
    
    1: kd> !pool fffff80003c582d0
    unable to get nt!ExpHeapBackedPoolEnabledState
    Pool page fffff80003c582d0 region is Unknown
    *fffff80003c58000 size:  c50 previous size:    0  (Allocated) *.... (Protected)
            Owning component : Unknown (update pooltag.txt)
    
    fffff80003c58c50 doesn't look like a valid small pool allocation, checking to see
    if the entire page is actually part of a large page allocation...
    
    fffff80003c58c50 is not a valid large pool allocation, checking large session pool...
    fffff80003c58c50 is not valid pool. Checking for freed (or corrupt) pool
    Bad allocation size @fffff80003c58c50, zero is invalid
    
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,852

    Is deleting the RtlFreeUnicodeString the ONLY change you made? There are several possible problems here. Remember, Scott advised you to make a copy of the string. If you're just copying the pointer to the string, you have no guarantee that the pointer is going to remain valid. If the process table changes, it's quite possible that pointer could now be pointing into random memory.

    Second, where are you copying the string to? What's the definition of FullImageName in IMAGE_CALLBACK_INFO2? Is that a fixed-length array? You aren't checking that there is enough room in that array before doing your copy. Or, even worse, is it defines as a wchar_t *? Because that means you are using a user-mode address without any validation, and that's always dangerous.

    Tim Roberts, [email protected]
    Software Wizard Emeritus

  • user555user555 Member Posts: 8
    edited March 2019

    @Tim_Roberts said:
    Is deleting the RtlFreeUnicodeString the ONLY change you made?

    Initially that was the only thing I did, hence the !analyze output above. I realised my mistake though and used RtlInitUnicodeString(&info->FullImageName, FullImageName->Buffer);. Is RtlInitUnicodeString enough to copy a string or should I allocate a buffer with ExAllocatePoolWithTag? I tried the former and freed it with RtlFreeUnicodeString(&info->FullImageName); just to end up with another corrupt pool crash.

    @Tim_Roberts said:
    Second, where are you copying the string to? What's the definition of FullImageName in IMAGE_CALLBACK_INFO2? Is that a fixed-length array? You aren't checking that there is enough room in that array before doing your copy. Or, even worse, is it defines as a wchar_t *? Because that means you are using a user-mode address without any validation, and that's always dangerous.

    typedef struct _ImageCallbackInfo2
    {
        UNICODE_STRING FullImageName;
        HANDLE ProcessId;
        SINGLE_LIST_ENTRY LinkField;
    } IMAGE_CALLBACK_INFO2, *PIMAGE_CALLBACK_INFO2;
    
  • Scott_Noone_(OSR)Scott_Noone_(OSR) Administrator Posts: 3,683

    You need to actually allocate a buffer so that you can free it. Something like:

    dest.Length = 0;
    dest.MaximumLength = src->Length;
    dest.Buffer = ExAllocatePoolWithTag(src.Length);
    
    RtlCopyUnicodeString(&dest, src);
    

    You should then free dest.Buffer with ExFreePool.

    -scott
    OSR

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,852
    via Email
    user555 wrote:
    > typedef struct _ImageCallbackInfo2 {
    > UNICODE_STRING FullImageName;
    > HANDLE ProcessId;
    > SINGLE_LIST_ENTRY LinkField;
    > } IMAGE_CALLBACK_INFO2, *PIMAGE_CALLBACK_INFO2;

    OK, but you're passing an IMAGE_CALLBACK_INFO structure from your
    user-mode app, and that also contains a UNICODE_STRING field, which
    contains a pointer.  Who is setting that up?  What kind of a pointer are
    you going to get?  If the user-mode app is setting up that structure
    with a pointer to a user-mode  buffer, then you have a problem.  You
    can't allow your kernel driver to use unprotected user-mode addresses
    without validation.  And, of course, you can't hand a kernel-mode
    pointer back to a user-mode app.

    The better solution would be:

        typedef struct {
            HANDLE ProcessId;
            wchar_t FullImageName[1024];
        } IMAGE_CALLBACK_INFO;

    Now, you don't have any pointers at all.  The memory access is all
    handled by the I/O manager, so it is known to be valid when you get into
    your ioctl handler.  You'll want to make sure the image name fits before
    doing the copy, and you can return a STATUS_BUFFER_OVERFLOW to tell the
    app to hand you a bigger buffer.

    Tim Roberts, [email protected]
    Software Wizard Emeritus

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 9-13 Sept 2024 Live, Online
Developing Minifilters 15-19 July 2024 Live, Online
Internals & Software Drivers 11-15 Mar 2024 Live, Online
Writing WDF Drivers 20-24 May 2024 Live, Online