Application Verifier Locks 0x201 Active Critical Section

Suppose you are trying to get your application Vista Certified or OEM Ready. You test your application with Application Verifier and you see this error in the log:

"<avrf:logEntry Time="2008-10-15 : 14:18:33" LayerName="Locks" StopCode="0x201" Severity="Error">

<avrf:message>Unloading DLL containing an active critical section. </avrf:message>

 

 

Now what? From the log, this is difficult to debug.

Little history how you might have arrived here. When you checked the Basics errors in Application Verifier, you got a warning mentioning something along the lines of "For the basic tests you need a debugger". This is because Application Verifier sets break points. If you don't have a default debugger, those breakpoints are ignored hence you need a debugger. Best thing you can do is run your application from the debugger: preferably Windbg since that has some useful extensions that help you navigate the process' memory. You can download it here. After installing and running you can under File | Open Executable navigate to your application. Hit F5, reproduce the error and break into the debugger.

Next best thing is to set Windbg as the interactive debugger (Windbg –I from elevated command prompt). When the application sets a break point (the application verifier layer in this case), the system will see if there is an interactive debugger. If it finds one, it launches it with your application as the target.

Good. So now both ways have taken you to this point:

(11e4.1604): Break instruction exception - code 80000003 (!!! second chance !!!)
eax=000001ff ebx=75988844 ecx=77dde7c4 edx=00000000 esi=00000000 edi=000001ff
eip=77db0004 esp=003ee8fc ebp=003eeafc iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!DbgBreakPoint:
77db0004 cc int 3

You of course need symbols. I explained how here.

Now comes the interesting part. Learn and appreciate the power of "!analyze –v":

0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
*** WARNING: Unable to verify checksum for e:\Playground\AVRF\x201\Debug\x201b.exe
APPLICATION_VERIFIER_LOCKS_LOCK_IN_UNLOADED_DLL (201)
Unloading DLL containing an active critical section.
This stop is generated if a DLL has a global variable containing a critical section
and the DLL is unloaded but the critical section has not been deleted. To debug
this stop use the following debugger commands:
$ du parameter3 - to dump the name of the culprit DLL.
$ .reload dllname or .reload dllname = parameter4 - to reload the symbols for that DLL.
$ !cs -s parameter1 - dump information about this critical section.
$ ln parameter1 - to show symbols near the address of the critical section.
This should help identify the leaked critical section.
$ dps parameter2 - to dump the stack trace for this critical section initialization.
Arguments:
Arg1: 718933cc, Critical section address.
Arg2: 00f8a104, Critical section initialization stack trace.
Arg3: 05be6fe4, DLL name address.
Arg4: 71870000, DLL base address.

The information is fairly self explanatory. In the process a dll is loaded. This dll initializes a critical section and then before deleting the critical section, the dll is unloaded. There are four parameters which are useful mentioned above. And then some complaining about symbols; followed by more useful information. (I think it complains about symbols because it wants private symbols for the OS. I don't have those, but the results are good anyway.)

Somewhere halfway, you'll find this:

CRITICAL_SECTION: 718933cc -- (!cs -s 718933cc)

So this is the actual critical section we're leaking. Clicking on the hyperlink gives us:

0:000> !cs -s 718933cc
-----------------------------------------
Critical section = 0x718933cc (<Unloaded_TheLib.dll>+0x233CC)
DebugInfo = 0x05c2afe0
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x00000000

Stack trace for DebugInfo = 0x05c2afe0:

0x77df99b6: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x19
0x75976936: vfbasics!AVrfpInitializeCriticalSectionCommon+0x136
0x75976ab2: vfbasics!AVrfpRtlInitializeCriticalSection+0x12
0x71881f37: <Unloaded_TheLib.dll>+0x11F37
0x7188bc28: <Unloaded_TheLib.dll>+0x1BC28
0x5e82c02c: MSVCR90D!_initterm+0x1C
0x71883131: <Unloaded_TheLib.dll>+0x13131
0x7188351e: <Unloaded_TheLib.dll>+0x1351E
0x71883451: <Unloaded_TheLib.dll>+0x13451
0x75a059c9: verifier!AVrfpStandardDllEntryPointRoutine+0x109
0x75a7859d: vrfcore!VfCoreStandardDllEntryPointRoutine+0x127
0x7598105e: vfbasics!AVrfpStandardDllEntryPointRoutine+0x10E
0x77dcfcc0: ntdll!LdrpCallInitRoutine+0x14
0x77dd9b28: ntdll!LdrpRunInitializeRoutines+0x270
0x77dd95ae: ntdll!LdrpLoadDll+0x4D5
0x77df29db: ntdll!LdrLoadDll+0x22A
0x7598164c: vfbasics!AVrfpLdrLoadDll+0x5C
0x75fc4d50: kernel32!LoadLibraryExW+0x231
0x75fc4dca: kernel32!LoadLibraryW+0x11
0x00f42d50: x201b!Cx201bDlg::OnBnClickedButton1+0x30

Hmm. Of course. The dll is gone. Which is kind of the crux of this problem. The bold line indicates where the critical section was initialized. If you have multiple critical sections, you might scratch your head which one you're leaking here. So you can try to reload the dll. There is a little snippet how you should be able to do this with .reload etc. I have never been able to do this. If you have, let me know.

What I do is spin up an instance of the application in the debugger and make sure that the dll is still loaded. (You can set breakpoints on LoadLibrary to make sure that you are just after the load, and before the Free. Subject for another blog potentially.) Then, I do a ln (as in list near) with the address from the bold line in the stack trace.

0:001> ln 71881f37
*** WARNING: Unable to verify checksum for E:\Playground\AVRF\x201\Debug\TheLib.dll
e:\playground\avrf\x201\thelib\thelib.cpp(49)+0x11
(71881ef0) TheLib!CTheLibApp::CTheLibApp+0x47 | (71881f70) TheLib!CTheLibApp::`scalar deleting destructor'

There we go. Thelib.cpp(49). Looking at that source line, takes me to the InitializeCriticalSection call of the problematic critical section.

Maarten