.NET application may crash when calling function from native C++ dll


 

Symptom

===========

Customer built an ASP.NET web application using Visual Studio 2008 on Windows 2008. In the source code, it called a function which was imported from native c++ dll:

 

[The source code looks like below]

 

public partial class _Default : System.Web.UI.Page

 {

        protected void Page_Load(object sender, EventArgs e)

        {

        }

        [System.Runtime.InteropServices.DllImport(“PinvokeLib.dll“, EntryPoint = “ TestStringAsResult“, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern string TestStringAsResult();

                ……

        protected void Button_Click(object sender, EventArgs e)

        {

            string str = TestStringAsResult();

        }

}

 

Above code seems working fine during debugging when “Use Visual Studio Development Server” in Web properties. However, when choose the web properties to “Use IIS Web Server”,  the web page seems hang and returns server internal error message. This issue will not occur on Windows 2003.

 

Troubleshoot

===========

We got the source code of native c++ dll and the related method was written like below:

 

extern “C” PINVOKELIB_API char * TestStringAsResult()

{

   return “Test String”;

}

 

We rebuilt the native dll and web application with VS 2008 on a test Windows 2008 server. When we ran the web application, w3wp.exe crashed and WerFault.exe (Windows Error Reporting service) showed up trying to record logs. In the application event log, we found 0xc0000374 exception:

Log Name:      Application

Source:        Application Error

Date:          12/12/2009 3:37:00 AM

Event ID:      1000

Task Category: (100)

Level:         Error

Keywords:      Classic

User:          N/A

Computer:      Computername

Description:

Faulting application w3wp.exe, version 7.0.6001.18000, time stamp 0x47919413, faulting module ntdll.dll, version 6.0.6001.18000, time stamp 0x4791a7a6, exception code 0xc0000374, fault offset 0x000b015d, process id 0x10e0, application start time 0x01ca7b1f684ff683.

 

0xc0000374 indicates that there is heap corruption issue existed. We used DebugDiag to catch w3wp.exe crash dump file and found below call stack:

 

0:023> kL200

ChildEBP RetAddr 

052deaf4 77df0d68 ntdll!RtlReportCriticalFailure+0x5b

052deb04 77df0e56 ntdll!RtlpReportHeapFailure+0x21

052deb38 77db0531 ntdll!RtlpLogHeapFailure+0xa1

052deb64 7680c56f ntdll!RtlFreeHeap+0x60

052deb78 77c3dc2c kernel32!HeapFree+0x14

052deb8c 77c3dc53 ole32!CRetailMalloc_Free+0x1c

052deb9c 7a0dce06 ole32!CoTaskMemFree+0x13

052debac 7a0e06c2 mscorwks!DefaultMarshalOverrides<CSTRMarshalerBase>::ReturnCLRFromNative+0x35

052dede8 79e957c3 mscorwks!RunML+0x949

052dee68 79e956b7 mscorwks!NDirectGenericStubPostCall+0x194

052deef4 79e85686 mscorwks!NDirectGenericStubReturnFromCall+0x1f

052deefc 051f0678 mscorwks!NDirectSlimStubWorker2+0x13d

WARNING: Frame IP not in any known module. Following frames may be wrong.

052def38 04836dee 0x51f0678

052def50 04836fdc System_Web_ni+0x276dee

00000000 00000000 System_Web_ni+0x276fdc

 

0:023> !clrstack

OS Thread Id: 0xb4c (23)

ESP       EIP    

052def14 77df015d [NDirectMethodFrameGeneric: 052def14] WebApplication3._Default.__PBExternal_fnreturnenstra()

052def24 051f0678 WebApplication3._Default.Button1_Click(System.Object, System.EventArgs)

052def44 04836dee System.Web.UI.WebControls.Button.OnClick(System.EventArgs)

052def58 04836fdc System.Web.UI.WebControls.Button.RaisePostBackEvent(System.String)

……

052df2ec 04663072 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)

052df2f8 0466157b System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

 

We could see that CoTaskMemFree was called to free unmanaged memory in this situation. The problem is due to CLR use different protocol to allocate memory and free memory between managed code and unmanaged code. According to below MSDN article, when CLR marshals unmanaged memory to the string, it will always invoke CoTaskMemFree method to free the unmanaged memory:

“Memory Management with the Interop Marshaler”

<http://msdn.microsoft.com/en-us/library/f1cf4kkz(VS.80).aspx>

 

Since the unmanaged memory is not allocate by CoTaskMemAlloc, it will crashed by

ntdll!RtlpLowFragHeapFree checking. This issue will also happen if we use NEW or malloc function to allocate memory in C++ dll. Below code will fail as well:

 

TCHAR* WINAPI TestStringAsResult()

{

TCHAR *ret;

ret = new TCHAR[10];

wcscpy(ret, L”test”);

return ret;

}

 

To fix this problem, we need to explicitly call CoTaskMemAlloc method to allocate memory in native dll:

 

#include <objbase.h>

……

extern “C” PINVOKELIB_API char * TestStringAsResult()

{

    STRSAFE_LPWSTR result = (STRSAFE_LPWSTR)CoTaskMemAlloc( 64 );

    StringCchCopy( result, sizeof(result) , (STRSAFE_LPWSTR)”This is return value” );

    return (char *) result;

}

 

If the unmanaged memory is not allocated with the CoTaskMemAlloc method and could not be changed, we must use an IntPtr and free the memory manually using the appropriate method. The declaration in c# should be changed from:

 

[System.Runtime.InteropServices.DllImport(“PinvokeLib.dll“, EntryPoint = “ TestStringAsResult“, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern string TestStringAsResult();

 

To:

 

[System.Runtime.InteropServices.DllImport(“PinvokeLib.dll“, EntryPoint = “ TestStringAsResult“, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern IntPtr TestStringAsResult();

 

Another Question

===========

Why this problem doesn’t happen under Win2003?

It is because that Windows Vista and Win2008 support new heap verification feature.

“HeapEnableTerminationOnCorruption”

<http://msdn.microsoft.com/en-us/library/aa366705.aspx>

 

It enables the terminate-on-corruption feature. If the heap manager detects an error in any heap used by the process, it calls the Windows Error Reporting service and terminates the process. So the heap corruption issue will be reported by Win2008 or Vista explicitly while Win2003 OS doesn’t report this though the heap corruption indeed exsits.

 

More Information

===========

Below two articles provides the sample code of calling native c++ dll function within c#:

 

“Strings Sample”

http://msdn.microsoft.com/en-us/library/e765dyyy(VS.80).aspx

 

“PinvokeLib.dll”

http://msdn.microsoft.com/en-us/library/as6wyhwt(VS.80).aspx

 

 

References

===========

Marshaling between Managed and Unmanaged Code

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx

Default Marshaling for Strings

http://msdn.microsoft.com/en-us/library/s9ts558h(VS.80).aspx

Default Marshaling for Arrays

http://msdn.microsoft.com/en-us/library/z6cfh6e6(VS.80).aspx

Memory Management with the Interop Marshaler

http://msdn.microsoft.com/en-us/library/f1cf4kkz(VS.80).aspx

 

Regards,

Hanson Wang 

 

Comments (2)

  1. Anonymous says:

    I have a similar problem.. Pls help out..

    My application uses twain dll for scanning documents. There are so many users using my application. But for few users my application hangs and getting the error that my application stopped working. The users reported this issue are using either windows vista or windows 7 version. The memory dump shows “System.Runtime.InteropServices.COMException”. Please see below the stack trace taken from the memory dump.

    STACK_TEXT:

    77a6015d ntdll!RtlReportCriticalFailure+0x5b

    77a60d68 ntdll!RtlpReportHeapFailure+0x21

    77a60e56 ntdll!RtlpLogHeapFailure+0xa1

    77a2b0c8 ntdll!RtlSizeHeap+0x69

    778212e0 ole32!CRetailMalloc_GetSize+0x21

    767f43d9 oleaut32!APP_DATA::FreeCachedMem+0x30

    767f3e6c oleaut32!SysFreeString+0x6b

    118b1647 VM31bTXP+0x1647

    779ee1c4 ntdll!LdrpCallInitRoutine+0x14

    779e8dea ntdll!LdrpUnloadDll+0x3d8

    779ee8e2 ntdll!LdrUnloadDll+0x46

    76aa0979 kernel32!FreeLibrary+0x15

    597d6512 twain!CDataSrc::unload+0x22

    597d66b9 twain!CDataSrc::SendMessageA+0x81

    597d6c1e twain!CDataSrc::identityMsg+0x18

    597d6d0d twain!CDataSrc::MessageNoAppCheck+0x47

    597d6e14 twain!CDataSrc::IDataSrc+0xd0

    597d58f6 twain!CDsm::registerDS+0x1b7

    597d5ca3 twain!CDsm::registerDirDSs+0x163

    597d5c87 twain!CDsm::registerDirDSs+0x147

    597d5dc7 twain!CDsm::findAndRegisterDSs+0x85

    597d5ed9 twain!CDsm::findClosestDefaultDS+0xcc

    597d634d twain!CDsm::identityMsg+0x17e

    597d6404 twain!CDsm::Message+0x91

    597d8ec6 twain!DSM_Entry+0x102

    0540605c unknown+0x0

    119792d3 unknown+0x0

    11979255 unknown+0x0

    68991eab System_Windows_Forms_ni+0xa51eab

    68991eab System_Windows_Forms_ni+0xa51eab

    68120317 System_Windows_Forms_ni+0x1e0317

    68136d71 System_Windows_Forms_ni+0x1f6d71

    68136d1a System_Windows_Forms_ni+0x1f6d1a

    68136d1a System_Windows_Forms_ni+0x1f6d1a

    68136b74 System_Windows_Forms_ni+0x1f6b74

    68140207 System_Windows_Forms_ni+0x200207

    Kindly advise when this occurs.. And what is the root cause

  2. Hi Raji,

    This is more like a Heap Corruption issue with BSTR.  You can try the Application Verifier, this will enable Page Heap and bring output to Debugger:

    http://www.microsoft.com/…/details.aspx