Sicherere Embedded-Systeme – Teil 2

Bessere Codequalität für sichere Systeme

27. Juni 2022, 6:00 Uhr | Von Marcus Nissemark
Diesen Artikel anhören

Fortsetzung des Artikels von Teil 1

Sicherheit von Anfang an

Alle Softwareentwickler nutzen einen Entwicklungsprozess, der einem Industriestandard entspricht, persönlich oder unternehmensorientiert ist. Rückblickend auf das PHASE-Konzept von Kleidermacher [2] streben sie danach, einen rigorosen Entwicklungsprozess zu befolgen, um hochsichere Software zu erstellen. Solche strengen Prozesse sind nur Standardprozesse, die Techniken mit hohen Qualitätssicherungsstandards sowie modernen und effizienten Softwareentwicklungsmethoden nutzen.
 
In Normen für funktionale Sicherheit wie ISO 26262 oder IEC 61508 beziehen sie sich auf Entwicklungsprozesse als Mittel zur Vermeidung systematischer Fehler bei der Softwareentwicklung. Dies bedeutet, dass der Prozess für Entwickler darin besteht, einen definierten Satz von Arbeitselementen zu befolgen, die für die Qualität des Systems wichtig sind. Wird eine bestimmte Funktion benötigt, sollte es Tests für die Funktion und Berichte geben, die das Verhalten dokumentieren, damit es nicht in Vergessenheit gerät. Es mag trivial klingen, aber das Konzept lässt sich auch umdrehen. Wenn es keine Anforderung für eine bestimmte Funktion gibt, muss sie auch nicht vorhanden sein, und es müssen keine Tests dafür vorhanden sein. Diese Maßnahmen stellen sicher, dass das System, die Komponente oder die Anwendung nur das tut, was es tun soll – nicht mehr und nicht weniger, was ein sicheres System wirklich ausmacht.
 
In der Regel erfordern Systeme, die Sicherheitsstandards wie ISO 26262 oder IEC 61508 erfordern, auch eine Art Validierung des tatsächlichen Prozesses und dass dieser während der gesamten Produktentwicklungsphase eingehalten wird. Manchmal wird dieser Schritt als Zertifizierung oder Qualifizierung für einen bestimmten Standard bezeichnet. Die Normen schreiben eine unabhängige Validierung vor, was Programmierern eine alternative Sicht auf ihren Prozess und die Ergebnisse ermöglicht. Es geht darum, sicherzustellen, dass Entwickler ihre eigenen Prozesse befolgt haben, alle vorgeschriebenen und von der entsprechenden Norm empfohlenen Schritte unternommen haben und dass alle produzierten Artefakte verfügbar sind und den Anforderungen entsprechen.

Unabhängigkeit bedeutet hier, dass es für Gutachter keine Anreize geben sollte, bei der Überprüfung festgestellte Mängel zu verschweigen. Zertifizierungsstellen, die solche Audits durchführen, sind Experten auf dem betreffenden Gebiet. So können Endkunden sicher sein, dass alle möglichen Maßnahmen zur Schaffung eines sicheren Systems ergriffen wurden.

passend zum Thema

Techniken für typischen Embedded-Code

Embedded-Code sollte sich nicht von normalem Code unterscheiden, sodass globale Variablen und gemeinsam genutzte Ressourcen so weit wie möglich vermieden werden und sich der Code gut dokumentieren und einfach pflegen lässt. Dies ist für Embedded-Code sogar noch wichtiger, da Embedded-Programmierer sich oft mit Hardware befassen. Ein Embedded-Entwickler sollte nicht davon ausgehen, dass seine Kollegen die Hardware-Peripherie und deren Verhalten so gut kennen wie er selbst.
 
Embedded-Software hat jedoch etwas andere Anwendungsfälle als PC-Programme, daher müssen einige Feinheiten durchdacht werden, wie z. B. Optimierungen und Interrupt-Verwaltung.

Den Code optimieren – aber sinnvoll

Das Optimieren von Code ist eine gängige Praxis, insbesondere in Embedded-Systemen. Möglicherweise muss die Größe des Codes verringert, die Ausführungsgeschwindigkeit erhöht oder eine andere Art von Optimierung durchgeführt werden. Leider ist es ohne Instrumentierung oder Profiling des Codes schwer zu sagen, wo die eigentliche Optimierung stattfinden muss. Es sei denn, es geht nur darum, den Code an ein System anzupassen. Normalerweise werden Optimierungsmaßnahmen in größerem Umfang vorgenommen, in der Regel im Rahmen der gesamten Anwendung. Oft wird dieser Schritt zu früh unternommen, was sich als schlechte Entscheidung erweist, wenn die volle Funktion noch nicht bereitsteht oder das Verhalten der Anwendung nicht bekannt ist. Dann werden Optimierungen eingeführt, die weniger genau sind.

Stattdessen sollte eine Optimierungsstrategie für die gesamte Anwendung entwickelt werden. Nur bestimmte Teile sollten im Hinblick auf die Ausführungsgeschwindigkeit optimiert werden und der Rest für die Codegröße – und zwar mithilfe einer geeigneten Compiler-Optimierungseinstellung. Dies ist in der Regel eine Best-Effort-Strategie auf einer Gesamtbasis. Besondere Vorsicht ist geboten, wenn die Anwendung zwischen verschiedenen Zielsystemen portiert wird, denn die Compiler verhalten sich möglicherweise anders als erwartet, da die Optimierungen in der Regel geräte- oder compilerspezifisch sind. Überarbeiteter Code wird hier leicht unterschätzt, denn es kann effizienter sein, einen weniger komplexen Algorithmus zu optimieren.

Interrupt-Verwaltung – bitte sorgfältig und bewusst

Normalerweise ist die Interrupt-Verwaltung eine Aufgabe des Betriebssystems und nichts, um das sich eine Softwareanwendung regelmäßig kümmert. Entwickler von Embedded-Systemen müssen jedoch häufig Gerätetreiber schreiben, für die eine Interrupt-Verwaltung erforderlich ist. Daher ist es wichtig, die Funktionsweise eines Interrupt-Handlers in Software mit oder ohne Betriebssystem zu verstehen. Außerdem läuft die Interrupt-Verwaltung regelmäßig im privilegierten Modus der CPU, was ein Ausführungszustand ist, der so kurz wie möglich gehalten werden sollte.

Ein Interrupt ist genau das, was er sein soll: eine Unterbrechung des regulären Codeflusses, unabhängig davon, ob er von Software oder Hardware erzeugt wird. Um die Reaktionsfähigkeit des Systems zu erhalten, sollte die als Interrupt-Handler oder Interrupt-Service-Routine bekannte Softwaresequenz kurz gehalten werden, aber dennoch eine große Menge an Datenverarbeitung bewältigen können. Daher sollten Programmierer beim Schreiben dieses Codes einige Techniken in Betracht ziehen:

  • Interrupts nur dann deaktivieren, wenn es notwendig ist, und dann auch nur für die kürzestmögliche Zeit.
  • Müssen Interrupts deaktiviert werden, sollten sie immer in der gleichen Routine wieder aktiviert werden.
  • Verlagern der Verarbeitung in den User-Space-Code und Implementieren eines Benachrichtigungsmechanismus im Interrupt-Handler.
  • Auf Wiederholbarkeit achten, um mehrere Interrupts zu verwalten.
  • Keine dynamischen oder Stack-Speicherzuweisungen im Interrupt-Handler durchführen.

Das Befolgen dieser Techniken für Interrupt-Handler sollte zu einem leicht zu pflegenden Code führen, der auch weniger fehleranfällig ist.

Schritte zu mehr Sicherheit

Um qualitativ hochwertigeren Anwendungscode für Embedded-Systeme zu erstellen und damit die Sicherheit der Produkte zu verbessern, sollten Entwickler eine Strategie verfolgen, um die Softwarekomplexität zu reduzieren und einfachere Lösungen zu entwickeln. Dies sind zum Beispiel isolierte, leicht zu pflegende, kleinere Softwarekomponenten. Auch verschiedene Arten von Analyse-Tools sollten zum Einsatz kommen, um dynamische als auch statische Analysen durchzuführen, einen Codierungsstandard zu befolgen und alle Compiler-Warnungen zu entfernen. Mit anderen Worten: einfache technische Praktiken, die viele Softwareentwickler bereits kennen, aber selten streng genug durchgesetzt werden.

Die Einhaltung eines strengeren, aber brauchbaren Prozesses wird Embedded-Produkten mehr Wert verleihen, da sie sicherer werden. Alle diese Schritte zielen darauf ab, so viele Fehler und Schwachstellen wie möglich zu beseitigen. Obwohl dies vernünftige Schritte sind, kommt es darauf an, sich an das Embedded-Versprechen zu erinnern, warum bestimmte Programmiersprachen verwendet werden und welche Einschränkungen sie mit sich bringen, sowie den Code richtig zu strukturieren, wenn typische Embedded-Implementierungen durchgeführt werden.

Obwohl all diese Schritte es Entwicklern ermöglichen, sicherere Embedded-Systeme zu erstellen, sollten sie sich dennoch an die impliziten Entscheidungen über die Softwaregrundlage wie Betriebssysteme erinnern. Denn sie sind in der Tat ein entscheidender Teil für die vollständige Sicherheit in Embedded-Systemen. Jeder Schritt, den Embedded-Entwickler unternehmen, um die Korrektheit und Qualität des Codes zu verbessern und auch nur potenzielle Probleme zu beseitigen, wird am Ende zu einem zuverlässigeren und sichereren System führen.

 

Literatur

[1] McCabe, T. J.: A Complexity Measure. IEEE Transactions on Software Engineering, 1976, H. 4, S. 308–320, DOI: 10.1109/TSE.1976.233837.

[2] Kleidermacher, D. und Kleidermacher, M.: Embedded Systems Security: Practical Methods for Safe and Secure Software. Newnes Elsevier, 2012, ISBN: 978-0-12-386886-2.

[3] Nissemark, M.: Sicherere Embedded-Systeme – Teil 1: Bessere Codequalität für sichere Systeme. elektronik.de, 20.6.2022, www.elektroniknet.de/embedded/software/bessere-codequalitaet-fuer-sichere-systeme.196165.html.

 

Der Autor

 

Marcus-Nissemark von Green Hills Software
Marcus Nissemark von Green Hills Software
© Green Hills Software

Marcus Nissemark

ist seit 2014 als Applikationsingenieur bei Green Hills Software tätig. Bevor er zu Green Hills in Schweden kam, arbeitete Nissemark seit 1999 als Softwarearchitekt und Entwickler von Embedded-Produkten. Zu diesen Produkten gehörten Steuerungen und Anzeigecomputer für schwere Fahrzeuge, medizinische und militärische Geräte. Seine Erfahrung umfasst mehrere Betriebssysteme, die Entwicklung von Gerätetreibern sowie die Herausforderungen des Prozess- und Produktmanagements in der Softwareentwicklung.

marcusn@ghs.com


  1. Bessere Codequalität für sichere Systeme
  2. Sicherheit von Anfang an

Lesen Sie mehr zum Thema


Jetzt kostenfreie Newsletter bestellen!

Weitere Artikel zu Green Hills Software GmbH