False Positive Kernel and Memory Leakage with COM and OleDB SQL 2008 Native Client?

False Positive Kernel and Memory Leakage with COM and OleDB SQL 2008 Native Client?

Dear all,

We are busy checking for Memory leaks in our Application with a trail of Intel Parallel C/C++ Studio with Inspector 2011 XE.

It looks we are getting lots of false Positives in our COM/OleDB SQL Provider.

I even wrote a small test program (See code beneath) witch causes lots of kernel and memory leaks originating in the Microsoft SQL OleDB Native Client 2008 dll.

The code:

// compile with: ole32.lib
//#define UNICODE
//#define _UNICODE
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250   // to include correct interfaces
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define NUMROWS_CHUNK  5
// AdjustLen supports binding on four-byte boundaries.
_inline DBLENGTH AdjustLen(DBLENGTH cb) { 
   return ( (cb + 3) & ~3 );
}
// Get the characteristics of the rowset (the IColumnsInfo interface).
HRESULT GetColumnInfo ( IRowset* pIRowset, 
                        DBORDINAL* pnCols, 
                        DBCOLUMNINFO** ppColumnsInfo,
                        OLECHAR** ppColumnStrings ) {
   usoft::CCountedPtr pIColumnsInfo;
   HRESULT hr;
   *pnCols = 0;
   if (FAILED(pIRowset->QueryInterface(IID_IColumnsInfo, (void**) &pIColumnsInfo.PtrNullified())))
      return (E_FAIL);
   hr = pIColumnsInfo->GetColumnInfo(pnCols, ppColumnsInfo, ppColumnStrings);
   if (FAILED(hr)) {}   /* Process error */ 
   //pIColumnsInfo->Release();
   return (hr);
}
// Create binding structures from column information. Binding structures
// will be used to create an accessor that allows row value retrieval.
void CreateDBBindings ( DBORDINAL nCols, 
                        DBCOLUMNINFO* pColumnsInfo, 
                        DBBINDING** ppDBBindings,
                        BYTE** ppRowValues ) {
   ULONG nCol;
   DBLENGTH cbRow = 0;
   DBLENGTH cbCol;
   DBBINDING* pDBBindings;
   BYTE* pRowValues;
   pDBBindings = new DBBINDING[nCols];
   if (!(pDBBindings /* = new DBBINDING[nCols] */ ))
      return;
   for ( nCol = 0 ; nCol < nCols ; nCol++ ) {
      pDBBindings[nCol].iOrdinal = nCol + 1;
      pDBBindings[nCol].pTypeInfo = NULL;
      pDBBindings[nCol].pObject = NULL;
      pDBBindings[nCol].pBindExt = NULL;
      pDBBindings[nCol].dwPart = DBPART_VALUE;
      pDBBindings[nCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
      pDBBindings[nCol].eParamIO = DBPARAMIO_NOTPARAM;
      pDBBindings[nCol].dwFlags = 0;
      pDBBindings[nCol].wType = pColumnsInfo[nCol].wType;
      pDBBindings[nCol].bPrecision = pColumnsInfo[nCol].bPrecision;
      pDBBindings[nCol].bScale = pColumnsInfo[nCol].bScale;
      cbCol = pColumnsInfo[nCol].ulColumnSize;
      switch (pColumnsInfo[nCol].wType) {
      case DBTYPE_STR: {
            cbCol += 1;
            break;
         }
      case DBTYPE_WSTR: {
            cbCol = (cbCol + 1) * sizeof(WCHAR);
            break;
         }
      default:
         break;
      }
      pDBBindings[nCol].obValue = cbRow;
      pDBBindings[nCol].cbMaxLen = cbCol;
      cbRow += AdjustLen(cbCol);
   }
   pRowValues = new BYTE[cbRow];
   *ppDBBindings = pDBBindings;
   *ppRowValues = pRowValues;
}
int main() {
   usoft::CCountedPtr pIMalloc;
   usoft::CCountedPtr pISourceRowset;    
   usoft::CCountedPtr pIRowset;        
   usoft::CCountedPtr pIAccessor;
   DBBINDING* pDBBindings = NULL;            
   HROW* pRows = new HROW[500];    
   HACCESSOR hAccessorRetrieve = NULL;        
   ULONG DSSeqNumber = 0;
   HRESULT hr;
   DBORDINAL nCols;
   DBCOLUMNINFO* pColumnsInfo = NULL;
   OLECHAR* pColumnStrings = NULL;
   DBBINDSTATUS* pDBBindStatus = NULL;
   BYTE* pRowValues = NULL;
   DBCOUNTITEM cRowsObtained;
   ULONG iRow;
   char* pMultiByte = NULL;
   short* psSourceType = NULL;
   BYTE* pDatasource = NULL;
   if (!pRows)
      return (0);
   // Initialize COM library.
   CoInitialize(NULL);
   // Retrieve the task memory allocator
   CoGetMalloc( MEMCTX_TASK, &pIMalloc.PtrNullified() );
   // Initialize the enumerator.
   if (FAILED(CoCreateInstance(CLSID_SQLNCLI10_ENUMERATOR, 
                               NULL,
                               CLSCTX_INPROC_SERVER, 
                               IID_ISourcesRowset, 
                               (void**)&pISourceRowset.PtrNullified()))) {
      // Process error.
      return TRUE;
   }
   // Retrieve the source rowset.
   hr = pISourceRowset->GetSourcesRowset(NULL, IID_IRowset, 0, NULL, (IUnknown**)&pIRowset.PtrNullified());
   //pISourceRowset->Release();
   if (FAILED(hr)) {
      // Process error.
      return TRUE;
   }
   // Get the description of the enumerator's rowset.
   if (FAILED(hr = GetColumnInfo(pIRowset, &nCols, &pColumnsInfo, &pColumnStrings))) {
      // Process error.
      goto SAFE_EXIT;
   }
   // Create the binding structures.
   CreateDBBindings(nCols, pColumnsInfo, &pDBBindings, &pRowValues);
   pDBBindStatus = new DBBINDSTATUS[nCols];
   if (sizeof(TCHAR) != sizeof(WCHAR))
      pMultiByte = new char[pDBBindings[0].cbMaxLen];
   if (FAILED(pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor.PtrNullified()))) {
      // Process error.
      goto SAFE_EXIT;
   }
   // Create the rowset accessor.
   if (FAILED(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 
                                              nCols,
                                              pDBBindings, 
                                              0, 
                                              &hAccessorRetrieve, 
                                              pDBBindStatus))) {
      // Process error.
      goto SAFE_EXIT;
   }
   // Process all the rows, NUMROWS_CHUNK rows at a time.
   while (SUCCEEDED(hr)) 
   {
      hr = pIRowset->GetNextRows(NULL, 0, NUMROWS_CHUNK, &cRowsObtained, &pRows);
      if( FAILED(hr)) {
         // process error
      }
      if (cRowsObtained == 0 || FAILED(hr))
         break;
      for (iRow = 0 ; iRow < cRowsObtained ; iRow++) {
         // Get the rowset data.
         if (SUCCEEDED(hr = pIRowset->GetData(pRows[iRow], hAccessorRetrieve, pRowValues))) {
            psSourceType = (short *)(pRowValues + pDBBindings[3].obValue);
            if (*psSourceType == DBSOURCETYPE_DATASOURCE) {
               DSSeqNumber = DSSeqNumber + 1;   // Data source counter.
               pDatasource = (pRowValues + pDBBindings[0].obValue);
               if ( sizeof(TCHAR) != sizeof(WCHAR) ) {
                  WideCharToMultiByte(CP_ACP, 
                                      0,
                                      (WCHAR*)pDatasource, 
                                      -1, 
                                      pMultiByte,
                                      static_cast(pDBBindings[0].cbMaxLen), 
                                      NULL, 
                                      NULL);
                  printf( "DataSource# %dtName: %Sn", 
                          DSSeqNumber, 
                          (WCHAR *) pMultiByte );
               }
               else {
                  printf( "DataSource# %dtName: %Sn", 
                          DSSeqNumber, 
                          (WCHAR *) pDatasource );
               }
            }
         }
      }
      pIRowset->ReleaseRows(cRowsObtained, pRows, NULL, NULL, NULL);
   }
SAFE_EXIT:
   // Do the clean-up.
   if ( pRows )
      delete [] pRows;
   if ( pRowValues )
      delete [] pRowValues;
   if ( pDBBindings )
      delete [] pDBBindings;
   if ( pDBBindStatus )
      delete [] pDBBindStatus;
   if ( pColumnsInfo )
      pIMalloc->Free(pColumnsInfo);
   if ( pColumnStrings )
      pIMalloc->Free(pColumnStrings);
   pISourceRowset = NULL;
   pIRowset = NULL;
   pIAccessor = NULL;
   pIMalloc = NULL;
   // Release COM library.
   CoUninitialize();
   return TRUE;
}

The results from Intel Inspector 2011 XE:

Can anybody tell me if these COM+ / OleDB (Kernel/Memory)Leakages are false positives or not?

Kind regards,

Henry Roeland

8 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Henry,

Quoting driekus77...
Can anybody tell me if these COM+ / OleDB (Kernel/Memory) Leakages are false positives or not?

...
IMalloc
ISourcesRowset
IRowset
IAccessor
...

I don'tthink that there areany memory leaks in these fundamental Generic and OLE DB COM interfaces.
You can doverification with a CRTset ofmemory leaks detection functions:

http://software.intel.com/en-us/forums/showthread.php?t=105996&o=a&s=lr Post #8

Howeverit will onlycheck your application.

Best regards,
Sergey

Hi Sergey,

Thanks for you quick response.

I fixed one of the "Unitialized memory access" but I have another one thats still bordering me:

The code is an example from Microsoft: http://msdn.microsoft.com/en-us/library/ms403324.aspx

I've added CCountedPtr and Boost smart Pointers and thats it.

I don't know who to trust the most Intel or Microsoft? :-)

Because the rest of the &...PtrNullified() calls look oke its seems to be something with the GetSourcesRowset function???

The API description of this function tells that the 5th parameter is an [in] :http://msdn.microsoft.com/en-us/library/ms711200

My jugement is that the GetSourcesRowset function is reading the Uninitialized memory before it is written???

Or do I have to Initialize the COM+ Object cpIRowset with something ?? (I have no qlue how to do this because its an Interface).

Is my jugement right?

Hope to hear from you.

Best regards,

Henry Roeland

I've tested also with the newest SQL Server 2012 Native Client (v11).

This one has 2 errors less that the SQLOLEDB driver I used in above print.

This tells me that Microsoft has some errors in this area.

But still memory and kernel leak on "CoInitialize(NULL)" and "CoUninitialize()".

Best regards,

Henry Roeland

Quoting driekus77...This tells me that Microsoft has some errors in this area.But still memory and kernel leak on
"CoInitialize(NULL)" and "CoUninitialize()".

Hi Henry,

That was discussed a couple of weeks ago on the forumand Intel Inspector XE really reports some "memory leaks"
on these two fundamental ( sorry for mentioning and stressingthis again )COM API functions. Do you see that I used
quotation-marks around memory leaks expression?

A test-case is as follows:
...
_tprintf( _T("Sub-Test Started\n") );

for( int t = 0; t < 65536; t++ ) // 2^16
{
// Sub-Test 1 - No Memory Leaks
::CoInitialize(NULL );
::Sleep( 1 );
::CoUninitialize();

// Sub-Test 2 - Forced Memory Leaks ( should allocate 1GB of memory )
// int *pData = ( int * )malloc(4096 * sizeof( int ) );
}

_tprintf( _T("Sub-Test Completed\n") );
...

and take a look at the Windows Task Manager 'Performance' property page. I tested that test-casemany times and
I have not seen any increase inallocated memory for a test application when a'Sub-Test 2' is commented.
Than, uncomment a line with a forced memory leaks 'Sub-Test 2'and repeat the test.

Best regards,
Sergey

Quoting Sergey Kostrov...take a look at the Windows Task Manager 'Performance' property page. I tested that test-casemany times and
I have not seen any increase inallocated memory for a test application when a'Sub-Test 2' is commented.
Than, uncomment a line with a forced memory leaks 'Sub-Test 2'and repeat the test.
Here is a screenshot of how it should look like:

Quoting driekus77...I don't know who to trust the most Intel or Microsoft? :-) ...

Trust both companies and verify both companies. Intel and Microsoft are spending billions of dollars on R&D
every year and they try todeliver as better as possible, in terms of quality, software. However, "...nobody is perfect..." and
sometimes errors are made. So, do your own verifications. Hereare pseudo-codes on a set oftest-cases:

vodsi main( void )
{
// Test #1
for( 65536 times ) // Monitor 'Performance' property page inWindows Task Manager
{
CoInitialize( NULL )
CoUnitialize()
}

// Test #2
for(32768 times ) // Monitor 'Performance' property page inWindows Task Manager
{
CoInitialize( NULL )
CreateInstance( IID_Interface1, pInterface1)
ReleaseInterface( pInterface1 )
CoUnitialize()
}

// Test #3
for(16384 times ) // Monitor 'Performance' property page inWindows Task Manager
{
CoInitialize( NULL )
CreateInstance( IID_Interface1, pInterface1)
pInterface1->Method1( ... )
ReleaseInterface( pInterface1 )
CoUnitialize()
}

// Test #4
for(8192 times ) // Monitor 'Performance' property page inWindows Task Manager
{
CoInitialize( NULL )
CreateInstance( IID_Interface1, pInterface1)
pInterface1->Method1( ... )
CreateInstance( IID_Interface2, pInterface2)
pInterface2->Method2( ... )
ReleaseInterface( pInterface2 )
ReleaseInterface( pInterface1 )
CoUnitialize()
}

// and so on
}

Quoting driekus77...Or do I have to Initialize the COM+ Object cpIRowset with something ?? (I have no qlue how to do this because its an Interface)...

I just checkedtopics 'IRowset Interface' and'Fetching Rows' in'SQL Server 2005Books Online' and I haven't found
anything about some additional initialization of IRowset OLE DB interface. A class-wrapper of the interface ( generated
from a tlb-library, for example ) should doinitializations of allmembers. If Microsoft software developers
missed to initialize some member(s) of the class-wrapper than Intel Inspector XE could report it.

Best regards,
Sergey

Leave a Comment

Please sign in to add a comment. Not a member? Join today