Zum Hauptinhalt Zur Navigation

FPU: Tausende Python-Pakete könnten falsche Berechnungen liefern

Wenig bekannte Compiler-Optionen und Besonderheiten bei der Gleitkommaarithmetik könnten in Python zu falschen Ergebnissen führen.
/ Sebastian Grüner
16 Kommentare News folgen (öffnet im neuen Fenster)
Viel Python-Pakete liefern unter Umständen falsche Ergebnisse. (Bild: Pixabay)
Viel Python-Pakete liefern unter Umständen falsche Ergebnisse. Bild: Pixabay

Der Informatiker und Assistant Professor an der NYU Tandon, Brendan Dolan-Gavitt, berichtet auf seinem Blog davon(öffnet im neuen Fenster) , dass mehr als 2.500 Python-Pakete unter bestimmten Umständen falsche Ergebnisse bei numerischen Berechnungen liefern könnten. Ausgelöst werde dies von einer "kleinen Handvoll" Pakete, die eine tückische Compiler-Option verwendeten, mit der die Berechnungen vermeintlich beschleunigt werden könnten, die Ergebnisse aber eben auch verändert würden.

Dies fiel Dolan-Gavitt durch eine Fehlermeldung der Numpy-Bibliothek für numerische Berechnungen auf. Darin wird darauf hingewiesen, dass die sogenannten subnormalen oder nichtnormalisierten Gleitkommazahlen wie Null behandelt werden. Bei den Subnormalen handelt es sich um Zahlen sehr nahe Null. Die Meldung habe für Besorgnis gesorgt, weil Gleitkommaarithmetik notorische Tücken habe, schreibt Dolan-Gavitt, da einige spezialisierte Algorithmen in dem beschriebenen Fall falsche Ergebnisse lieferten.

Ursache für den Fehler ist demnach, dass eine dynamische Bibliothek mit der Compiler-Option -ffast-math erstellt und von dem genutzten Python-Programm geladen wird. Die Compiler-Option führt standardmäßig dazu, dass die CPU-Flags für FTZ (Flush to Zero) und DAZ(öffnet im neuen Fenster) (Denormals are zero, Nichtnormalisierte als Null) gesetzt werden, und zwar insbesondere auch dann, wenn die Bibliothek dynamisch geladen wird. Dadurch ändert sich aber das Verhalten des gesamten Prozesses, nicht nur der Bibliothek.

Im konkreten Fall des Fehlers von Dolan-Gavitt fand dieser schnell den Verursacher: die Netzwerkbibliothek Gevent. Diese nutzt als Compiler-Option die Optimierung -Ofast , die wiederum -ffast-math impliziert. Wie sich an Gevent selbst zeigte, hat aber aber auch das triviale Abschalten der fehlerbehafteten Option durch -fno-fast-math im Fall der Nutzung von -Ofast anders als erwartet keinen Effekt. Die zuvor versuchte Fehlerbehebung in Gevent führte also nicht zum gewünschten Ergebnis.

Viele weitere Python-Pakete betroffen

Weiter schreibt Dolan-Gavitt: "Nachdem das unmittelbare Rätsel gelöst war, wollte ich herausfinden, wie viele andere Projekte (absichtlich oder versehentlich) -ffast-math in ihren dynamischen Bibliotheken, die in PyPI hochgeladen wurden, aktiviert haben könnten." Dafür schrieb der Informatiker zunächst ein Skript, das Binärdateien darauf untersuchen kann, ob die von -ffast-math ausgelösten Veränderungen der FTZ-/DAZ-Flags vorhanden sind.

Mithilfe weiterer Skripte lud Dolan-Gavitt dann das gesamte Python-Paketarchiv herunter und untersuchte die Binärdateien. Demnach haben nur 49 Pakete jemals eine entsprechende Binärdatei verteilt, die mit der möglicherweise fehlerverursachenden Option erstellt wurde.

Reklame

Python 3: Das umfassende Handbuch: Über 1.000 Seiten Sprachgrundlagen, OOP und Beispielprogramme

Jetzt bestellen bei Amazon (öffnet im neuen Fenster)

Anschließend versuchte der Informatiker, die Metadaten und Abhängigkeiten der Pakete zueinander zu untersuchen, was offenbar nicht besonders einfach war. Dolan-Gavitt schätzt letztlich, dass rund 2.500 Python-Pakete wiederum von einem der gefundenen Pakete abhängen, die bewusst oder unbewusst -ffast-math verwenden. Einige davon haben mehr als eine Million Downloads pro Monat. Zusammen haben die Pakete gar fast zehn Millionen Downloads pro Monat.

Dolan-Gavitt warnt andere Programmierer vor den möglicherweise bisher nicht erkannten Folgen der eventuell fehlerhaften Berechnungen. Darüber hinaus weist er darauf hin, dass das Verhalten der Compiler geändert werden sollte. Ein entsprechender GCC-Bugreport(öffnet im neuen Fenster) ist aber schon mehr als zehn Jahre alt, ohne dass sich das Verhalten geändert hat. Eine aktuelle Diskussion der LLVM-Entwickler(öffnet im neuen Fenster) weist zudem darauf hin, dass diese das GCC-Verhalten umsetzen, auch weil dies ebenfalls vor rund zehn Jahren explizit gewünscht war.


Relevante Themen