Warum man fremdem Code nicht trauen darf
main():
Was meint ihr, gelingt es mir als Programmierer von - int a = 0;
- printf("Der Wert von Variable 'a' ist %d.\n", a);
- BoeseFunktion(4711);
- a = 1;
- printf("Der Wert von Variable 'a' ist jetzt %d.\n", a);
BoeseFunktion()
dass die Ausgabe in der zweiten Zeile Der Wert von Variable 'a' ist jetzt 0.lautet? Selbst wenn 'a' nur lokal der Main-Funktion bekannt ist? Ich sage ja:
BoeseFunktion():
Das funktioniert eigentlich ganz einfach. Mit - void BoeseFunktion(int tmp) {
- int* pointer;
- pointer = &tmp - 1;
- *pointer = *pointer + 8;
- }
pointer = &tmp - 1;
hole mich mir einen Zeiger auf den ersten Funktionsparameter und reduziere die Adresse um 1. Auf x86-Systemen bin ich nun an der Rücksprungadresse von BoeseFunktion() auf dem Stack.
So sieht das im Speicher aus:

*pointer = *pointer + 8;
erhöhe ich anschließend die Rücksprungadresse um 8 Byte, was gerade soviel ist um das a = 1;
in main() zu überspringen.
Solches Gehacke mit Pointer-Arithmetik ist natürlich sowieso pfui. Aber was uns dieses Beispiel eigentlich lehren soll ist, dass man fremden Funktionen nicht trauen soll.
Es könnte schließlich sein, dass ich sie geschrieben habe... 😉Schadensabwehr
Aber wie bekommt man nun das Problem in den Griff, dass Fremdcode, den man in so gut wie jedem Projekt findet, unbeabsichtigt oder beabsichtigt Unfug anstellt und Daten oder die Programmausführung beeinträchtigt? Da gibt es mehrere Möglichkeiten. Unter anderem:- Logische Programmablaufkontrolle Man stelle sich vor, jede eigene Funktion meldet sich bei ihrem Aufruf an einer zentralen Instanz mit einem eindeutigen Key. Die zentrale Instanz überprüft dann anhand einer Tabelle ob es in Ordnung ist, dass Funktion3 nun auf Funktion2 folgt oder nicht. Bringt fremder Code - zum Beispiel eine defekte DLL - den Ablauf durcheinander so wird dies schnell aufgedeckt. Voraussetzung für ein solches System ist natürlich eine deterministische Reihenfolge der Funktionsaufrufe.
- Datenredundanz Zuerst einmal sollten alle Daten, die irgendwie wichtig sind, redundant vorliegen; am besten redundant-invers. Das heißt, es gibt nicht eine Variable 'a', sondern z.B. 'unsigned char u8VentilSollwert' und 'unsigned char u8VentilSollwertInvers'. Das hat nicht nur einen eingängigeren Namen als 'a' sondern informiert uns auch gleich noch über das Präfix u8, dass es eine unsigned 8-Bit-Variable ist mit Wertebereich von 0..255. In der einen steht dann z.B. 1 (binär 0000 0001) und in der anderen das Gegenteil, also 254 (1111 1110). Vor Verwendung wird jedes Mal geprüft, ob die beiden Variablen noch Bit-invers sind und falls nicht mit Fehlermeldung abgebrochen.
- Datensicherung Wenn die Daten umfangreicher sind, gibt es die Möglichkeit sie mit einer Checksumme zu sichern. Wird die Checksumme oder die Daten beschädigt, so fällt dies auf, sobald man die Checksumme nachrechnet und mit dem Prüfwert vergleicht.
Qt: bloß keine Waisenkinder instantiieren
Wenn man in Qt eine Klasse implementiert, sollte man tunlichst darauf achten, dass alle instantiierten Attribute
Das Elternelement hier ist
Also besser gut aufpassen bei so was, sonst kann das Debugging richtig ätzend werden.
this
als parent haben.
Andernfalls kann es nämlich sein, dass irgendwann der Garbage-Kollektor auf das offenbar elternlose Kind-Objekt stößt und es im Sinne von "ist das Kunst oder kann das weg" in die Tonne wirft.
Und dann hat man den Salat.
Ist mir hiermit passiert:
- class Measurement : public Object
- {
- Q_OBJECT
- public:
- Measurement(QObject* parent = 0);
- ~Measurement();
- };
- class XYPlot : public QQuickPaintedItem
- {
- Q_OBJECT
- public:
- XYPlot(QQuickPaintedItem* parent = 0);
- private:
- Measurement m_Measurement;
- };
XYPlot
, welches ein Attribut der Klasse Measurement
besitzt.
Bei mir gab es unreproduzierbare SIGSEGV segmentation faults, was immer auf irgendein Problem mit ungültigem Pointer hinweist.
Beim Debuggen habe ich dann festgestellt, dass kurz zuvor der Dekonstruktor von Measurement
aufgerufen wurde - obwohl das zugehörige XYPlot-Objekt noch existierte.
Woran lag es? ⇒ Ich hatte vergessen, dem Kind-Objekt zu sagen wer sein parent ist. Im Konstruktor der Elternklasse XYPlot
also m_Measurement
mit this
instantiieren:
- XYPlot::XYPlot( QQuickPaintedItem* parent) : QQuickPaintedItem(parent), m_Measurement(this)
- {
- }
Irrationales Programmieren mit QML
Ich habe Qt5 auf dem Raspberry Pi 2 kompiliert und installiert. Das war zwar ein bisschen umständlich, lief bis jetzt eigentlich ohne unüberwindbare Probleme.
Aber jetzt hol' ich gleich die Kettensäge und geb' dem Drecksteil den Rest!
Den Code des Küchencomputers portiere ich derzeit auf Qt5 und QtQuick 2.4 und mache ihn unabhängig von Plattform und Auflösung.
Er läuft inzwischen gleichermaßen problemlos auf Windows 7 wie auf Debian Wheezy mit LXDE.
Nur auf dem Raspberry nicht. Da werden manche Texte nicht angezeigt und das Layout ist verschoben.
Und QML wirft mir unzählige Fehlermeldungen entgegen - allen gemein ist, dass sie mit string-Properties zu tun haben.
Also habe ich in den letzten Stunden ein Minimalprojekt angelegt um das Problem nachzustellen, einzukreisen und dann eine passende Lösung zu entwickeln.
So was kostet Zeit, funktioniert aber immer.
Nur hier nicht. Dieser Mistcode bringt mich noch um den Verstand:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: myWindow
width: 400
height: 200
visible: true
Text {
id: myText
text: "12"
}
Text {
x: 30
text: myText.text
Component.onCompleted: {
myText.text = "99"
}
}
}
Das funktioniert genau so, wie es soll.
Wenn man aber "99"
durch "34"
ersetzt, bleibt der zweite Text leer und QML meldet
Unable to assign [undefined] to QStringin der Zeile
text: myText.text
.
Allerdings nur auf dem Raspberry. Unter Windows und einem x86-Linux macht es, was es soll.
Was zur Hölle soll das?
Warum gerade "34"
??
Warum nicht z.B. "42"
???
Nerv! Ich spiele schon mit dem Gedanken wieder zurück auf Qt4.8 zu portieren ...