Böses PHP: „Allowed memory size exhausted“ durch fread


Letzte Woche trat bei einem User folgender Fehler bei der Aktivierung von WPPP auf: Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 956301313 Bytes) […]. Mal eben fast ein Gigabyte Speicher anfordern? WPPP sollte eigentlich Speicher „sparen“.

Die Ursache war (relativ) schnell gefunden. Die französische Sprachdatei des Plugins All In One SEO Pack scheint defekt zu sein. In den String-Tabellen waren die ersten Einträge ok, dann aber scheinen ein paar Byte verrutscht zu sein was Pointer weit über das Ende der Datei und viel zu große String-Längen verursacht, zum Beispiel eben jene 956.301.313 Bytes.

Was passiert da? Also, an der entsprechenden Stelle werden die Werte aus der Tabelle gelesen (Position und Länge). Dann wird per fseek an eine völlig utopische Stelle in der Datei gesprungen (Dateilänge etwa 6KB, Sprungziel irgendwo im MB-Bereich).

Zunächst habe ich mich gefragt, warum fseek es überhaupt zulässt, weit hinter das Ende der Datei zu springen. Die Begründung dafür lautet, man könne ja möglicherweise an eine noch nicht existierende Stelle in der Datei schreiben wollen. Die Datei wurde zwar nur lesend geöffnet, aber ok. Kein schönes Verhalten, aber ok.

Sprünge mit fseek setzen aber auch nicht das EOF-Flag. feof liefert also trotzdem false. Und sogar ftell teilt mir mit, ich befände mich an der (nicht existierenden) Stelle. Wie also rausfinden, dass da nichts Lesbares ist?

PHP sagt: Der nächste Lesezugriff wird einfach fehlschlagen. Und genau das wird im WPPP-Code nach dem Seek gemacht. Es wird gelesen. Dank der defekten Datei soll zwar fast ein GB gelesen werden, aber da steht ja eh nichts (außerdem ist die Datei ja gar nicht so lang), also sollte das fehlschlagen. Also einfach den Fehler abfangen und fertig.

Tja, schön wär’s. Dieses dämliche fread scheint sich nämlich erst den Speicher für die angeforderten Daten zu reservieren und danach überhaupt erst zu versuchen zu lesen. Bevor also fread überhaupt merkt, dass da gar nichts zum Lesen ist, fliegt es wegen zu wenig Speicher, den es gar nicht brauchen würde, aufs Maul. Danke fread. Danke PHP.

Sorry, aber diese Funktionsweise finde ich dämlich. Wollte das nur mal loswerden.

Gelöst habe ich das Problem übrigens, indem beim Laden die Tabellen auf Zeiger und Längen, die über das Dateiende hinausgehen, überprüft werden.


Kommentare sind geschlossen.