views:

861

answers:

4

I have a console-mode program that uses SQLite3 to maintain a database file. It takes a while to execute, but it should be safe at any point to cancel, assuming that the database writes happen. (This is all under Windows)

Is it any safer, from the point of a running program, to hit CTRL+C in the console than to have another program call TerminateProcess on it?

I've noticed that I can get database corruption if TerminateProcess is called- I assume that this is because the program does not get a chance to finish writes. My guess is that CTRL+C is better, because the program gets a signal and terminates itself, rather than the OS killing it.

Note that the program doesn't actually handle the signal (unless SQLite does); I'm talking about the built-in default mechanisms of Win32 executables to handle the CTRL+C signal.

To clarify/simplify the question- given that this write has just executed:

fwrite(buf, 1024*1024, 1, stream);

During this write, will TerminateProcess behave differently from CTRL+C?

A: 

SQLite claims to be atomic, even during power failures, see http://www.sqlite.org/atomiccommit.html.

The only exception to this is that some disk systems will report that the write occurred successfully before the data is actually written to the disk platters, i.e. the data is in the disk cache, or the operating system is lying to SQLite. See http://www.sqlite.org/lockingv3.html, section 6.0 How To Corrupt Your Database Files.

Processes that are terminated must stop all running threads and complete pending I/O operations before they exit. Data integrity should be assured, provided that the process hasn't crashed.

Robert Harvey
"The only difference in this regard between a Ctrl-C and a TerminateProcess is how the application has been written to handle either of these events."Is this conjecture or do you have any proof to back it up?
arolson101
The link that Manuel gave appears to be authoritative. Apparently I made an incorrect assumption about terminating; it must be pending I/O that holds the task open, rather than the programmer intercepting the terminate call. I edited my answer to reflect this.
Robert Harvey
+1  A: 

Your application has to the opportunity to handle CTRL + c and CTRL + break, as key strokes or signals (depends on config), meaning that the application has the opportunity to make a clean exit, these would be a much softer way to terminate a process, allowing it a little more execution time if nothing else.

TerminateProcess is more of a kick in the teeth, the application cannot handle this, it comes from the kernel, and if the application could handle it, this would create all sorts of issues with 'un killable' processes hanging their TerminateProcess handler and refusing to exit.

As I understand it as soon as TerminateProcess is called on the process, it can not execute anymore code thats it, no clean up, no shutdown, its just over, you cant handle it, it simply wouldnt make sense if you could, not from a security perspective.

Excellent code project article here on hanlding windows console signals:

http://www.codeproject.com/KB/winsdk/console_event_handling.aspx

Implimenting some of the above signal handling you could ensure that the database writes have a chance to complete before the program exits, rather than possibly leaving it to chance.

You can block TerminateProcess, but its' not really 'polite' programming, it's more like root kit programming, I have seen a good article on this at rootkit.com so search there for 'Process Invincibility' once you have the invicible process, it could in its own time shut down after receiving such a 'request', and perform any clean up before hnad, but this most certainly a hack.

It seems to me though that the ctrl+c behaviour you are currently seeing is due it not immediatley ending the running process.

A: 

I believe for your purposes, it is safer to use [CTRL]+[C]. This will cause a signal to sent to the program to terminate. If the program does not handle the signal, it will terminate on the spot.

TerminateProcess forcibly terminates the process and any child threads.

From MSDN:

TerminateProcess Function Terminates the specified process and all of its threads. ... Remarks

The TerminateProcess function is used to unconditionally cause a process to exit. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.

TerminateProcess initiates termination and returns immediately. This stops execution of all threads within the process and requests cancellation of all pending I/O. The terminated process cannot exit until all pending I/O has been completed or canceled.

A process cannot prevent itself from being terminated.

There are ways for a process to block TerminateProcess, but I doubt SQLite3 will do that.

Manuel
Your article seems to state that there's no difference, but you say that it's safer to use CTRL+C. Remember, we're talking about a program not specifically written to handle signals. Is there a reason why you say it's safer to use CTRL+C?
arolson101
CTRL+C wouldn't force termination of the process and child threads, it would only tell it to shutdown. If it's not handled, then it just exits. TerminateProcess can cause global data corruption as it says on MSDN. In your solution, you state that nothing would stop the write; that's because even TerminateProcess will not end it if there's I/O ("The terminated process cannot exit until all pending I/O has been completed or canceled."). That was in MSDN as well.
Manuel
A: 

All of these are compelling arguments, but the only way to know for sure is to try it. So I wrote a simple program that allocates a 1GB buffer, assigns some data to it, then writes it to a file using a single fwrite(). I tried several methods to get the write to "corrupt" the data (I was expecting a truncated file, specifically):

  • Calling TerminateProcess (via perl's kill function and Win32::Process::Kill)
  • Hitting CTRL+C
  • Using Task Manager's "End Process"
  • Using Process Explorer's "Kill Process"

Nothing would stop the write- in every case, the file was the correct size and had the correct data. And although the "Kill" would happen instantly, the process would linger until the write had completed.

It seems conclusive that there is no difference between TerminateProcess and CTRL+C from an I/O point of view- once the write starts, it seems guaranteed to complete (barring power outages).

arolson101
Very intresting, I suppose the IO operation completes regardless, as it simply does not make sense to stop it, what could it possibly do other than lead to corruption. Must be another way of blocking TerminateProcess hidden in there somewhere. Interesting stuff. thanks.