Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C#
Asked Answered
A

4

15

I have a c++ dll which serving some functionality to my main c# application. Here i try to read a file, load it to memory and then return some information such as the Pointer to loaded data and count of memory blocks to c#. The Dll reads file to memory successfully but on the return to the main application, program crashes due to Heap Corruption(Critical error detected c0000374).

The code is quite simple and straightforward and I have done some similar things before with no problem, However i could not figure out what makes the problem here, I tried to allocate memory using "new, malloc and GlobalAlloc" but neither did help. Codes are as follow:

C++ MyDll:

typedef unsigned long         U32;

extern "C" __declspec(dllexport) int ReadFile(LPSTR Path, U32** DataPtr, U32* Count)
{
   FILE *fp;
   U32 *Data;
   CString tempStr(Path);
   long fSize;

   if(!(fp = fopen(tempStr, "rb"))) {
    return 0;
   }

   // Obtain File Size;
   fseek(fp, 0, SEEK_END);
   fSize =  ftell(fp);
   rewind(fp);

   Data = (U32 *)GlobalAlloc(0, fSize);
   if(Data == NULL) {
            fclose(fp);
            return -1;
    }

    // Copy file into the buffer.
        if(!(*Count = fread(Data, sizeof(U32), fSize / sizeof(U32), fp))) {
           fclose(fp);
           free(Data);
           return -2;
        }

   *DataPtr = (U32 *)Data;
       return 1;
}

C# Application:

        [DllImport(@"MyDll.dll", CallingConvention= CallingConvention.Cdecl)]
    private static extern int ReadFile([MarshalAs(UnmanagedType.LPStr)]string Path, out IntPtr dataPtr, out uint Count);

private void readDump(string Path)
{
    uint count = 0;
    IntPtr Data = new IntPtr();

   try{
       if(ReadFile(Path, out Data, out count) == 1) //The Program crashes just right after this statement
       {
           //Do Something ...
       }
    }
    catch() {}

}

The program crashes on both debug and release mode. Unless I pause the program in debug mode after loading the file and call some blocks of memory in the "Visual Studio's Immediate window". The size of files to be loaded are around 64MB and we have more than 2GB unused ram on the PC.

UPDATE: I noticed that, some third party programs which they working before, crash with "Exception Code: c0000005", and some other weird things happens in Windows 7 (the Host). so I tested the code in another installation of windows and everything seems to work as they should. So probably it's related be the Windows 7. Now how could I fix the problem? "sfc /scannow" failed to find any issue.

Adulate answered 5/5, 2014 at 11:18 Comment(14)
fSize / 4 is wrong, it won't be 4 if you use, say, GCC. I assume this goes down hill because you forgot the CallingConvention property in the [DllImport] attribute, it is Cdecl. There is no point at all in writing code like this, FileStream will do it just as well.Levitan
Thanks for your comment, I changed "fSize / 4" to "fSize/sizeof(U32)" and "[DllImport(PCIiDllAddress)]" to [DllImport(PCIiDllAddress, CallingConvention= CallingConvention.Cdecl)], but problem still exists. I have good reasons to do some jobs in c++, (this is not my complete code).Adulate
C++ never has much trouble corrupting the heap. I guess the problem is located in the code we cannot see. Unit-test the heck out of the code first before you try to interop with it.Levitan
All the code you see here, crashes with no difference.Adulate
@HansPassant thanks for your companionship. I updated some information, I would appreciate if you read the explanation and help me on the issue.Adulate
Access violations are just more evidence of heap corruption.Levitan
Do you know how could I fix this problem?!Adulate
Finally I figured out that this problem is just happening in my old windows setup. I just reinstalled the windows and problem has gone.Adulate
I am having a similar problem now with code that used to run fine but now comes up with these strange exceptions. Should I reinstall Windows 7 as well do you think? What bugs me is that no other software is affected, just the stuff I compile.Henleigh
@Henleigh in my experience there was a few other problems in 2 or 3 other applications. It may be worth to run the code in another PC or Installation of win7.Adulate
After reinstalling Windows, still didn't work. I took forever trying various different things, isolating different libraries I was using and trying to test them independently, etc. I had actually tracked the bug down to the C Standard free but thought that perhaps free was being overwritten with a function pointer causing corruption (hence 0xC0000374). As it turns out, it actually was free causing the problem, and not the implementation of it either.Henleigh
I was trying to free const char*s. For nearly a year I have found free to ignore constant pointers, so I haven't been careful about using free on memory that both could have been or may have not been constant. For some reason free no longer ignores constant pointers but instead does something strange with them. Perhaps it tries to deallocate the executable image, or perhaps it was intentionally throwing the heap corruption error (maybe it thought something must have gone wrong if someone would try deleting a pointer of this sort).Henleigh
I have yet to test this within the much larger application I was working on when I first started encountering this problem, only a small test program. I'll let you know how that goes.Henleigh
The GlobalAlloc example uses GlobalFree. Would that have made any difference here?Faulty
N
44

If all your code is indeed what is shown above, then I don't see the problem. However, when I get this issue, sometimes its because malloc/new/whatever detects heap corruption, often this corruption has already occurred previously in the program, but the crash has been delayed until the next call to new/malloc.

If you read other files, or allocate or free other buffers before the above is executed and crashes, I would look there for problems. Perhaps throw a bunch of asserts anywhere you write to buffers and check the bounds and what you are writing for overruns. Sorry this isn't a concrete answer, I do not have enough rep to leave this advice as a comment.

Nothing answered 11/5, 2015 at 5:33 Comment(3)
Actually I'm not sure about the problem, but as i said in the update and after this time and program working perfectly, I figured that this problem was just happening in my old installation of windows, so I reinstalled the windows and problem had gone.Adulate
This was my issue, I was overrunning a std::vector earlier in the code but puzzling over why free was crashingMaes
I like to use tools like gflags and Application Verifier to detect such heap corruption earlier.Catchings
S
6

I'm late to the party but here I go.

I received this error code from my own program, which brought me to this post. I was setting an array position out of range, causing the next allocation to crash the program, in Windows 7. I found the error by compiling with the -g flag with gcc from MinGW and then running the program with gdb. Somewhere here you read or write an invalid location and the next allocation picks up on the heap corruption. I solved my problem by bounds checking my iterators, but that doesn't seem to be the problem here.

Main Problems with the C Program:

  • The method you are using to find the size of the file is alright, however when you allocate memory for the Data array you are casting to a 32 bit integer array, which has problems. Casting to this requires that the size of the file in bytes be some multiple of 4. If you were to have a file with FILE_SIZE % 4 == 1 then there are 3 bytes which are not part of your allocated data, but are accessed when you look at the final element of the u32 array.
  • How fread is implemented, you should be protected from writing to this out of range position, however you will miss the last 1 to 3 characters when the file size is not a multiple of 4 since integer division truncates remainders.
  • You should close the file before exiting the scope, including after reading everything in the file.

Solutions:

  • A solution to this is may be to round up the array size to the nearest multiple of 4. You are then guaranteed to not access out of range positions from [0] to [fSize - 1].
  • If trying to copy past the last byte of the file causes a fatal error I don't have a decent solution besides using character arrays rounded up the nearest multiple of 4, then reading byte for byte. After reading everything you can then cast to u32 since C is permissive with casting.

That version of software may have been doing some extra work when casting so that it wrote to a location out of range, or C# made some extra allocation calls that would do the same (I am not familiar with how C# compiles and what instruction changes it may impose).

Some code for finding the next multiple of 4:

size_t diff, rfSize = fSize; /* size_t is preferable for array sizes and indexes, 
                              * but matching to fSize's data type will work and
                              * ensures no truncation occurs. */

/* Only if this is not already a multiple of 4 */
if (diff = fSize % 4)
  /* Mod gives the remainder by division by 4, which is also the difference between 
   * fSize and the next multiple of 4. */
  rfSize= fSize + diff;
Softspoken answered 23/5, 2020 at 6:1 Comment(2)
I'm late to response to your insightful answer, which I'm really sorry for. This is really make sense, and I should setup an test scenario to see if it really was the problem. and I will reply here. thanks by the way.Adulate
I'm not sure if this is the case here, but the error that brought be here was: the program reads a field size, that field size is 0, so it allocates 0 bytes, then tries to read data that doesn't exist and put in the malloced space that doesn't exist, then checks the count vs the field size. Well, it would check the bounds if it got that far without crashing.Wysocki
A
1

You are allocating the output data 2 times. Once in C# as new IntPtr and then in C++ as GlobalAlloc and then return the Pointer Returned by GlobalAlloc. So the Pointer returned by new intPtr has been lost.

Age answered 29/7, 2015 at 11:48 Comment(1)
I dont think that this answer is correct. the C# IntPtr is a managed object... the GlobalAlloc() in c++ allocates an array on the heap, then assigns the ptr to that memory to the pointer pointed to by the new C# IntPtr. In fact, I wrote a test to prove it. I can't really paste the code here in a comment, but passing an out IntPtr to a native function, and assigning a U32* to it, in exactly the way the OP does, works flawlessly - I was able to read the correct values (which I wrote in the c++ side) out and print them on the managed c# side.Nothing
C
1

I'm also way late, and this isn't a very useful answer, but I had a similar problem which was solved by doing a clean/rebuild of my code. Indeed there was some stale .obj file in there that was causing the issue.

Climate answered 6/8, 2022 at 2:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.