ObQueryTypeInfo and NtQueryObject buffer overrun in Windows 8

**** Update: **** Microsoft is now aware of this bug.

Here’s some output from WinDbg on Windows 8 while I was debugging a driver:

Spot the difference

These two UNICODE_STRINGs are from the OBJECT_TYPE structures of the Section and TmTx (transaction) object types. Can you spot the difference between these two strings? The Section string’s MaximumLength includes two extra bytes for a null terminator, while the TmTx string’s MaximumLength doesn’t.

Now take a look at the code for ObQueryTypeInfo, which is called from NtQueryObject when you pass in the ObjectTypeInformation information class. Look out for the buffer overrun at the end. Also notice that the string “TmTx” happens to be 8 bytes long (when using WCHARs).

#define ALIGN_UP(Length, Type) (((Length) + sizeof(Type) - 1) & ~(sizeof(Type) - 1))

NTSTATUS ObQueryTypeInfo(
__in POBJECT_TYPE ObjectType,
__out_bcount(Length) POBJECT_TYPE_INFORMATION ObjectTypeInfo,
__in ULONG Length,
__out PULONG ReturnLength
)
{
    NTSTATUS status;

    __try
    {
        *ReturnLength += sizeof(OBJECT_TYPE_INFORMATION) + ALIGN_UP(ObjectType->Name.MaximumLength, ULONG_PTR);

        if (Length < *ReturnLength)
        {
            status = STATUS_INFO_LENGTH_MISMATCH;
        }
        else
        {
            ObjectTypeInfo->TotalNumberOfObjects = ObjectType->TotalNumberOfObjects;
            // ...
            ObjectTypeInfo->DefaultNonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;

            ObjectTypeInfo->TypeName.Buffer = (PWSTR)(ObjectTypeInfo + 1);
            ObjectTypeInfo->TypeName.Length = ObjectType->Name.Length;
            ObjectTypeInfo->TypeName.MaximumLength = ObjectType->Name.MaximumLength;

            memcpy(ObjectTypeInfo + 1, ObjectType->Name.Buffer, ObjectType->Name.Length);
            ((PWSTR)(ObjectTypeInfo + 1))[ObjectType->Name.Length / sizeof(WCHAR)] = 0; // **** Oops! ****

            status = STATUS_SUCCESS;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        status = GetExceptionCode();
    }

    return status;
}

The solution is pretty simple. Until Microsoft fixes this bug, if you’re calling NtQueryObject with some ObjectInformationLength, make sure you have ObjectInformationLength + 2 bytes allocated in the buffer that you are passing in.

Leave a Reply