Software-Entwicklung

Latente Defekte in bestehender Software aufdecken

20. Januar 2014, 15:07 Uhr | Von Paul Anderson
Diesen Artikel anhören

Fortsetzung des Artikels von Teil 1

Latente Fehler stecken oft in scheinbar harmlosen Code-Zeilen

Das folgende kurze Codebeispiel enthält einen latenten Defekt, der zu Tage treten kann, wenn der Code für eine andere Plattform kompiliert wird:

char *s;

while (isspace(*s)) s++; //
skip over whitespace in the
input

Das Problem besteht darin, dass die Funktion isspace einige nicht sofort ins Auge fallende semantische Eigenschaften besitzt. Es hat beispielsweise den Anschein, als würde die Funktion einen Wert des Typs char als Eingangswert verwenden. Stattdessen aber ist die Funktion so definiert, dass sie einen Parameter des Typs int verarbeitet. Übergibt der Programmierer also einen Wert des Typs char, so wird dieser in einen Wert des Typs int umgewandelt. Dies ist in den meisten Fällen unkritisch. Ist char jedoch ein vorzeichenbehafteter Typ und ist der Wert des Parameters so, dass das höchstwertige Bit 1 lautet, so wird eine Vorzeichenerweiterung durchgeführt, so dass als Resultat irgendein negativer Integer-Wert herauskommt. Handelt es sich beispielsweise um das Zeichen „ß“, dessen hexadezimaler Code 0xdf lautet, so wird an isspace der Wert -33 übergeben. Einige Implementierungen von isspace nutzen den Wert als Index für ein Array. Ein negativer Indexwert verursacht somit einen Zugriff auf Speicher außerhalb der Grenzen des Array. Dies mag im besten Fall nur ein falsches Ergebnis der Funktion zur Folge haben. Ebenso könnte dieser Fehler jedoch einen Programm-absturz auslösen.

Korrekterweise sollte der Code den Parameter also als unsigned char ablegen, um eine Vorzeichenerweiterung zu unterbinden: while (isspace((unsigned char)

*s)) s++;

Ein weiteres Beispiel für einen Code mit Tücken:

struct region r;
memcpy(&r, sizeof(r), 0); //
Zeroise the struct

Hier beabsichtigt der Programmierer, alle Felder auf null zu setzen. Anstatt aber alle Felder nacheinander einzeln explizit mit dem Wert 0 zu belegen, wird die Anweisung memcpy verwendet, um alle Felder auf einmal auf null zu setzen. Dies ist eine durchaus übliche Vorgehensweise, jedoch ist die Schnittstelle zu memcpy als verwirrend berüchtigt. Als zweites Argument soll der Wert (in diesem Fall null) angegeben werden, während es sich beim dritten Argument um die Zahl der Bytes handelt, an die geschrieben werden soll. In dem obigen Beispiel hat der Programmierer den Fehler begangen, die Reihenfolge der Argumente zu verwechseln, was keineswegs selten vorkommt. Es werden also null Bytes geschrieben, so dass die Initialisierung der Variable unterbleibt.

Es kann sein, dass dieser Code auf einer Plattform einwandfrei verarbeitet wird, weil die Werte der entsprechenden Felder zufällig ohnehin null lauten. Auf einer anderen Plattform aber können die Felder irgendwelche beliebigen Werte enthalten, was in einem nicht vorgesehenen Betriebszustand resultiert und das Programm abstürzen lässt.

Leider gibt es noch zahlreiche weitere Situationen, in denen bei der Wiederverwendung von existierendem Code latente Defekte zu Tage treten. Hierzu zählen beispielsweise Pufferüberläufe, Nullzeiger-Dereferenzierungen, Ressourcenlecks, nicht initialisierte Variablen, arithmetische Überläufe, Nebenläufigkeitsfehler und vieles mehr. Die Spanne der möglichen Symptome reicht von echten Abstürzen bis hin zu mysteriösen, schwierig zu diagnostizierenden Verhaltensauffälligkeiten.

 

 


  1. Latente Defekte in bestehender Software aufdecken
  2. Latente Fehler stecken oft in scheinbar harmlosen Code-Zeilen
  3. Statische Analyse als sinnvolle -Ergänzung zum Testen

Lesen Sie mehr zum Thema


Das könnte Sie auch interessieren

Jetzt kostenfreie Newsletter bestellen!

Weitere Artikel zu elektroniknet