Skip to content

wineserver: Report non-zero exit code for abnormal process termination

Adam Rehn requested to merge adamrehn/wine:nonzero-exitcode into master

Overview:

Wine currently reports an exit code of zero for processes that terminate due to either of the following conditions:

  • The Linux process that represents the Windows process receives a signal such as SIGKILL (e.g. due to a user manually killing the process, or the Linux OOM killer targeting the process due to memory pressure)

  • The process fails to start because the image is a 32-bit executable and Wine has only been built with 64-bit support

Both of these scenarios represent failures, and so a non-zero exit code is appropriate. This patch ensures that an exit code of 1 is reported for these abnormal process termination edge cases.

Underlying cause:

The following logic flow occurs in the Wine server for typical process termination:

  1. When a process object is created by the Wine server, its exit_code field is set to an initial value of STILL_ACTIVE, and the exit_code field in each of its corresponding thread objects is set to a default value of zero.

  2. When a client process terminates, it sends a terminate_process protocol request to the server, and the handler passes the specified exit code to the terminate_process() function. This function sets the is_terminating field of the process object to a value of 1, and if the exit code is non-zero then it also copies its value to each of the thread objects for the process.

  3. The client process closes the pipe that it had been using to communicate with the server.

  4. The server detects the pipe close event and calls the kill_process() function, specifying a value of zero for the violent_death parameter.

  5. The logic in the kill_process() function identifies this as a normal termination on pipe close, and then subsequently calls the kill_thread() function to kill each of the threads for the process.

  6. The kill_thread() function then calls remove_process_thread(), which copies the exit code from the last thread object to the exit_code field of the process object.

  7. The final value of the exit_code field is reported when responding to get_process_info protocol requests, as sent by NtQueryInformationProcess().

The abnormal process termination scenarios listed earlier will skip the terminate_process protocol request and immediately close the pipe. As a result, the is_terminating field of the process object will retain its default value of zero, and the exit_code field of each thread for the process will also remain at the default value of zero.

Since the logic in process_poll_event() always treats a pipe close event as a non-violent termination, remove_process_thread() will simply copy the exit code value of zero from the last thread into the process object, and this will then be reported when querying the process exit code.

Fix details:

The fix modifies process_poll_event() to check the value of the is_terminating field of the process object, and to treat a pipe close event as a violent termination if the value of the field is zero. This triggers the alternative code path in kill_process() that calls terminate_process() and specifies an exit code value of 1, which is copied to the thread objects and subsequently to the process object.

In my testing, only the abnormal process termination scenarios listed above result in the value of the is_terminating field being zero when the pipe is closed. In all other scenarios, I have observed the value to be 1 when the pipe close event is detected. Non-abnormal scenarios tested include ordinary process completion with both zero and non-zero exit codes, crashes in Windows processes due to unhandled exceptions, and ending processes via the Wine implementation of Task Manager.

Merge request reports

Loading