Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binaries built on Windows 7 fails on Windows Server 2012

An application built on nightly build machine does not work on Windows Server 2012 but works fine on other desktops.

An exception of the kind "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." is thrown.

When I debug using remote debugging across the WindowsServer2012 machine and build machine, I see that this exception is thrown at a place where kernel32 call HeapSize is made in the code. Here is how HeapSize is imported and called :

[DllImport("kernel32")] 
static extern int HeapSize(int hHeap, int flags, void* block); 
// Returns the size of a memory block. 

public static int SizeOf(void* block) 
{ 
    int result = HeapSize(ph, 0, block); 
    if (result == -1) throw new InvalidOperationException(); 
    return result; 
}

This is called as part of an unsafe class's constructor:

    public UnManagedBuffer(StringBuilder sb)
    {
        PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
        Size = UnManagedMemory.SizeOf(PtrStart);
        PtrWriteNextValue = PtrStart + Size - 1;
        PtrReturnNextValue = PtrStart;
    }

Any clues on what could be missing and how to fix this?

This is what I see in Windbg:

This is what I see in Windbg

EventLog shows:

    Log Name:      Application
    Source:        .NET Runtime
    Level:         Error
    Keywords:      Classic
    Description:
Application: TestEngine.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
   at Core.Utils.UnManagedMemory.HeapSize(Int32, Int32, Void*)
   at Core.Utils.UnManagedMemory.SizeOf(Void*)
   at Core.Utils.UnManagedBuffer..ctor</Event>

Faulting application name: TestEngine.exe, version: 1.0.0.0, time stamp: 0x56b532bb
Faulting module name: ntdll.dll, version: 6.3.9600.18185, time stamp: 0x5683f0c5
Exception code: 0xc0000005
Fault offset: 0x0000000000057306
Faulting process id: 0x2eb8
Faulting application start time: 0x01d164e45b12d7dd
Faulting application path: C:\NGDLM\Lib\TestEngine.exe
Faulting module path: C:\Windows\SYSTEM32\ntdll.dll
Report Id: bea6eb89-d0d7-11e5-80eb-0050568cd888
Faulting package full name: 
Faulting package-relative application ID: 
like image 265
NVK Avatar asked Oct 31 '22 09:10

NVK


1 Answers

The code you've written should not have ever worked.

HeapSize returns the size of a heap, eg, something allocated by calling HeapAlloc. The pointer provided to HeapSize must be the pointer returned by calling HeapAlloc:

lpMem [in]

A pointer to the memory block whose size the function will obtain. This is a pointer returned by the HeapAlloc or HeapReAlloc function.

You're calling HeapSize, but providing a pointer that could be anywhere within that heap; or not in that heap at all:

    PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
    Size = UnManagedMemory.SizeOf(PtrStart);
    PtrWriteNextValue = PtrStart + Size - 1;
    PtrReturnNextValue = PtrStart;

Not only will Marshal.StringToHGlobalAnsi() return a pointer somewhere in the heap, and not a pointer to the heap itself, you don't even know which heap the pointer was allocated from, because the process could have multiple heaps allocated.

All of that doesn't matter, because it appears you have a fundamental misunderstanding of the purpose of this function - you appear to be using it to retrieve the size of an allocation made inside a heap. The memory returned by Marshal.StringToHGlobalAnsi() is not allocated by calling HeapAlloc() (because it's not a heap!), it's allocated by calling AllocHGlobal. The memory allocated by it has to be freed by calling Marshal.FreeHGlobal():

From Marshal.StringToHGlobal()'s documentation:

Because this method allocates the unmanaged memory required for a string, always free the memory by calling FreeHGlobal.

This Marshal method has nothing to do with HeapAlloc, HeapSize or related functions.

If you did actually want to find out the size of the memory allocation of the pointer returned by Marshal.StringToHGlobal(), you could dig through the source of the the Marshal class and find out that it uses the win32 function LocalAlloc. It so happens that LocalAlloc has a sister function LocalSize, which indeed can be used to find the size of an allocation.

However, there is no guarantee that doing so will work in the future, because the .Net framework provides no guarantee that it'll continue to use LocalAlloc. If they changed the internals LocalSize might stop working.

...

All of that said:

I don't think any of this is what you meant to do in the first place

Looking at your code again:

    PtrStart = (byte*)Marshal.StringToHGlobalAnsi(sb.ToString());
    Size = UnManagedMemory.SizeOf(PtrStart);
    PtrWriteNextValue = PtrStart + Size - 1;
    PtrReturnNextValue = PtrStart;

You're trying to find the length of the ansi string returned to you.

All of this business of HeapSize or LocalSize is completely irrelevant.

If you just want to find the length of an 'ansi' string, you just need to implement a stupid simple string length, or use any of the implementations already there for you.

The following program uses Marshal.StringToHGlobal(), and prints:

String: 'Hello'; Length: 5

    public static void Main( string[] args )
    {
        IntPtr strPtr = IntPtr.Zero;
        string str = "Hello";
        try
        {
            strPtr = Marshal.StringToHGlobalAnsi( str );

            Console.Out.WriteLine( "String: '{0}'; Length: {1}", str, AnsiStrLen( strPtr ) );
        }
        finally
        {
            if( strPtr != IntPtr.Zero )
            {
                Marshal.FreeHGlobal( strPtr );
            }
        }
    }

    public static int AnsiStrLen( IntPtr strPtr )
    {
        int size = 0;

        while( Marshal.ReadByte( strPtr ) != 0 )
        {
            size++;
            strPtr = IntPtr.Add( strPtr, 1 );
        }

        return size;
    }
like image 70
antiduh Avatar answered Nov 11 '22 20:11

antiduh