пятница, 29 июня 2018 г.

interesting case of memory leak

after three weeks of work service osqueryd.exe consumed about 150 mb of memory. so I made full memory dump with process explorer and run !heap -l in windbg
298991 string in log ! lets write quick and ditry perl script to calculate sizes of leaked blocks:
my $state = 0;
my($str, %dict, $size);
while( $str = <> )
{
  chomp $str;
  last if ( $str eq '' );
  if ( ! $state )
  {
    $state = 1 if ( $str =~ /^-----/ );
    next;
  }
  $str = substr($str, 72, 10);
  $str =~ s/^\s+//g;
  $str =~ s/\s+$//g;
  $size = hex($str);
  next if ( !$size );
  $dict{$size} += 1;
}

# dump results
my $iter;
foreach $iter ( sort { $dict{$b} <=> $dict{$a} } keys %dict )
{
  printf("%X %d\n", $iter, $dict{$iter});
}
results are encouraging:
A0 80659
4D0 80596
20 54034
10 53737


it seems that we have pairs of leaked objects with size 0xa0 and 0x4d0. with my partial structs matcher we can find two appropriate candidates - WTSINFOA (size 0x90) and WTSCLIENTA (size 0x4b0). Also can check in IDA content of some blocks - yep, almost certainly they are our found structs.

Qiuck search in source code gives only place where WTSINFOA used - function genLoggedInUsers:
  PWTS_SESSION_INFO_1 pSessionInfo;
  unsigned long count;
  unsigned long level = 1;
  auto res = WTSEnumerateSessionsEx(
      WTS_CURRENT_SERVER_HANDLE, &level, 0, &pSessionInfo, &count);
  for (size_t i = 0; i < count; i++) {
    Row r;

    char* sessionInfo = nullptr;
    unsigned long bytesRet = 0;
    res = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
                                     pSessionInfo[i].SessionId,
                                     WTSSessionInfo,
                                     &sessionInfo,
                                     &bytesRet);
 ...

    auto wtsSession = (PWTSINFO)sessionInfo;
 ...
    if (clientInfo != nullptr) {
      WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, clientInfo, count);
      clientInfo = nullptr;
    }
    if (sessionInfo != nullptr) {
      WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, sessionInfo, count);
      sessionInfo = nullptr;
    }
  }

all seems fine - memory allocated in WTSQuerySessionInformation and free at end of cycle. Can you guess what wrong here ? ok, I could not too
So finally rtfm:
To free the returned buffer, call the WTSFreeMemory function

Yep, this memory leak caused wrong using of windows API functions. Interesting would find this bug existing code analyzers like pvs studio or coverity ?

2 комментария: