Access violation in MiniDumpWriteDump when invoked out-of-process

痞子三分冷 提交于 2019-12-24 11:25:40

问题


The documentation of the MiniDumpWriteDump function states that

MiniDumpWriteDump should be called from a separate process if at all possible, rather than from within the target process being dumped.

So I wrote a small MFC crash handler program that does just that. I followed the advice in this SO answer by Hans Passant, i.e. I am passing the value of the exception pointer from the crashing program to the crash handler program even though the exception pointer is not valid in the context of the crash handler program. This works well when I run tests in a debug build, but when I switch to a release build the crash handler program crashes, with an access violation that occurs inside the MiniDumpWriteDump function.

I am stumped. Why should this work in debug builds, but not in release builds? It's maddening because access violations often are indicators for accessing invalid pointers, and the exception pointer I am receiving in the crash handler program is indeed invalid - but on the other hand I am told that this should not matter, that MiniDumpWriteDump is interpreting the pointer in the context of the crashing process (from where the pointer originated).

Any ideas what I could be doing wrong?

On a sidenote: In his answer, Hans proposes a solution where the watchdog process is pre-launched, then goes to sleep and wakes up when it is triggeredd by the crashing process. My solution is slightly different: I am simply launching the crash handler program at the time when the crash occurs, then I pass the necessary information from the crashing program to the crash handler program via command line arguments. I double-checked that the information being passed is correct, specifically the exception pointer.


回答1:


I struggled with a similar issue, and have now finally noticed what was wrong.

The MSDN documentation of MINIDUMP_EXCEPTION_INFORMATION states that the ClientPointers field must be TRUE if the ExceptionPointers address is from the target process instead of the local process.

After setting this field correctly, I can simply pass ThreadId and ExceptionPointers from the crashing process, fill them into a MINIDUMP_EXCEPTION_INFORMATION in the dump-writing process, and it works perfectly.




回答2:


I changed my approach so that the final solution looks like the one suggested by Hans Passant: The watchdog process is pre-launched, then goes to sleep and wakes up when it is triggeredd by the crashing process. The crashing process makes a deep-copy of the EXCEPTION_POINTERS structure and passes that information to the watchdog process.

Here's the code that makes the deep-copy. As mentioned in the comments to the question, the main "problem" is EXCEPTION_RECORD which is a linked-list of potentially unlimited size.

// The maximum number of nested exception that we can handle. The value we
// use for this constant is an arbitrarily chosen number that is, hopefully,
// sufficiently high to support all realistic and surrealistic scenarios.
//
// sizeof(CrashInfo) for a maximum of 1000 = ca. 80 KB
const int MaximumNumberOfNestedExceptions = 1000;


// Structure with information about the crash that we can pass to the
// watchdog process
struct CrashInfo
{
    EXCEPTION_POINTERS exceptionPointers;
    int numberOfExceptionRecords;
    // Contiguous area of memory that can easily be processed by memcpy
    EXCEPTION_RECORD exceptionRecords[MaximumNumberOfNestedExceptions];
    CONTEXT contextRecord;
};


// The EXCEPTION_POINTERS parameter is the original exception pointer
// that we are going to deep-copy.
// The CrashInfo parameter receives the copy.
void FillCrashInfoWithExceptionPointers(CrashInfo& crashInfo, EXCEPTION_POINTERS* exceptionPointers)
{
    // De-referencing creates a copy
    crashInfo.exceptionPointers = *exceptionPointers;
    crashInfo.contextRecord = *(exceptionPointers->ContextRecord);

    int indexOfExceptionRecord = 0;
    crashInfo.numberOfExceptionRecords = 0;
    EXCEPTION_RECORD* exceptionRecord = exceptionPointers->ExceptionRecord;

    while (exceptionRecord != 0)
    {
        if (indexOfExceptionRecord >= MaximumNumberOfNestedExceptions)
        {
            // Yikes, maximum number of nested exceptions reached
            break;
        }

        // De-referencing creates a copy
        crashInfo.exceptionRecords[indexOfExceptionRecord] = *exceptionRecord;

        ++indexOfExceptionRecord;
        ++crashInfo.numberOfExceptionRecords;
        exceptionRecord = exceptionRecord->ExceptionRecord;
    }
}

When we receive the CrashInfo structure in the watchdog process we now have a problem: The EXCEPTION_RECORD references are pointing to invalid memory addresses, i.e. to memory addresses that are valid only in the crashing process. The following function (which must be run in the watchdog process) fixes those references.

// The CrashInfo parameter is both in/out
void FixExceptionPointersInCrashInfo(CrashInfo& crashInfo)
{
    crashInfo.exceptionPointers.ContextRecord = &(crashInfo.contextRecord);

    for (int indexOfExceptionRecord = 0; indexOfExceptionRecord < crashInfo.numberOfExceptionRecords; ++indexOfExceptionRecord)
    {
        if (0 == indexOfExceptionRecord)
            crashInfo.exceptionPointers.ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]);
        else
            crashInfo.exceptionRecords[indexOfExceptionRecord - 1].ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]);
    }
}

We are now ready to pass &(crashInfo.exceptionPointers) to the MiniDumpWriteDump function.

Note: Obviously this is not a complete solution. You will probably want to pass more info from the crashing process to the watchdog process. The CrashInfo structure is the candidate to hold this information. Also the way how the processes communicate with each other is not shown here. In my case I went with the solution presented by Hans Passant which is linked at the beginning of the question: Use an event for synchronization (CreateEvent + SetEvent) and a memory-mapped file (CreateFileMapping + MapViewOfFile) to shuffle the information from one process to the next. The (unique) names of the event and the memory-mapped file are determined by the main process and passed to the watchdog process via command line arguments.



来源:https://stackoverflow.com/questions/32691914/access-violation-in-minidumpwritedump-when-invoked-out-of-process

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!