Fehler im Programmcode

Software spielt in vielen Bereichen des Alltags eine immer wichtiger werdende Rolle. Sie steuert und regelt technische Geräte, angefangen beim einfachen MP3-Player bis hin zu hochkomplexen Industrieanlagen. Ohne Software wäre es nicht möglich diese hoch komplexen technischen Geräte zu betreiben.
Waren die ersten Programme noch klein und überschaubar, nimmt die Größe von Softwarepaketen von Jahr zu Jahr immer mächtigere Dimensionen an. Hatte beispielsweise der Quellcode von Windows 98 noch 13 Millionen Zeilen Quellcode beanspruchte das nur zwei Jahre später veröffentlichte Windows 2000 bereits 30 Millionen Zeilen Code. Durch Updates oder neue Versionen wachsen komplexe Softwaresysteme jährlich um etwa sieben Prozent. Bei solch umfangreichen Projekten sind Fehler im Programmcode nicht hundertprozentig zu verhindern. Welch fatale Auswirkungen jedoch auch kleinste Fehler haben können zeigt folgendes Beispiel:

  • Am 4. Juni 1996 startete vom französischen Weltraumbahnhof in Guyana eine Ariane 5 Rakete der ESA. Vierzig Sekunden nach dem Start leitete die Rakete die Selbstzerstörung ein. Entwicklungskosten von knapp 7 Milliarden Dollar konnten den 500 Millionen Dollar teuren Crash nicht verhindern. Schuld war ein simpler Programmierfehler in der Software des Steuerungscomputers. Der Computer stürzte durch diesen Fehler bei der Umrechnung einer Gleitkommazahl in eine ganze Zahl ab.

Dieses Beispiel ist kein Einzelfall. Es gibt unzählige Ereignisse in denen Softwarefehler Millionenschäden verursachten. So sind etwa 50% der Ausfälle im industriellen Sektor inzwischen softwarebedingt und die Anzahl steigt. Es wird angenommen, dass durchschnittlich etwa 3 Fehler pro 1000 Zeilen geschriebenem Softwarecode existieren. In sicherheitskritischen Anwendungen sind es unegfähr 0,5 Fehler in 1000 Zeilen. Doch wie das Beispiel des Absturzes der Ariane 5 Rakete zeigt genügt manchmal schon ein einziger Fehler, um eine fatale Katastrophe zu verhindern.

Die Gründe warum so viele und auch kritische Fehler entstehen sind vielfältig:

  • Es wird erwartet, dass ein großes Modul eine höhere Komplexität hat als ein kleines. Es kann aber wieder zu Problemen kommen, wenn ein Modul sehr viele einfache oder gleiche Anweisungen enthält und ein anderes einen hoch optimierten kurzen Algorithmus Offensichtlich ist der kurze optimierte Algorithmus schwere zu verstehen als viele einfache Anweisungen.
  • Stärker Verknüpfung (Abhängigkeiten) von Anwendungen. Viele bekannte Dateitypen (z.B. pdf, odt) kann man nur mit einem bestimmten Programm öffnen. Jedes installierte Programm übermittelt dem System, mit welchen Dateitypen es arbeiten möchte. Wenn man eine Datei mit einem anderen Programm öffnen möchtet, kann man dies einmalig tun.
  • „Time–to–market“ ist die Zeit von der Entwicklung eines Produktes bis zu dessen Vermarktung. Eine sehr kurze „time-to-market“ ergibt insbesondere bei Produkten mit Kurzem Produktlebenszyklus. Der erste Produkt auf den Markt bringt höhere Einkommen, aber wenn bereits zahlreiche Mitbewerber ähnliche Produkt liefern können, kann das Produkt nur zum niedrigen Preis angeboten werden.
  • Open – Source – Software ist ein aus beliebten Zielen den Hackers
  • unzählige weitere Gründe

    • Jetzt soll ein kurzer Überblick verschiedener Fehlertypen gegeben werden:

      Fehlertypen:

      • Lexikalische Fehler
        Ein lexikalischer Fehler ist ein falsches oder unbekanntes Eingabewort. Das kann passieren bei schlechten Englischkenntnissen, Java unterscheidet bspw. auch ganz streng zwischen Klein- und Großschreibung.


      neu

      statt

      new

    • Syntaktische Fehler
      Einen syntaktischen Fehler kann man mit einem Grammatikfehler vergleichen . Alle Worte wurde richtig geschrieben, dennoch ist der Satzbau falsch. Häufig wurde ein Semikolon vergessen.
    • Semantische Fehler
      Ein semantische Fehler ist ein Fehler mit der falschen Deklaration.

    • if (a = b)
      {
      ...
      }

      statt


      if (a == b)
      {
      ...
      }

    • Laufzeitfehler
      Einen Laufzeitfehler kann man folgendermaßen beschreiben:
      Im Programm gibt's keine Tippfehler,
      die Syntax ist in Ordnung,
      alle Variable sind korrekt deklariert. Der Programmcode ist also frei von lexikalischen, syntaktischen und semantischen Fehlern, es wird daher problemlos kompiliert. Der Compiler kann nämlich nur die ersten drei Typen von Fehlern erkennen und melden. Wenn jetzt aber im Programm folgende Zeile vorkommen:
    • > > > > > x = y / z;

      funktioniert das Programm – einwandfrei, bis eines Tages der Benutzer des Programm für z den Wert 0 eingibt. Da man nicht durch 0 dividieren kann, stürzt das Programm ab und meldet einen Laufzeitfehler.

    • Logische Fehler
      Laufzeitfehler werden zwar nicht vom Compiler entdeckt, fallen aber auf, wenn das Programm abstürzt. Logische Fehler führen meistens nicht zu einem Programmabsturz, sondern nur zu völlig falschen Ergebnissen. Ein ganz einfacher Fall

      do
      {
      IO.readInt("Bitte eine Zahl zwischen 1 und 4 eingeben:");
      IO.print("%zahl" + zahl);
      }
      while (zahl < 5);
    • Das Programm erfordert eine Zahl zwischen 1 und 4, die Schleife läuft aber so lange wie die Zahl kleiner als 5 ist.

    Neben Programmabstürzen und allgemeinen Fehlfunktionen können Programmfehler aber auch Sicherheitslücken entstehen lassen und somit z.B. vertrauliche oder persönliche Daten frei zugänglich machen. So gab es auch in den neuen Windows Versionen immer wieder Programmfehler durch welche der Computer unfreiwillig mit schädlichen Viren infiziert werden kann. Als Beispiel sei hier der erst kürzlich bekannt gewordene Stuxnet Virus genannt, welcher sich durch USB-Sticks verbreitet hat ohne das der Anwender etwas davon mitbekam. Firmen, die sensible Daten, Konstruktionspläne etc. halten, können solche Fehler in enorme Schwierigkeiten bringen.
    Aus diesem Grunde ist es notwendig Fehler in Softwareprodukten von an Anfang an zu vermeiden und zu versuchen, sicherheitskritische Fehler gänzlich auszuschließen. Um dies zu bewerkstelligen gibt es eine Reihe von Möglichkeiten und Techniken die bei der Softwareentwicklung eingesetzt werden können.

    Softwareengineering

    Eine relativ neue Methode ist das Softwareengineering. Hierunter wird die ingenieursmäßige Entwicklung von Programmen verstanden, welche durchdacht und strukturiert ist. So muss von vornherein klar definiert werden, welche Anforderungen an das Programm gestellt werden. Es darf nicht einfach drauf los programmiert werden, sondern in geplanten Schritten. Einige einfach einzuhaltende Dinge sollten immer gemacht werden:

    • Der geschriebene Code sollte immer dokumentiert werden, damit auch andere Programmierer ihn verstehen können.
    • Der Code muss wartbar sein. Dies bedeutet, dass er jederzeit relativ einfach an neue Anforderungen angepasst werden kann.
    • Der Code muss wieder und wieder getestet werden.

    Obwohl sich sich diese Techniken sehr einfach anhören, finden sie erst nach und nach Einzug in die IT Welt.
    Auch wenn ein Programm ingenieursmäßig entwickelt und anschließend systematisch getestet wird, sind Fehler, welche kritische Sicherheitslücken entstehen lassen, nicht immer zu verhindern. Einige Fehler fallen erst auf, wenn ein Programm von einer großen Anwendergemeinde benutzt wird. Aus diesem Grunde werden an Programmen vor Veröffentlichung oftmals sogenannte Alpha- und Betatests durchgeführt.

    Alpha- und Betaversionen

    Eine Alphaversion ist eine Version eines Programms, welche erstmals durch fremde Personen, also nicht Entwickler, getestet wird. Dies sind meist Firmenangestellte oder von der Firma beauftragte Tester. Im zweiten Stadium wird eine sogenannte Betaversion veröffentlicht. Diese Version ist für einen großen Anwenderkreis frei verfügbar und kann somit von einer breiten Masse getestet werden.

    Fehlertoleranz

    Ein fehlertolerantes System kann weiter seine normale Funktion erfüllen. Das zu erreichen, ist eine schwierige Aufgabe und oft nicht nötig. Bei einem fehlertoleranten System müssen alle Komponenten redundant und voneinander unabhängig sein. 100%ige Fehlertoleranz und Sicherheit sind nicht möglich, und Aufwand und Kosten steigen unbegrenzt, je näher man an die 100%-Grenze kommt. Man hat immer einen Kompromiss zwischen Kosten, Leistung, Transparenz und Fehlertoleranzgrad.

    von Alina Ezunov und Nico Grundmeier

    Quellen