Nachweis der Konformität mit MISRA C und MISRA C++Sicherheitskritische, MISRA-konforme C- und C++-Systeme
Von
Michał Rozenau, Parasoft Kraków, Polen
9 min Lesedauer
Sicherheitskritische Anwendungen werden oft in C oder C++ implementiert. Doch wie lässt sich die Konformität mit MISRA-C- und C++-Richtlinien erzielen?
Sicherheitsnormen: Software, die Teil sicherheitskritischer Systeme ist, muss angemessen verifiziert werden.
Bei der Implementierung sicherheitskritischer Anwendungen wird in der Regel entweder C oder C++ als Programmiersprache verwendet. Aber in vielen Fällen, in denen das sicherheitskritische System in C++ geschrieben wurde, sind auch einige C-Komponenten beteiligt.
Dies kann die Implementierung der Standardbibliothek sein, das RTOS oder ein Utility-Modul, auf das mehrere Projekte gemeinsam zugreifen. Weder in den MISRA-Richtlinien noch im MISRA-Compliance-Dokument ist explizit definiert, welche Maßnahmen in einer solchen Situation zu ergreifen sind, um das erwartete Sicherheitsniveau des Gesamtsystems zu erreichen.
Dieser Beitrag beschreibt einen möglichen Ansatz zum Nachweis der Konformität mit MISRA C und MISRA C++. Der Fokus liegt dabei auf den möglichen Nuancen und Herausforderungen, die bei dem Versuch auftreten, die Konformität mit den MISRA C- und C++-Richtlinien zu erzielen.
Die Sicherheitsnormen IEC 61508 und ISO 26262
Nach Sicherheitsnormen wie IEC 61508 [1] oder ISO 26262 [2] muss Software, die Teil sicherheitskritischer Systeme ist, angemessen verifiziert werden. Dazu empfehlen sie verschiedene Methoden, wie die statische Analyse des Quellcodes. Techniken wie Kontrollflussanalyse, Datenflussanalyse, statische Codeanalyse und statische Analyse auf der Grundlage einer abstrakten Interpretation des Codes werden sowohl auf der Ebene der Softwareeinheit als auch auf der Ebene der Softwareintegration (dringend) angeraten.
Die Programmierung eines komplexen Systems verlangt manchmal die Verwendung mehrerer Programmiersprachen. Die Kombination von C und C++ in einem System ist nicht ungewöhnlich, da diese Sprachen so konzipiert wurden, dass sie ein gewisses Maß an Kompatibilität und Interoperabilität gewährleisten. Heutzutage bietet jeder größere Compilerhersteller kompatible C- und C++-Compiler an, die es ermöglichen, diese Sprachen in einem System zu mischen – sofern bestimmte Regeln eingehalten werden [3].
Sicherheitskritische Systeme, die sowohl C als auch C++ verwenden, müssen – wie alle anderen sicherheitskritischen Systeme – die Anforderungen der Sicherheitsnormen erfüllen. Daher sollten die Methoden der statischen Codeanalyse sowohl auf der Ebene der Softwareeinheit als auch auf der Integrationsebene angewendet werden. Da MISRA C und MISRA C++ die Richtlinien der ersten Wahl für den Einsatz dieser Sprachen in kritischen Systemen sind, liegt es nahe, diese auch für die Verifikation der gemischten C- und C++-Codebasis zu verwenden. Leider definieren weder die MISRA-C- und C++-Richtlinien noch das MISRA-Compliance-Dokument explizit die Maßnahmen, die in einer solchen Situation zu ergreifen sind, um das erwartete Sicherheitsniveau des Gesamtsystems zu erzielen.
Szenario: C und C++ in einem einzigen System
Das häufigste Szenario für die gleichzeitige Verwendung von C und C++ in einem einzigen System besteht darin, dass einige Module (Subsysteme) in C und andere Module (Subsysteme) in C++ geschrieben sind. Letztere können mit den ersteren über eine wohldefinierte API interagieren, die als ein Satz von Funktionen implementiert ist.
Betrachten wir das Beispielsystem, das aus dem in C geschriebenen Modul der Sensorbibliothek (Bild 1) und der in C++ geschriebenen GUI- und Controller-Anwendung (Bild 2) besteht. Wir erörtern nun die möglichen Ansätze, um die Einhaltung der MISRA-C- und MISRA-C++-Richtlinien für ein solches Projekt zu erreichen.
Wir gehen davon aus, dass das für die statische Codeanalyse gewählte Werkzeug Folgendes analysieren kann:
- Sowohl C- als auch C++-Code,
- Code gegen die MISRA-C-Richtlinien,
- Code gegen die MISRA-C++-Richtlinien.
Ein System als eine Summe von Subsystemen
Im ersten Ansatz versuchen wir, die gesamte MISRA-Konformität unseres Systems zu beanspruchen, indem wir behaupten, dass sie gegeben ist:
- MISRA-C-Konformität des C-Moduls (Bild1),
- MISRA-C++-Konformität des C++-Moduls (Bild 2).
Wir versuchen, diesen Ansatz zu implementieren, indem wir C- und C++-Module als getrennte Subsysteme betrachten und die statische Codeanalyse so konfigurieren, dass sie durchgeführt werden kann:
- MISRA-C-Prüfung der Quell- und Headerdateien, aus denen das C-Modul besteht (Bild 1)
- MISRA-C++-Prüfung der Quell- und Headerdateien, aus denen das C++-Modul besteht (Bild 2).
Allerdings berücksichtigt dieser Ansatz nicht, dass die Headerdateien der Sensorbibliothek (Bild 1) als C++-Code kompiliert werden, wenn sie in die C++-Quelldateien der GUI- und Controller-Anwendung (Bild 2) enthalten sind. Das bedeutet: Unser ursprünglicher Ansatz ist unvollständig, da er die MISRA-C++-Konformität dieser Headerdateien nicht gewährleistet.
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel IT-Medien GmbH, Max-Josef-Metzger-Straße 21, 86157 Augsburg, einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von Newslettern und Werbung nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung.
Gemeinsame Headerdateien sind sowohl C als auch C++
Im nächsten Ansatz modifizieren wir unsere ursprüngliche Lösung, indem wir gemeinsam genutzte Header als Elemente sowohl des C-Moduls (Bild 1) als auch des C++-Moduls (Bild 2) analysieren. Dies führt dazu, dass die gemeinsam genutzten Header sowohl nach den MISRA-C- als auch nach den MISRA-C++-Richtlinien untersucht werden.
Bei diesem Ansatz muss man darauf achten, dass der Code sowohl mit MISRA C als auch mit MISRA C++ konform ist. Dateien, die sowohl als C- als auch als C++-Code kompiliert werden, müssen entsprechend geschrieben werden, z. B. durch bedingte Kompilierung, um sicherzustellen, dass die externe C-Deklaration zur Benennung von Funktionen zum Einsatz kommt, wenn die Datei als C++ kompiliert wird, oder um das C-Schlüsselwort _Noreturn vom C++-Attribut [[noreturn]] zu unterscheiden. Außerdem müssen semantische Unterschiede zwischen den Sprachen C und C++ berücksichtigt werden, da derselbe Quellcode je nach Sprache eine unterschiedliche Bedeutung haben kann, was wiederum zu unvorhergesehenem Verhalten des Codes führen kann. Beispiele für solche Situationen sind:
- Typ eines Zeichens (Literal),
- Typ einer Aufzählungskonstanten,
- Verkettung von const-Objekten.
Um solche Probleme zu vermeiden, ist es sinnvoll, die gemeinsamen Header-Dateien so einfach wie möglich zu halten und im Idealfall nur die API-Definition aufzunehmen, die für die Interoperabilität der Module erforderlich ist. Dazu gehören Typdefinition und nicht-definierende Erklärungen von Funktionen. Andere Definitionen, insbesondere solche von Funktionen (einschließlich Funktionen mit interner Verknüpfung), sollten nicht in den gemeinsamen Headerdateien enthalten sein, damit es nicht zu einem unerwarteten Verhalten kommt.
Werden die gemeinsamen Headerdateien so einfach wie möglich gehalten, trägt dies auch zur Konformität mit MISRA C und MISRA C++ bei, da einige der Richtlinien, die im Kontext einer bestimmten Sprache definiert wurden, sich nicht strikt auf Code anwenden lassen, der sowohl in C als auch in C++ kompiliert wurde. Die MISRA-C:2012-Regel 11.9 schreibt vor, dass „NULL" die einzig zulässige Form der Null-Zeiger-Konstante ist, während MISRA C++:202x in solchen Fällen die Verwendung von „nullptr" vorschreibt. Die MISRA-C:2012-Regel 10.x enthält mehrere Anforderungen an den Gebrauch von Ausdrücken arithmetischer Typen unter Verwendung des Konzepts des essentiellen Typmodells – wobei diese Anforderungen nicht direkt auf C++-Code anwendbar sind, da das essentielle Typmodell nicht für die Verwendung im Kontext der Sprache C++ vorgesehen ist.
Dieser Ansatz ist vollständiger als der vorherige, da er die gemeinsam genutzten Header als C- oder C++-Code betrachtet, je nachdem, in welcher Sprache die Übersetzungseinheit, zu der sie gehören, kompiliert wurde. Sowohl MISRA C als auch MISRA C++ enthalten eine Reihe von Richtlinien, die auf das gesamte System angewendet werden müssen. In MISRA Compliance:2020 wird erwähnt, dass diese Regeln bei der Analyse der gesamten Codebasis zu überprüfen sind, was in unserem Fall C- und C++-Code einschließt. Leider erfüllt unser derzeitiger Ansatz diese Kriterien nicht, da er keine Analyse auf Gesamtsystemebene beinhaltet, d. h. es gibt keine Analyse, die die Integration des C-Moduls (Bild 1) und des C++-Moduls (Bild 2) verifiziert.
Überprüfung der Konformität mit Regeln auf Systemebene
Im nächsten Schritt betrachten wir die Anwendbarkeit der MISRA-C- und MISRA-C++-Regeln auf Systemebene auf die gesamte Codebasis, die sowohl C-Module als auch C++-Module enthält. Die aktuelle Version von MISRA C:2012 schließt 53 Richtlinien auf Systemebene ein, MISRA C++:2008 beinhaltet keine explizite Klassifizierung des Umfangs, und die für das öffentliche Review veröffentlichte Version von MISRA C++:202x umfasst 35 Richtlinien auf Systemebene. Unter den Richtlinien auf Systemebene gibt es einige, die nicht auf gemischte C- und C++-Codebasen anwendbar sind.
Die MISRA-C:2012-Regel 5.8 verlangt, dass alle Bezeichner von Funktionen mit externer Verknüpfung eindeutig sein müssen. Während dies im Kontext der Sprache C sinnvoll ist, sollte es nicht auf C++-Code angewendet werden, denn dadurch würde die Verwendung von Funktionsüberladungen völlig unmöglich.
Darüber hinaus gibt es die MISRA-C:2012-Regel 17.11, welche die Verwendung des Funktionsspezifizierers "_Noreturn" für Funktionen vorschreibt, die niemals zurückkehren. Diese Regel kann nicht wörtlich auf C++ angewendet werden, da in dieser Sprache kein solches Schlüsselwort existiert. Um dem Geist der MISRA-C:2012-Regel 17.11 zu folgen, sollte das Attribut "[[noreturn]]" in C++-Code zum Einsatz kommen. Solche Richtlinien dürfen nicht wörtlich auf eine gemischte Codebasis aus C und C++ angewendet werden.
Es gibt einige Richtlinien auf Systemebene, die nur in MISRA C oder MISRA C++ existieren, aber sie können sicher zur Überprüfung der gesamten Codebasis herangezogen werden, da sie entweder nie für eine andere Sprache gelten oder ihre Anwendung sicher und ohne unerwünschte Folgen möglich ist. MISRA C++:202x enthält eine Regel, die verlangt, dass bei der Deklaration von Mitgliedsfunktionen die Funktionsspezifikationen "virtual", "override" und "final" angemessen verwendet werden. Es ist klar, dass die Regel niemals einen nicht konformen Code in der Sprache C auslösen wird, in der es diese Mitgliedsfunktionen nicht gibt. Die MISRA-C:2012-Regel 2.5 verbietet unbenutzte Makrodefinitionen. Obwohl diese Regel nicht in den MISRA-C++-Richtlinien enthalten ist, wird ihre Anwendung auf den C++-Code keinen Schaden anrichten. Solche Richtlinien lassen sich ohne Bedenken auf die gemischte C- und C++-Codebasis anwenden.
Einige Richtlinien auf Systemebene verlangen eine Datenfluss- und Kontrollflussanalyse des Quellcodes, um festzustellen, ob der Code konform ist oder nicht. In vielen Fällen enthalten sowohl MISRA C als auch MISRA C++ übereinstimmende Versionen der Richtlinie, z. B:
- Der Wert eines Objekts darf nicht gelesen werden, bevor er gesetzt wurde (MISRA-C:2012-Regel 9.1, MISRA-C++:2008-Regel 8-5-1, MISRA-C++:202x-Regel),
- Funktionen dürfen sich weder direkt noch indirekt selbst aufrufen (MISRA-C:2012-Regel 17.2, MISRA-C++:2008 Regel 7-5-4, MISRA-C++:202x-Regel),
- Ein Objekt darf keinem überlappenden Objekt zugewiesen werden (MISRA-C:2012-Regel 19.1, MISRA-C++:2008-Regel 0-2-1, MISRA-C++:202x-Regel).
Um zu überprüfen, ob der Code diesen Regeln entspricht, muss eine statische Codeanalyse für die gesamte Codebasis mit einem statischen Codeanalysetool erfolgen, das den Daten- und Kontrollfluss zwischen C- und C++-Code verfolgen kann. Dies ist notwendig, um eine Nichtkonformität zu melden, z. B. wenn ein nicht initialisiertes Objekt vom C-Code an den C++-Code übergeben wird, der seinen Wert liest.
Die Anwendung von MISRA C und MISRA C++ auf der Systemebene auf eine gemischte C- und C++-Codebasis erfordert eine gründliche Analyse dieser Richtlinien, um zu bestimmen, welche anzuwenden sind und welche nicht. Eine solche Analyse würde den Rahmen dieser Arbeit sprengen – hier gehen wir davon aus, dass ein Satz von auf Systemebene anwendbaren Richtlinien auf die gesamte Codebasis angewendet wird.
Weder die MISRA-C- und C++-Richtlinien noch das MISRA-Konformitätsdokument definieren explizit, was es bedeutet, dass ein Projekt, das gemischten C und C++ Code enthält, mit MISRA C und MISRA C++ konform ist. Es ist daher fraglich, ob ein solches Projekt offiziell MISRA-Konformität beanspruchen kann. Dennoch sollte man versuchen, die MISRA-Richtlinien auf ein solches Projekt anzuwenden. Die Umsetzung des vorgeschlagenen Ansatzes, bei dem der gesamte in C kompilierte Code gegen die MISRA-C-Richtlinien und der gesamte in C++ kompilierte Code gegen die MISRA-C++-Richtlinien und zusätzlich die gesamte Codebasis gegen die auf Systemebene anwendbaren MISRA-C- und C++-Richtlinien geprüft wird, kann ein hohes Maß an Vertrauen in das Sicherheitsniveau des Gesamtsystems schaffen. Er eignet sich dazu, um die Konformität mit den relevanten Normen zur funktionalen Sicherheit zu behaupten. (mk)