c00000fd
c00000fd

Reputation: 22265

Unexpected behavior of GetTokenInformation on Windows XP 64-bit

I'm curious if I'm missing something in understanding APIs that one should call first with NULL parameters to retrieve the required buffer size and then call them again once the buffer was allocated.

My understanding was that this buffer length will not change from the 1st call to the 2nd. (Well, assuming that we're not in the "race condition" situation, which is another story.)

So here's a simple real-life example that I'm observing on Windows XP SP2 (64-bit). The method below acquires current user's SID:

HANDLE hToken;
if(OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
    DWORD dwSize = 0;
    if(!GetTokenInformation(hToken, TokenUser, NULL, dwSize, &dwSize) &&
        ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        BYTE* pb = new (std::nothrow) BYTE[dwSize];
        if(pb)
        {
            TOKEN_USER* pTU = (TOKEN_USER*)pb;
            DWORD dwSize2;
            if(GetTokenInformation(hToken, TokenUser, pTU, dwSize, &dwSize2) &&
                dwSize == dwSize2)
            {
                LPWSTR pName;
                if(ConvertSidToStringSid(pTU->User.Sid, &pName))
                {
                    //Got it!
                    _tprintf(L"User SID=%s\n", pName);

                    LocalFree(pName);
                }
            }

            delete[] pb;
        }
    }

    CloseHandle(hToken);
}

That part where I do dwSize == dwSize2 after the second call to GetTokenInformation, well it fails because dwSize is returned as 44 from the first call to GetTokenInformation, and then dwSize2 is returned as 36 from the 2nd call.

Is this behavior normal?

Upvotes: 0

Views: 257

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596226

It is not unheard of for an API to return a larger value when retrieving the needed byte size (maybe it has be rounded up for alignment purposes, etc), and then the same API to return a smaller value when retrieving the actual data, since it is reporting how many bytes were actually written into the allocated memory. You don't have to compare the two sizes, trust that if the function says it succeeded than it really did and that the written size is <= the allocated size.

If you are worried about a race condition that alters the needed byte size, use a loop to allocate the memory until successful:

HANDLE hToken;
if (OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
    BYTE* pb = NULL;
    DWORD dwSize = 0;
    TOKEN_USER* pTU = NULL;
    BOOL bRet;

    do
    {
        bRet = GetTokenInformation(hToken, TokenUser, pTU, dwSize, &dwSize);
        if (bRet)
            break;

        if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            break;

        delete[] pb;
        pb = new (std::nothrow) BYTE[dwSize];
        if (!pb)
            break;

        pTU = (TOKEN_USER*)pb;
    }
    while (true);

    if (bRet)
    {
        // use pTU as needed...
    }

    delete[] pb;
    CloseHandle(hToken);
}

Upvotes: 2

dvasanth
dvasanth

Reputation: 1377

As long the dwSize is greater than dwSize2, it should be fine.

Upvotes: 1

Related Questions