Die C-Sprache ist sehr leistungsfähig und wird häufig verwendet - insbesondere im Linux-Kernel -, ist aber auch sehr gefährlich. Ein Linux-Kernel-Entwickler beschrieb den Umgang mit C-Sicherheitslücken.Sie können fast alles in C tun, aber das bedeutet nicht, dass es getan werden muss. C-Code ist sehr schnell, wird aber ohne Sicherheitsgurte getragen. Selbst wenn Sie ein Experte sind, wie die
meisten Linux-Kernel-Entwickler , sind Killerfehler immer noch möglich.
Zusätzlich zu Fallstricken wie
Zeiger-Aliasen weist C grundlegende unkorrigierte Fehler auf, die auf ihre Opfer warten. Dies sind die Sicherheitslücken, die
Case Cook , Google Linux-Kernel-Sicherheitsingenieur, auf
der Linux-Sicherheitskonferenz in Vancouver behoben hat.
„C ist eine Art Assembler. Es ist fast Maschinencode “, sagte Cook und bezog sich auf ein Publikum von mehreren hundert Kollegen, die die Geschwindigkeit der Anwendungen auf C verstehen und schätzen. Die schlechte Nachricht ist jedoch, dass„ C mit gefährlichem Gepäck, vage Verhaltensweisen und anderen Schwächen einhergeht, die dazu führen
Sicherheitslücken und anfällige Infrastruktur. “
Wenn Sie C in Ihren Projekten verwenden, sollten Sie auf Sicherheitsprobleme achten.
Linux-Kernelschutz
Im Laufe der Zeit entdeckten Cook und Kollegen zahlreiche Probleme mit nativem C. Um sie anzugehen, wurde das
Kernel-Selbstschutzprojekt gestartet. Er arbeitet langsam und stetig daran, den Linux-Kernel vor Angriffen zu schützen und den problematischen Code von dort zu entfernen.
Dies ist kompliziert, sagt Cook, weil "der Kernel Dinge tun muss, die für eine bestimmte Architektur sehr spezifisch sind, um Speicherverwaltung, Interrupt-Behandlung, Sheduling usw.". Eine große Menge an Code bezieht sich auf bestimmte Aufgaben, die sorgfältig geprüft werden müssen. Beispiel: "C verfügt nicht über eine API zum Festlegen von Seitentabellen oder zum Wechseln in den 64-Bit-Modus", sagte er.
Bei einer solchen Auslastung und bei schwachen Standardbibliotheken in C gibt es zu viel vages Verhalten. Cook zitierte - und stimmte zu - Raf Leviens Blog-Artikel
"Mit undefiniertem Verhalten ist alles möglich".Cook gab konkrete Beispiele: „Was ist der Inhalt der„ nicht initialisierten “Variablen? Das ist alles, was mir vorher in Erinnerung geblieben ist! Es gibt keine Typen in ungültigen Zeigern, aber können typisierte Funktionen über sie aufgerufen werden? Natürlich! Die Montage ist alle gleich: Sie können jede Adresse kontaktieren! Warum hat
memcpy()
nicht das Argument 'maximale Ziellänge'? Es spielt keine Rolle, tun Sie einfach, was Sie sagen. Alle Speicherbereiche sind gleich! “
Warnungen ignorieren ... aber nicht immer
Einige dieser Funktionen sind relativ einfach zu handhaben. Cook kommentierte: „Linus [Torvalds] mag die Idee, immer lokale Variablen zu initialisieren. Also mach es einfach. “
Aber mit einer Reservierung. Wenn Sie eine lokale Variable in switch initialisieren, erhalten Sie eine Warnung: "Die Anweisung wird niemals ausgeführt
[-Wswitch-unreachable]
", da der Compiler den Code verarbeitet. Diese Warnung kann ignoriert werden.
Es können jedoch nicht alle Warnungen ignoriert werden. "Arrays mit variabler Länge sind immer schlecht", sagte Cook. Weitere Probleme sind Stapelerschöpfung, Zeilenüberlauf und Verstöße gegen den Seitenschutz. Darüber hinaus machte Cook auf die
Langsamkeit von VLA aufmerksam . Durch das Entfernen aller VLAs aus dem Kernel wurde die Leistung um 13% erhöht. Die Verbesserung von Geschwindigkeit und Sicherheit ist ein doppelter Vorteil.
Obwohl die VLAs fast aus dem Kernel entfernt wurden, blieben sie in einem Code. Glücklicherweise sind VLAs mit dem Compiler-
-Wvla
leicht zu finden.
Ein weiteres Problem ist in der Semantik von C verborgen. Wenn im Schalter eine Unterbrechung fehlt, was meinte der Programmierer dann? Das Überspringen einer Unterbrechung kann unter verschiedenen Bedingungen zur Codeausführung führen. Dies ist ein bekanntes Problem.
Wenn Sie im vorhandenen Code nach break / switch-Anweisungen suchen, können Sie mit
-Wimplicit-fallthrough
eine neue switch-Anweisung hinzufügen. Dies ist eigentlich ein Kommentar, aber moderne Compiler analysieren ihn. Sie können
das Fehlen einer Unterbrechung auch
explizit mit einem "Fallthrough" -Kommentar markieren .
Cook stellte auch einen Leistungseinbruch fest, als er die Grenzen für die
Zuweisung des Plattenspeichers überprüfte. Wenn Sie beispielsweise
strcpy()-family
überprüfen, wird die Leistung um 2% reduziert. Alternativen wie
strncpy()
ihre eigenen Probleme. Es stellt sich heraus, dass Strncpy nicht immer mit einem Nullzeichen endet. Cook sprach das Publikum traurig an: "Wo kann ich die besten APIs bekommen?"
Während einer Fragerunde fragte ein Linux-Entwickler: "Kann ich alte, schlechte APIs loswerden?" Cook antwortete, dass Linux das Konzept der Legacy-APIs für einige Zeit unterstütze. Trotzdem lehnte Torvalds diese Idee ab und argumentierte, dass eine veraltete API vollständig weggeworfen werden sollte. Das ewige Löschen der API sei jedoch "politisch gefährlich", fügte Cook hinzu. Also, während wir stecken bleiben.
Langfristige Lösung des Problems? Mehr Entwickler verstehen Sicherheitsprobleme
Cook sieht eine lange und schwierige Reise vor. Die Idee, einen Linux C-Dialekt zu erstellen, schien einst attraktiv, wird es aber nicht. Das eigentliche Problem mit dem gefährlichen Code ist, dass "die Leute den Code nicht bereinigen wollen - nicht nur schlechten Code, sondern auch C selbst", sagte er. Wie bei allen Open Source-Projekten „brauchen wir engagiertere Entwickler, Prüfer, Tester und Backport-Spezialisten.“
Gefährliches C: Unterricht
- C ist eine ausgereifte und leistungsstarke Sprache, die jedoch technische Schwierigkeiten und Sicherheitsprobleme verursacht.
- Linux-Entwickler achten besonders auf die Sicherung von C (ohne die Leistung zu verlieren), da der größte Teil des Betriebssystems darauf geschrieben ist.
- Der Linux Linux-Kernel-Sicherheitsingenieur identifizierte bestimmte Sprachschwachstellen und erklärte, wie diese vermieden werden können.