Microsoft KB Archive/272024

= BUG: The ATL application may crash when it releases calls that are made with _ATL_DEBUG_INTERFACES or _ATL_DEBUG_REFCOUNT defined =

Article ID: 272024

Article Last Modified on 9/29/2005

-

APPLIES TO

 Microsoft ActiveX Template Library 3.0, when used with:  Microsoft Visual C++ 6.0 Enterprise Edition

 Microsoft Visual C++ 6.0 Professional Edition

 Microsoft Visual C++ 6.0 Standard Edition 

-

<div class="notice_section">

This article was previously published under Q272024

<div class="symptoms_section">

SYMPTOMS
Release calls that are made with _ATL_DEBUG_INTERFACES or _ATL_DEBUG_REFCOUNT defined are not thread-safe. The most common symptom is an access violation (AV) under the stress of multiple threads (even if they are all STA). The crash may not occur near any ATL code.

In addition, _QIThunk::Release may access member variables after the object has been deleted regardless of your threading model. This can lead to unpredictable behavior, including an access violation. Typically, a crash related to this occurs inside _QIThunk::Release.

If you experience crashing with _ATL_DEBUG_INTERFACES defined but do not experience the crashing without it defined, then the crashing behavior may be caused by one of several ATL 3.0 bugs in the _QIThunk releasing mechanism.

<div class="cause_section">

CAUSE
There are two functions that contribute to the thread safety problems: ATL::CComModule::DeleteNonAddRefThunk and ATL::_QIThunk::Release.

The purpose of the DeleteNonAddRefThunk function is to iterate through the array of interface thunks and delete all thunks that cannot be AddRef'ed. However, DeleteNonAddRefThunk does not check whether a thunk is marked as not AddRef'able before deleting it.

The Release method's problems stem from two sources:
 * Release accesses _QIThunk object members after a call to InterlockedDecrement. In a multithreaded scenario, another thread could decrement the count after the call to InterlockedDecrement and delete the _QIThunk object. This might cause an access violation if the first thread accesses any class members.
 * Release should delete the thunk before allowing the underlying object the chance to be deleted, because once the underlying object is deleted, another thread can allocate another object and get the same address. The AddThunk function can then find the existing thunk (that is about to be deleted) and return it, instead of creating a new thunk.

<div class="resolution_section">

RESOLUTION
Replace ATL::CComModule::DeleteNonAddRefThunk and ATL::_QIThunk::Release in Atlbase.h with the following: void DeleteNonAddRefThunk(IUnknown* pUnk) {   EnterCriticalSection(&m_csObjMap); for (int i = 0; i < m_paThunks->GetSize; i++) {       if (m_paThunks->operator[](i)->pUnk == pUnk             && m_paThunks->operator[](i)->bNonAddRefThunk            ) {           delete m_paThunks->operator[](i); m_paThunks->RemoveAt(i); break; }   }    LeaveCriticalSection(&m_csObjMap); }

inline ULONG _QIThunk::Release {   if (bBreak) DebugBreak; ATLASSERT(m_dwRef > 0); IUnknown *r_pUnk          = pUnk; bool    r_bNonAddRefThunk = bNonAddRefThunk; LPCTSTR r_lpszClassName   = lpszClassName; IID     r_iid             = iid; ULONG   r_dwRef           = InterlockedDecrement(&m_dwRef); ATLTRACE(_T(&quot;%d< &quot;), r_dwRef); AtlDumpIID(r_iid, r_lpszClassName, S_OK); if (r_dwRef==0 && !r_bNonAddRefThunk) _pModule->DeleteThunk(this); r_pUnk->Release; return r_dwRef; }

<div class="status_section">

STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed in the &quot;Applies to&quot; section.

<div class="moreinformation_section">

MORE INFORMATION
The _ATL_DEBUG_INTERFACES mechanism can assist you in troubleshooting reference counting and QueryInterface-related problems in your code.

The _ATL_DEBUG_INTERFACES mechanism does this by returning an object of type ATL::_QIThunk in place of any interface you request. This object tracks reference counting for each interface. It does this by intercepting all method calls.

For any method calls but IUnknown method calls, the _QIThunk object adds no functionality. For the IUnknown method calls, the _QIThunk object does the appropriate tracking before passing the call on to the real object.

ATL::_QIThunk::Release must manage the lifetime of the _QIThunk object as well as allow the underlying object to manage its lifetime.

Most of the _QIThunk lifetime-management-related code is enclosed in a critical section. This is not possible in _QIThunk::Release because of the call to the Release method in the underlying object.

The Release method in the underlying object could wait for a second thread to terminate. If the second thread also requires a Release call or a QueryInterface call through _QIThunk, a deadlock would result.

This justifies the need to use InterlockedDecrement instead of a critical section, and also adds a level of complexity to designing a thread-safe function.

Additional query words: AV 0xC0000005

Keywords: kbtshoot kbbug kbpending KB272024

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.