MISRA-C++ bietet Richtlinien und Konformität auch für neuen Sprachstandard C++20

MISRA-C++:2020

Inhaltsverzeichnis

Weil MISRA-C++:2008 modernere C++-Mechanismen nicht unterstützte, begann eine AUTOSAR-Arbeitsgruppe die MISRA-C++-Regeln auf den C++14-Sprachstandard hin zu aktualisieren und zu ergänzen. Einen Teil der Regeln hatte die Arbeitsgruppe unverändert übernommen, einen anderen modernisiert beziehungsweise ersetzt und weitere neue Regeln hinzugefügt. Andere C++-Programmierrichtlinien, wie die C++ Core Guidelines, High Integrity C++ und der "C++ Coding Standard" des SEI CERT, dienten als Ausgangsbasis.

Jetzt schließt sich der Kreis, indem die MISRA-C++-Arbeitsgruppe auf Basis der AUTOSAR-C++-Regeln aktuelle auf den C++17 Sprachstandard abzielende Richtlinien entwirft, die bis Ende 2020 erscheinen sollen. Dabei streicht die Arbeitsgruppe einige der MISRA-C++:2008-Regeln, überarbeitet andere wenn nötig und fügt neue Regeln hinzu. Das Ergebnis lässt sich auf etwa 300 Regeln in MISRA-C++:2020 schätzen. Die Arbeitsgruppe für die MISRA-C++-Regeln setzt sich aktuell aus Experten für Safety-critical Software, Softwareexperten aus der Automobilbranche, Mitarbeitern von Herstellern statischer Analysewerkzeuge sowie Experten des ISO-C++-Normierungskomitees zusammen.

In der Vor-COVID-Zeit traf man sich sechsmal im Jahr für zwei Tage, um an den Regeln zu arbeiten. Zwischendurch gab es zum Teil kürzere Videokonferenzen. Jetzt treffen sich die Experten nur noch online, aber öfter, um die Regeln bis Ende 2020 zu veröffentlichen. Zum Zeitpunkt des Entstehens dieses Artikels finden wöchentliche Videokonferenzen statt. Daneben gibt es noch eine Unterarbeitsgruppe, die sich mit möglichen Safety-Regeln für hochparallele Systeme in C++ (z. B. GPUs) beschäftigt. Diese erweiterten Regeln fließen noch nicht in die nächste MISRA-C++-Version ein, sind aber in Software für autonomes Fahren wichtig.

Die Zielgruppe der Richtlinien sind nicht C++-Experten, sondern durchschnittlich begabte C++-Entwickler. Das hat zum Beispiel bei MISRA-C++:2008 den Eindruck bei C++-Cracks erweckt, dass die Regeln kein gutes und modernes C++ fördern, weil modernes C++ von den Regeln zum Teil ignoriert oder nicht direkt unterstützt wurde. Da der komplette Regeltext für eine geringe Gebühr (ca. 20 Euro) lizenziert werden muss, förderte die fehlende freie Zugänglichkeit zu den jeweiligen Begründungen die Mythenbildung zu MISRA-C++. Das sollte sich mit dem neuen Regelwerk MISRA-C++ ändern, da an diesem Experten mitwirken, denen wichtig ist, dass modernes und gutes C++ gefördert wird. Das war unter anderem der Grund, warum der Autor dort pro bono mitwirkt.

Wenn man heute schon nach MISRA-C++ entwickeln soll, ist es sinnvoll, sich über die kompletten Regeln zu informieren und den Text des Regelwerkes zu lizenzieren, um bei Fehlermeldungen der Prüfwerkzeuge die zugehörige Begründung für eine Regel nachvollziehen zu können. Dies hilft auch bei der Entscheidung, eine Meldung aus der statischen Codeanalyse gegebenenfalls bewusst zu ignorieren.

Ein globales Ziel ist einfache und verständliche Software, wie dies auch von den Safety-Normen gefordert wird. Sicherheitskritische Software muss sich immer auch von Menschen untersuchen lassen, speziell wenn unerwünschtes Verhalten beobachtet wird. Programmcode, der aufgrund seiner Kompliziertheit nicht durchschaubar ist, lässt sich nur schwer einem entsprechenden Sicherheitsreview unterziehen. Einfacher Code ist aber auch in der Entwicklung besser beherrschbar. Er ist elegant und drückt sich klar aus, das heißt aber nicht, dass nur primitive Sprachmittel zur Verfügung stehen. Letztere führen oft eher zu unnötiger Kompliziertheit. Zum Beispiel sind für Anfänger globale Variablen oft einfacher zu verstehen als die vielfältigen Parameterübergabemechanismen in C++, sie führen aber rasch zu unbeherrschbarer Komplexität wie im Fall der unerwarteten Beschleunigung bei Toyota-Fahrzeugen, die durch überkomplizierte Software verursacht wurde.

Leider ist C++ eine umfangreiche und zum Teil komplizierte Programmiersprache. Vor allem die Ähnlichkeit zu C und die trotzdem vorhandenen Unterschiede führen zu Missverständnissen von Programmierern im Programmcode. C++ bietet ein wesentlich besseres Typsystem, das aber aus Kompatibilitätsgründen bewusst Schwachstellen des Typsystems von C umfasst, beispielsweise die automatische Promotion "kleinerer" Ganzzahltypen, bei denen auch vorzeichenlose Typen wie (uint16_t) zu vorzeichenbehafteten Typen (int – heute oft 32 Bit) umgewandelt werden. Letzteres birgt die Gefahr von Undefined Behavior bei Überlauf, zum Beispiel bei der Multiplikation zweier Variablen vom Typ uint16_t, die als signed int-Multiplikation erfolgt. Der folgenden Kasten zeigt die Regel "Kontrollausdrücke dürfen nicht invariant sein", die vom Autor übersetzt wurde.

Kontrollausdrücke dürfen nicht invariant sein

Kategorie: verlangt (Required)

Analyse: unentscheidbar (Undecidable), System

Vertiefung (Amplification)

Diese Regel gilt bei:

  • Kontrollausdrücken von if, while, for, do ... while und switch-Anweisungen und
  • dem ersten Operanden des ?:-ternären Operators.

Sie gilt nicht bei Kontrollausdrücken:

  • von if constexpr-Anweisungen,
  • in nicht instanziierten Templates,
  • innerhalb eines instanziierten Templates, wenn der Ausdruck von einem Templateparameter abhängt.

Begründung (Rationale)

Ein invarianter Wert eines Kontrollausdrucks ist möglicherweise ein Programmierfehler. Er führt dazu, dass unerreichbarer Code existiert, den der Compiler möglicherweise entfernt. Das kann zum Beispiel dazu führen, dass defensiver Programmcode, der eigentlich Fehler erkennen soll, aus dem ausführbaren Programm entfernt ist.

Ausnahmen (Exception)

  • Der Ausdruck true kann für Endlosschleifen genutzt werden.
  • Eine do-while-Schleife darf den Kontrollausdruck false haben.

Beispiele – Auszug

Anmerkung: s8a steht für eine Variable vom Typ int8_t, u16a für eine Variable vom Typ uint16_t.

s8a = ( u16a < 0u ) ? 0 : 1; /* Non-compliant - u16a always >= 0 */
if ( 2 > 3 )
{
/* Non-compliant - always false */
}

Jede MISRA-Regel ist einer der Kategorien "obligatorisch" (Mandatory), "verlangt" (Required) oder "empfohlen" (Ad visory) zugeordnet. Außerdem liefern die Regeln Informationen darüber, ob sie durch Werkzeuge überprüfbar sind (Decidable), ob sich die Prüfung anhand einer Übersetzungseinheit (Single Translation Unit), also einer C++-Quelldatei, entscheiden lässt oder ob das Gesamtprogramm (System) zu analysieren ist. Es gibt Regeln, die aber nicht im Allgemeinen entscheidbar sind, wie das obige Beispiel. In solchen Fällen kann ein Prüfwerkzeug zwar offensichtliche Verletzungen anzeigen, aber die Abwesenheit der Meldungen solcher nicht entscheidbaren Regeln heißt nicht automatisch, dass sie nicht doch verletzt sind. An dieser Stelle sind ein menschliches Review sowie entsprechende Tests notwendig.

Innerhalb der Regel kann die sogenannte Amplification genauer spezifizieren, was mit der Regelüberschrift gemeint ist. Die "Rationale" erklärt die Situation, warum die Regel existiert, und mit "Exception" werden Ausnahmen angegeben, bei denen die Regel nicht angewendet werden soll.