View Single Post
Old 26th November 2013, 17:35   #259
Junior Member
Join Date: Nov 2013
Posts: 2
First of all, sorry if my memory is letting me down. I've been investigating this quite a while ago and may be wrong in details. However we made a custom LockedList build with this fix applied, put it to use in production and never seen the huge percent issue since then.

AFAIR the issue may only appear when working with LockedList::AddFile. LockedList code has a function called GetSystemHandleInformation that retrieves a structure containing all open system handles.

GetSystemHandleInformation is typically called twice: first time from GetSystemHandlesCount to get the total number of open handles and set maximum value of the progress bar, second time to iterate through handles and find processes locking the file.

Now imagine you use LockedList::AddFile and LockedList::AddModule together in your setup. For AddModule LockedList uses a totally different approach and enumerates process modules only. To set maximum value of the progress bar it calls GetSystemHandlesCount to retrieve total number of handles (say, 50000) and GetSystemModulesCount to retrieve number of modules (say, 500) and sets progress bar top to 50500. Then it starts the actual search and iterates through handles and modules.

The problem is GetSystemHandlesCount has a mistake and may fail in either of these calls. Fail in the first call will cause progress bar maximum to be 500 instead of 50500 so during the search it will go over the top and show huge percent values. Fail in the second call will break the search and file lockers will not be found.

Now about the mistake. GetSystemHandleInformation calls WinAPI function called NtQuerySystemInformation. Again it does this twice: first time to know the size of the structure to contain system handles, second time to fill the structure. The problem is number of open handles may increase between these calls so that memory allocated for SYSTEM_HANDLE_INFORMATION structure will not be enough to hold it. This is likely to happen during intense activity of other processes. In that case NtQuerySystemInformation will return STATUS_INFO_LENGTH_MISMATCH and invoker must take care of this by increasing the buffer and trying again, but LockedList does not. See for reference:

An unusual aspect of calling NtQuerySystemInformation with SystemHandleInformation is that if you supply a buffer which is too small, it returns STATUS_INFO_LENGTH_MISMATCH (0xc0000004) instead of giving you the correct buffer size in ReturnLength. This means you will have to guess the buffer size. A common technique is to call NtQuerySystemInformation in a loop until it succeeds with STATUS_SUCCESS (0), reallocating and doubling the buffer size each time it fails with STATUS_INFO_LENGTH_MISMATCH.
This is what I added to GetSystemHandleInformation:

while(size < max_size)
// Allocate required memory.
pSysHandleInformation = (SYSTEM_HANDLE_INFORMATION*)GlobalAlloc(GPTR, size);
if (pSysHandleInformation == NULL)
return NULL;

// Query the objects (system wide).
NTSTATUS res = NtQuerySystemInformation(SystemHandleInformation, pSysHandleInformation, size, NULL);
if (NT_SUCCESS(res))
pSysHandleInformation = NULL;


size *= 2;

ntstatus.h has to be included for checking return codes. See full diff for attached.

I suggest this to be included into project mainline.
Attached Files
File Type: txt SystemEnum.txt (2.5 KB, 343 views)
voidcast is offline   Reply With Quote