Want to see how to crash almost every KDE application?
- In your favorite KDE application, open any modal dialog, e.g. the configuration dialog.
- Quit the application using D-BUS, like this:
qdbus org.kde.kontact /MainApplication quitChances are high that your application just bit the dust and you're greeted by Dr. Konqui.
Why does it happen? Well, let's have a look at how modal dialogs are usually created. Most often applications have some "main widget" with slots reacting to user actions, creating a modal dialog on the stack, like this:
void ParentWidget::slotDoSomething() {
SomeDialog dlg( this ); //the dlg-on-stack-variant
if ( dlg.exec() == QDialog::Accepted ) {
const QString str = dlg.someUserInput();
//do something with with str
}
}What could possibly go wrong here? Nothing, you might say. Looks safe, right? This code is so trivial and the pattern is used in so many Qt and KDE applications. Wrong! This code is bound to cause a crash in a couple of situations, where a D-BUS call to quit() is only one of them (and a somewhat esoteric one, admittedly). Why?
While dlg.exec() is executed and the dialog is shown, your application returns to the event loop and processes any event that comes along. Which means, that anything might happen before exec() returns. Right. Anything. In the usual case, the user enters something and then presses either OK or Cancel to close the dialog, and exec() returns. He can't to anything else, as the parent widget is disabled anyway, as the dialog is modal. But in most non-trivial applications, there are more things going on: Asynchronous background jobs might complete, e.g. Konqueror might finish loading a website and replace the currently displayed Kate KPart while you're in its print dialog configuring your printout. KMail might start an interval mail check while a (modal) KMessageBox still tells you that it couldn't reach your mail server last time. Or something is triggered by a D-BUS call.
That's fine most of the time, unless in the following case: The background operation/d-bus call triggers the deletion of the dialog's parent, the ParentWidget instance which created SomeDialog dlg in its slotDoSomething(). When the ParentWidget is deleted, dlg is deleted in turn, as it's the child of ParentWidget. Thus when exec() returns, we are in an awkward situation: The execution of the parent widget's slotDoSomething is continued, although "this" is already deleted, and so is dlg, which was created on the stack (!). Access to a member variable of the parent widget will crash the application, and if not, it will crash when leaving slotDoSomething, as it tries to destroy the dlg object a second time when unwinding the stack. Booom.


. Well, there is the