Suchen

Software-Entwicklung gemäß IEC61508

Redakteur: Martina Hafner

Teil 3 der Norm IEC 61508 richtet sich an Software-Entwickler sicherheitsgerichteter Systeme. Da viele Softwarefehler auf Spezifikationsmängeln und mangelnder Transparenz in den Abläufen basieren nehmen Entwicklungsprozesse und Methoden in der Norm einen breiten Raum ein.

Firma zum Thema

Erweitertes Sicherheits-V-Modell nach IEC 61508
Erweitertes Sicherheits-V-Modell nach IEC 61508
(Bild: Quategra)

Für Softwarefehler gilt seit jeher: Kleine Fehler können große Auswirkungen haben. Dabei sind Softwarefehler immer (latent) vorhanden, insbesondere da die Komplexität von Software kontinuierlich steigt.Die Qualität heutiger Software wird nicht allein durch möglichst fehlerfreie Codierung und Tests erreicht, nach verschiedenen Untersuchungen basieren viele Softwarefehler bereits auf Spezifikationsmängeln und mangelnder Transparenz in den Abläufen. Daher nehmen Entwicklungsprozesse und Methoden einen breiten Raum im 3. Teil der IEC 61508 ein.

Softwarefehler sind immer Entwurfs- oder Implementierungsfehler. Im Kontext der IEC 61508 werden diese Fehler als „systematische Fehler“ bezeichnet. Sicherheitskritische Software sind Programmteile, die unmittelbar oder mittelbar die Sicherheit eines Systems durch Fehlfunktionen beeinflussen können. Dabei sind Funktionen der Applikation (z.B. Berechnungen, Ablaufsteuerungen), der Hardware (z.B. Diagnosen wie RAM- Test) und der Kommunikation (z.B. sichere Datenübertragung) unterscheidbar.

Anforderungen der IEC61508-3

Teil 3 der Norm IEC 61508 beschreibt einen Lebenszyklus der Software und schlägt Techniken und Verfahren vor, wie sicherheitsgerichtete Software(teile) entworfen und dokumentiert sein sollte. Alle Tätigkeiten und Ergebnisse des Lebenszyklus müssen dokumentiert werden.

Basierend auf dem Software- Lebenszyklus ist das bekannte V-Modell mit sicherheitskritischen Anforderungen erweitert worden und kann als Grundlage einer Softwareentwicklung nach der IEC61508-3 angewendet werden. Haben sich im Unternehmen andere Software- Entwurfsmodelle (Wasserfall, Spiralmodelle) etabliert, sollte der Zusammenhang zu den vorgeschlagenen Phasen der IEC61508 dargestellt werden. Dabei ist die Rückverfolgbarkeit für sicherheitsgerichtete Funktionen in der Dokumentation elementar: Die Informationskette Spezifikation- Entwurf- Implementierung- Test und zurück muss für jede einzelne Sicherheitsfunktion erkenntlich sein.

Spezifikation der Software-Sicherheitsanforderung

  • Beschreibung der sicherheitsrelevanten Funktion
  • Beschreibung der Schnittstellen zwischen Hard- und Software
  • Trennung zwischen sicherheitsrelevanten und nicht sicheren Programmteilen
  • Sicherheitsrelevante Kommunikationsverbindungen

Je nach SIL sind dabei bestimmte Methoden der Beschreibung gefordert (z.B. semi- formale oder formale Methoden).

IEC 61508 Teil-7 enthält generell eine nähere Beschreibung der vorgeschlagenen Methoden. Diese sind jedoch oft in der Softwareentwicklung von Embedded- Systemen nahezu unbedeutend. Äquivalente Methoden und Verfahren sind erlaubt – so ist heute z.B. UML 2.0 ein gängiger Standard, um Software zu entwerfen und zu dokumentieren.

Planung der Validierung der Software bezüglich der Sicherheit

Die IEC 61508 fordert immer eine Planung der geforderten Tests (Validierungen) im Vorfeld, inklusive Reviews und Begutachtungen (Verifikationen). In der Testplanung werden die Tests mit Erwartungshaltung beschrieben. Auch wann diese Tests von wem auszuführen sind, muss aus der Dokumentation ersichtlich sein. Auf dieser System- Ebene sind die Tests auf die Sicherheitsanforderungen an die Software zu beziehen, nicht auf die fertigen Module (siehe Software- Modultest).

Erweitertes Sicherheits-V-Modell nach IEC 61508
Erweitertes Sicherheits-V-Modell nach IEC 61508
(Bild: Quategra)

Softwareentwurf und Softwareentwicklung

Zunächst ist eine Softwarearchitektur zu entwerfen. Diese ist bezüglich der definierten Sicherheitsanforderungen zu begutachten (zu verifizieren). Dabei sollte die Softwarearchitektur Informationen zu Programmmodulen und der Trennung zwischen sicherheitsgerichteten und nichtsicheren Programmteilen beinhalten. Softwareschichten, Schnittstellen (auch Software/Hardware-Schnittstellen), Interruptverarbeitung, Daten- und Funktionsgrafen sowie verschiedene andere „Constraints“ (Zeitanforderungen, Determinismus, Prioritäten, etc) können bereits in der Software-Architektur spezifiziert werden.

Der Übergang von der Architektur zum Software-Design ist fließend. Die Vorgaben aus der Architektur werden detailliert. Softwaremodule werden näher spezifiziert (Funktionen, Daten, Schnittstellen), wenn möglich mit semi-formellen Methoden. Dazu geeignet sind Klassendiagramme, Block-Diagramme, Sequenz-Diagramme, Programmablaufpläne und/oder Zustandsautomaten.

Im folgenden Software-Entwurf selber werden eher allgemeine Anforderungen gestellt. Verwendete Werkzeuge und die Programmiersprache sind zu wählen (C ist laut IEC 61508 nur mit Einschränkungen verwendbar!). Dabei spielt die „Betriebsbewährtheit“ der Werkzeuge eine wichtige Rolle. Ein geeignetes Konfigurationsmanagement ist dringend empfohlen.

Insbesondere beim Einsatz der Programmiersprache C in Embedded-Systemen sind, die Empfehlungen der IEC61508-3 zu beachten. Programmierkonventionen mit Spracheinschränkungen (z.B. Vermeidung dynamischer Speicher und Pointer), defensive Programmierung und die Kapselung von Moduldaten und Funktionen (Geheimnisprinzip) werden ebenso erwähnt, wie die strukturierte Programmierung mit beispielsweise:

  • Wenige Verzweigungen im Modul
  • Einfacher Zusammenhang zwischen Eingangs- und Ausgangsdaten
  • Komplexe Bedingungen und Berechnungen als Sprungbedingung vermeiden
  • GOTO (unbedingter Sprung) vermeiden
  • Keine dynamischen Variablen und keine Objekte
  • Vorsicht bei Interrupts, Pointer und rekursiven Routinen!

Zur Verifikation des Codes bezüglich der Sicherheit gibt es weitere Anforderungen:

  • Code Reviews
  • Modultests
  • Integrationstests mit vordefinierten Testfällen/-daten (Funktions- , Black Box-, White Box- und Performance-Test)
  • Dokumentation, Versionierung und Rückverfolgbarkeit der Ergebnisse

Die Funktionen der Software können klassifiziert werden. Dabei gibt es sicherheitsrelevante Abstufungen (z.B. anhand einer „Criticality Analyse“ mittels FMECA) der einzelnen Funktionen. Bei sicherheitskritischen Programmteilen sollten Maßnahmen zur Erkennung und Vermeidung von Softwarefehlern definiert werden, auf die in den folgenden Tests besonders eingegangen wird.

Software-Modultest

Im Software-Modultest finden sich Testkonzept und Testfälle. Sie sind je nach Phase vom entsprechenden Entwurf abgeleitet. Testfälle definieren eine Erwartung und ein Resultat (siehe auch IEEE829). Alle relevanten Zweige im Modul sollten durch Tests abgedeckt sein. Aufgrund des Aufwandes ist eine Code- und Decision- Coverage von 100% in der Praxis als gängige Mindestanforderung zu finden.

Neben Guttests, Grenzwerten, Über- und Unterläufe sollten alle Funktionalitäten getestet werden, die als Sicherheitsfunktion definiert sind und zu sicheren Reaktionen im System führen können. Bei den Tests kann grundsätzlich zwischen statischen und dynamischen Tests unterschieden werden:

Statische Tests:

  • Reviews/ Code Inspection/ Walkthrought
  • Statische Analyse (z.B. PC-Lint)

Dynamische Tests:

  • Funktionaler, Stress-, Reaktions-, Performance-Test
  • Datenfluss- und Interface-Test

Integrationstest (Module)

Analog den Modultest werden hier Tests „auf höherer“ Ebene gefordert. Unterschieden werden können grundsätzlich Modul-Integrationstest (Test mehrerer Module im Zusammenspiel und Integrationstest (Test der Module und Teilsysteme auf der Zielhardware). Überschneidungen, auch mit der Validierung der Softwaresicherheitsanforderungen, sind oft nicht zu vermeiden – hier sollte eine projektbezogene herangehensweise definiert werden.

Wichtig ist, dass alle Software-Tests auf einer(!) Softwareversion nach Spezifikation und Testplan durchgeführt werden und jederzeit auf einer beliebigen Version reproduzierbar sind. Empfohlen sind folgende Methoden: Black-Box- und Performance Tests mit Grenzwert-Analyse, Ablauf-Analyse, Stress- und Performance-Tests.

Software Validierung/Verifikation

Für die Software-Validierung sind Testplanungen und Testfälle von der Spezifikation der Software-Sicherheitsanforderung abgeleitet und der Zusammenhang ist rückverfolgbar. Die Validierung soll durch Simulation oder Stimulation durchgeführt werden:

  • Eingangssignale
  • Ereignisse im Vorgriff
  • Unerwünschte Zustände, die eine Reaktion des Systems erfordern
  • Grenzwert-Analyse, Prozess-Simulation
  • Ablauf-Analyse, Performance-Test

Softwaremodifikation

Die Softwaremodifikation beginnt mit einer Anforderung zur Änderung der Software. Diese enthält die geforderte Änderung und den Änderungsgrund. Es folgen eine Einschätzung bezüglich der Beeinflussung von Sicherheitsfunktion, eine detaillierte Änderungsdokumentation inklusive Testfälle und erneut auszuführende vorhandene Tests. Eventuell muss nach der Änderung sogar die gesamte Software neu verifiziert werden.

Abläufe und Tätigkeiten erfolgen dabei nach einem (Teil-) Prozess, der im FSM (Functional Safety Management) definiert wurde (siehe 1. Teil dieser Artikelreihe). Die Rückverfolgbarkeit der Softwareänderungen (Konfigurationsmanagement) ist erforderlich.

Beurteilung der funktionalen Sicherheit

Die Ergebnisse der einzelnen Phasen sind, wie in der IEC61508 Teil 1, Abschnitt 8 vorgegeben, einer Beurteilung zu unterziehen. Festgelegt wird, je nach erforderlichem SIL, der minimale Grad der Unabhängigkeit derjenigen, die die Beurteilung ausführen. Die erforderlichen Tätigkeiten dazu sind in der Tabelle A.10 definiert. Die Beurteilung kann aufgrund von Checklisten, Wahrheitstabellen, Komplexitätsmetriken, Versagensanalyse (auch mit gemeinsamer Ursache für diversitäre Software) oder Zuverlässigkeitsdiagrammen geprüft werden.

Die Programmiersprache C

Streng typisierte Programmiersprachen sind nach der IEC 61508 empfohlen (ADA, Pascal), C jedoch nicht (nur mit Einschränkungen). Aber: C ist in der Softwareentwicklung von Embedded-Systemen de facto Standard. Es gibt eine Zuordnung der C-Befehle zu Assembler-Instruktionen, um die Abhängigkeit von einer Laufzeitumgebung zu minimieren. Mit C kann (relativ) hardwarenah programmiert werden (Speicherzugriffe und hardwarenahe Konstrukte). Es gibt bewährte Compiler und Debugger für C unter fast allen Umgebungen.

Know-How in der C-Programmierung ist (meist) vorhanden und die Investition ist bereits erfolgt (Compiler, Debugger, Editoren, etc.). Funktionen der Standardbibliothek stehen in den meisten Umgebungen zur Verfügung, dadurch sind C Programme gut portabel. Vorsicht: Nicht immer wird die Standardbibliothek voll unterstützt!

Laut IEC61508 ist C nicht uneingeschränkt empfohlen. Warum?

  • C ist flexibel, weil nur sehr eingeschränkte Prüfungen bei Speicherzugriffen, Variablentypen und Stacknutzung durchgeführt werden. Dadurch können die Compiler (anders als z. B. in Pascal) nur sehr eingeschränkt bei der Fehlersuche helfen.
  • C enthält kritische Funktionen, z. B. gets() kann fremde Speicherbereiche überschreiben (bei zu langer Eingabe). Der Fehler ist nicht erkennbar oder prüfbar.
  • Modularisierung in C erfolgt auf Dateiebene. Die Bekanntgabe der Funktions- und Datenschnittstellen erfolgt in Headerdateien. Das ist insgesamt ein sehr schwach ausgeprägtes Modulkonzept.
  • Wegen der Hardwarenähe der Programmiersprache können manche Sprachkonstrukte (z.B. Bitmasken oder Zeigerarithmetik) zu Annahmen über die Byte-Reihenfolge (Endianess) oder Wortbreite führen, was die Portabilität auf andere Prozessoren einschränkt.

Die „Mängel“ in C lassen sich gut klassifizieren. Hier einige Beispiele, die jedem Programmierer sofort einleuchten:

Unspezifiziertes Verhalten in C:

  • Die Repräsentation von Fließkomma-Datentypen.
  • Die Reihenfolge, in der Ausdrücke (+, -, *, /, &, &&, …) ausgewertet werden.
  • Die Reihenfolge und Kontinuität von Speicher, welcher durch die Funktionen calloc, malloc und realloc angefordert wurde.
  • Ein Funktionspointer wird benutzt, um eine Funktion aufzurufen, die nicht mit der Deklaration der originalen Funktion kompatibel ist.
  • Ein Funktionspointer wird in einen Pointer auf ein Objekt oder umgekehrt umgewandelt.
  • Ein Bit-Feld ist mit einem anderen Typ definiert als int, signed int oder unsigned int.
  • In den stdio-Funktionen existieren diverse undefinierte Verhaltensmuster.

Implementierungsabhängiges Verhalten in C:

  • Bei einem „einfachen“ char kann nicht mit Bestimmtheit gesagt werden, ob dieser signed oder unsigned ist.
  • Das Ergebnis einer Konvertierung eines int in einen kürzeren int oder das Ergebnis der Konvertierung eines unsigned int in einen signed int lässt sich nicht darstellen.
  • Auf einen signed int wird eine bitweise Operation angewendet.
  • Das Verhalten von calloc, malloc und realloc, wenn die angeforderte Speichergröße Null ist.

Warum MISRA-Regeln?

Die Frage ist, wenn es in C so viele unspezifizierte und unprüfbare Fälle geben kann, wie kann die Software trotzdem „sicherer“ werden? Natürlich lässt sich argumentieren, dass Profis um die kritischen Stellen wissen und damit umgehen können. Leider sind nicht alle Programmierer Profis. Daher gehen die MISRA-Regeln von Basis-Voraussetzungen aus und versuchen durch diverse empfohlene oder geforderte Einschränkungen unsichere Konstrukte in C von vornherein zu verhindern.

Im Folgenden einige Beispiele von häufigen Fehlerquellen in C:

  • Programmierer machen Fehler: Vertipper (z. B. „=“ statt „==“ in einer if-Anweisung) oder der zu implementierende Algorithmus wurde falsch interpretiert.
  • Der Programmierer versteht die Sprache falsch (z. B. die Rangfolge von Operatoren).
  • Der Compiler erzeugt Code anders, als vom Programmierer erwartet. Nicht alle Features sind eindeutig definiert und können sich von Compiler zu Compiler unterscheiden.
  • Der Compiler selbst enthält Fehler: Compiler und Linker sind ebenfalls nur Software. Sie halten nicht immer den Sprach-Standard ein. Die Compiler-Programmierer können Standard falsch interpretiert haben.
  • Laufzeit-Fehler wie: Arithmetische Fehler (z. B. Division durch Null), Über- und Unterläufe, Gültigkeit von Pointer-Adressen, Fehler bei Array-Grenzen.

Regeln gemäß MISRA-C-2004

Die MISRA ist eine US-amerikanische Vereinigung von Herstellern, Zulieferern und Beratern aus dem Bereich der Automobilindustrie, welche Richtlinien zur Entwicklung und zum Einsatz sicherheitskritischer Software entwickelt. Darunter ist die Richtlinie MISRA-C eine der bekanntesten und wichtigsten im Bereich Embedded-Systeme.

Das Werk beinhaltet 141 Regeln insgesamt. Einige der Regeln sind nicht zwingend; ihre Befolgung wird jedoch empfohlen. Die Regeln sind in 21 Kategorien unterteilt. Hier einige Beispiele:

  • Regel 14: Der Typ char sollte immer als signed char oder unsigned char deklariert werden. Der Datentyp char wird in Abhängigkeit vom Compiler entweder als signed oder als unsigned interpretiert.
  • Regel 70: Funktionen sollten nicht sich selbst aufrufen (sowohl direkt, als auch indirekt).
  • Regel 104: Nicht-konstante Pointer zu Funktionen sollten nicht verwendet werden.
  • Regel 118: Dynamische Speicheranforderung (calloc, malloc, realloc, free) sollte nicht verwendet werden.

Der große Vorteil von MISRA ist, dass es Tools gibt, die die Einhaltung der meisten Regeln im Code prüfen und Hinweise ausgeben. Während Softwarestruktur, Konventionen und andere Empfehlungen meist nur auf „Good-Will-“ oder Review-Basis funktionieren, können diese Regeln durch toolbasierte Tests geprüft werden. Weiterführend gibt es die MISRA 2008, die nochmals das Regelwerk erweitert.

Sollte MISRA als Einschränkung zu C in Ihrem Unternehmen neu eingeführt werden, sprechen Sie vorher mit Ihren Entwicklern. Nicht alle Regeln sind unumstritten und bei Programmierern beliebt. Kompromisse können von Fall zu Fall durchaus erlaubt sein.

Quellen:

DIN EN 61508 Funktionale Sicherheit sicherheitsbezogener elektrischer, elektronischer, programmierbar elektronischer Systeme, Teil 1-7, November 2002

Sicherheitstechnik für Komponenten und Systeme, Peter Wratil, Michael Kieviet, Hüthig Verlag 2007

Elektronische Sicherheitssysteme, Josef Börcsök, Hüthig Verlag 2004

Functonal Safety, A Straightforward Guide to IEC 61508 and Related Standard, David J Smith & Kenneth G L Simpson, Butterworth/Heinemann Verlag 2001

Software Criticality Analysis, TÜV Süddeutschland, H.Spiess, 2000

Einführung zur IEC61508, TÜV Süddeutschland, TÜV Automotive GmbH, 2003

IEC 61508- Teil 3; Sicherheitsgerichtete Softwareentwicklung, Olaf Winne, Design&Elektronik Forum „Sichere System“, 2004

Modeling for Reliability Analysis, Jan Pukite und Paul Pukite, IEEE Press 1998

Herleitung und Nachweis der SIL-3 Klassifizierung nach der Norm IEC61508-2 für ein zweikanaliges Mikrocontrollersystem, HTWK Leipzig, Quategra Gmbh; Jens Kleemann, 2008

Safer C, Les Hatton, Mcgraw-Hill Publ.Comp.,März 1995

(ID:45384186)