WTSQuerySessionInformation() - memory leak on WinXP/2K3

From: Chuck Chopp (ChuckChopp_at_rtfmcsi.com)
Date: 07/26/04


Date: Sun, 25 Jul 2004 20:56:35 -0400

I have some code in an application that makes use of the WTS*() API
functions to gather information about terminal services sessions. This code
  was believed to have been thoroughly debugged and was running correctly on
WinNT v4.0 TSE SP6 & Win2K SP4 Server/Adv.Server for quite some time now.
Recently, an end-user of this application reported that it was leaking
memory. After some testing, I ended up creating a small console mode
application to demonstrate the problem.

The memory leak appears to be occurring within WTSQuerySessionInformation()
when the information class is WTSInitialProgram or WTSApplicationName; none
of the other information class items seems to result in the leak occurring.
  On WinXP SP1 [vanilla - no hot fixes] w/FUS disabled, these information
class items will result in the leak occurring regardless of whether the
session id specified is the console [session zero] or for a non-existent
session id. On Win2K3 [vanilla - no hot fixes], these information class
items will result in the leak occurring for the console session [session
zero], but not for any other existent or non-existent sessions.

The test program code is included here. Build it as a console mode
application and link against WTSAPI32.LIB. Run it while logged on to the
system console. To test for memory leakage, I measure the memory usage as
reported by TASKMGR.EXE before the start of the test and at the end of the
test before the program terminates. There seems to be a certain amount of
memory overhead that is incurred simply for doing *anthing* at all with the
WTS*() API functions. However, running the program several times in a row
passing in the same session id # and the same test # but with increasing
loop iterations should result in the same delta in memory utilization if
there is no memory leak. I ran it with 1 iteration, 1000 iterations and
5000 iterations for each combination of session id # and information class
item and used the results to determine that WinXP & Win2K3 are the versions
of Windows that have this problem.

Testing has shown that WTSQuerySessionInformation() leaks this information
regardless of whether or not a buffer was returned or whether the API
function call returned success or failure.

Usage is as follows:

LeakText <server-name> <session-id> <info-class> <loop-iterations>

I use "" for the local server, zero (0) for the session id, zero (0) for the
Initial Program & one (1) for the Application Name for info-class, and
values of 1, 1000 and 5000 for the loop iteration counts. The other
information classes don't cause the leak regardless of whether they return
string, numeric or complex structured data types.

Examples:

LeakTest "" 0 0 1
LeakTest "" 0 0 1000
LeakTest "" 0 0 5000
LeakTest "" 0 1 1
LeakTest "" 0 1 1000
LeakTest "" 0 1 5000

// LeakTest.cpp : Defines the entry point for the console application.
//

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

int main(int argc, char* argv[])
{
   DWORD dwSessID;
   DWORD dwError;
   LONG nRequest;
   WTS_INFO_CLASS InfoClass;
   HANDLE hServer;
   int Iterations;
   BOOL bResult;
   LPSTR szDataBuf = NULL;
   DWORD dwDataBufSize = 0;

   if (argc >= 2)
   {
   }
   else
   {
     printf("Missing server-name (required parameter)! Aborting....\n");
     return 1;
   }

   if (argc >= 3)
   {
     dwSessID = (DWORD) atol(argv[2]);
   }
   else
   {
     printf("Missing session-id (required parameter)! Aborting....\n");
     return 1;
   }

   if (argc >= 4)
   {
     nRequest = atol(argv[3]);
   }
   else
   {
     printf("Missing request (required parameter)! Aborting....\n");
     return 1;
   }

   if (argc >= 5)
   {
     Iterations = atol(argv[4]);
   }
   else
   {
     Iterations = 2500;
   }

   switch(nRequest)
   {
     case 0:
     {
       InfoClass = WTSInitialProgram;
       break;
     }

     case 1:
     {
       InfoClass = WTSApplicationName;
       break;
     }

     case 2:
     {
       InfoClass = WTSWorkingDirectory;
       break;
     }

     case 3:
     {
       InfoClass = WTSOEMId;
       break;
     }

     case 4:
     {
       InfoClass = WTSSessionId;
       break;
     }

     case 5:
     {
       InfoClass = WTSUserName;
       break;
     }

     case 6:
     {
       InfoClass = WTSWinStationName;
       break;
     }

     case 7:
     {
       InfoClass = WTSDomainName;
       break;
     }

     case 8:
     {
       InfoClass = WTSConnectState;
       break;
     }

     case 9:
     {
       InfoClass = WTSClientBuildNumber;
       break;
     }

     case 10:
     {
       InfoClass = WTSClientName;
       break;
     }

     case 11:
     {
       InfoClass = WTSClientDirectory;
       break;
     }

     case 12:
     {
       InfoClass = WTSClientProductId;
       break;
     }

     case 13:
     {
       InfoClass = WTSClientHardwareId;
       break;
     }

     case 14:
     {
       InfoClass = WTSClientAddress;
       break;
     }

     case 15:
     {
       InfoClass = WTSClientDisplay;
       break;
     }

     case 16:
     {
       InfoClass = WTSClientProtocolType;
       break;
     }

     default:
     {
       printf("Invalid request code! Aborting....\n");
       return 1;
     }
   }

   printf("Hit any key to start the test");
   getchar();
   printf("\n");

   for (int i = 1; i <= Iterations; i++)
   {
     hServer = WTSOpenServer(argv[1]);

     if (hServer)
     {

       bResult =
WTSQuerySessionInformation(hServer,dwSessID,InfoClass,&szDataBuf,&dwDataBufSize);
       dwError = GetLastError();

       if (szDataBuf)
       {
         WTSFreeMemory(szDataBuf);
         szDataBuf = NULL;
         dwDataBufSize = 0;
       }

       WTSCloseServer(hServer);
       hServer = NULL;
     }
   }

   printf("Hit any key to terminate");
   getchar();
   printf("\n");

        return 0;
}

I'd like to get some confirmation that this problem can be reproduced by
other systems besides my own test systems.

Additionally, when a Win2K3 system requests these same 2 info class items
for session id 0 on a remote Win2K system, no leak occurs, while doing the
same for session id 0 on a remote WinXP system does result in a leak. If a
WinXP system executes the same code with the same parameters, the leak
occurs for both remote Win2K systems and remote Win2K3 systems.

Based on this information, I am fairly certain that the root cause of the
problem is a defect in WTSAPI32.DLL on WinXP and Win2K3. That API library
is where the WTS*() API functions are implemented, and
WTSQuerySessionInformation() is responsible for memory allocations that
subsequently need to be freed by WTSFreeMemory(). The underlying kernel API
functions [which support terminal services] and which are wrapped up by the
WTS*() API functions do not allocate memory; instead, they expect a
user-supplied buffer to be provided in which to place their results.

-- 
Chuck Chopp
ChuckChopp (at) rtfmcsi (dot) com http://www.rtfmcsi.com
RTFM Consulting Services Inc.     864 801 2795 voice & voicemail
103 Autumn Hill Road              864 801 2774 fax
Greer, SC  29651
Do not send me unsolicited commercial email.


Relevant Pages

  • Re: memory leaks using MySQL 5.0 API
    ... >leaks, but I'm unable to grasp the reason of leak. ... - Allocate memory for a pointer variable to point at. ... and use memory returned from the MySQL API instead. ...
    (comp.unix.programmer)
  • Re: WTSQuerySessionInformation() - memory leak on WinXP/2K3
    ... >>the leak continues to consume more and more heap memory. ... > a session other than my own, the call fails with access denied (for any ... > information classes other than WTSInitialProgram and WTSApplicationName ...
    (microsoft.public.win32.programmer.kernel)
  • Re: Desktop Creation
    ... If I must store a secret I use CryptProtectDataAPI. ... But I don't understand what the resource creation is a potential security ... CreateDekstopuse the attached window station of the processus that call ... session 1 and communicate with an IPC with the SYSTEM service. ...
    (microsoft.public.win32.programmer.kernel)
  • Re: headers sent issue
    ... huge waste of resources, cpu & memory. ... You are using memory for buffering that hypothetically could be needed ... will keep the use of memory & CPU to a minimum. ... start session ...
    (comp.lang.php)
  • Re: headers sent issue
    ... huge waste of resources, cpu & memory. ... You are using memory for buffering that hypothetically could be needed ... will keep the use of memory & CPU to a minimum. ... start session ...
    (comp.lang.php)

Loading