Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Predictable exit code of crashed process in Windows

For a process exiting normally in Windows, the exit code of the process is generally either the return value from main, or the exit code passed to std::exit. %ERRORLEVEL% can then be used to query the exit code, and that can be used to determine whether the program executed either correctly, or there were some exceptional inputs/failures that indicate a particular problem (application specific).

However, I'm interested in the exit code if the process crashes. Take a very simple example program:

int main()
{
    int * a = nullptr;
    *a = 0xBAD;
    return 0;
}

When I compile this and run in Windows, on the command line I get:

MyCrashProgram.exe -> crashes
echo %ERRORLEVEL%  -> -1073741819

The exit code is consistently this number. Which leads me to several questions:

  • Was the exit code -1073741819 somehow predicable, based on the invalid write crash?
  • If so, is there some way to determine the type of crash, based on the exit code?
  • Does this change with the compiler used (I used MSVC 2012)?
  • Does this change with the version of Windows being used (I used Win10 TP)?
  • Does this change with the architecture (eg. x64 - I used Win32)?

Note, I am not interested in how to modify the program to catch the exception. I am interested in classifying crashes that can happen in existing programs, that I may not be able to modify.

like image 854
MuertoExcobito Avatar asked May 28 '15 16:05

MuertoExcobito


2 Answers

The comment about STATUS_ACCESS_VIOLATION, led me to the documentation on GetExceptionCode:

The return value identifies the type of exception. The following table identifies the exception codes that can occur due to common programming errors. These values are defined in WinBase.h and WinNT.h.

EXCEPTION_ACCESS_VIOLATION maps to STATUS_ACCESS_VIOLATION in the list that follows. All exceptions in the list prefixed with STATUS are directly defined to exception codes prefixed with EXCEPTION. Following the documentation to RaiseException, it explains the process of attempting to debug the exception when it occurs, the final step being:

If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. For most exceptions, the default action is to call the ExitProcess function.

So to answer my questions:

  • Yes, the exit code was predicatble, it maps to EXCEPTION_STATUS_VIOLATION.
  • Other types of errors would map to other common exception codes. However, with a call to RaiseException with an arbitrary exception code (which was unhandled), the exit code of the process could be anything
  • The exit code is dependent on the Windows SDK, not the compiler, executing Windows version or architecture. Although this could theoretically change with newer Windows SDKs, that is highly unlikely for backwards compatibility.
like image 68
MuertoExcobito Avatar answered Oct 10 '22 23:10

MuertoExcobito


Here is a related short blog post by Raymond Chen (emphasis mine):

There is no standard for process exit codes. You can pass anything you want to Exit­Process, and that's what Get­Exit­Code­Process will give back. The kernel does no interpretation of the value. If youw want code 42 to mean "Something infinitely improbable has occurred" then more power to you.

There is a convention, however, that an exit code of zero means success (though what constitutes "success" is left to the discretion of the author of the program) and a nonzero exit code means failure (again, with details left to the discretion of the programmer). Often, higher values for the exit code indicate more severe types of failure. The command processor ERROR­LEVEL keyword was designed with these convention in mind.

There are cases where your process will get in such a bad state that a component will take it upon itself to terminate the process. For example, if a process cannot locate the DLLs it imports from, or one of those DLLs fails to initialize, the loader will terminate the process and use the status code as the process exit code. I believe that when a program crashes due to an unhandled exception, the exception code is used as the exit code.

A customer was seeing their program crash with an exit code of 3 and couldn't figure out where it was coming from. They never use that exit code in their program. Eventually, the source of the magic number 3 was identified: The C runtime abort function terminates the process with exit code 3.

like image 36
Ohad Schneider Avatar answered Oct 10 '22 22:10

Ohad Schneider