WritableResourceStream::handleWrite() enters infinite loop on broken TLS socket (EPIPE without PHP warning)

Offen Anfängerfreundlich
#189 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
2/5
Geschätzter Aufwand
1-3 Stunden
Anfängerfreundlichkeit
76/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Ruhig
Tech-Stack
php
Bereich
backend, networking

Rechercherichtung

Beginnen Sie in src/WritableResourceStream.php beim handleWrite()-Guard um Zeile 151 und verfolgen Sie dann, wie sich die Ergebnisse von fwrite() und abgefangene Warnungen auf den Schreibpuffer und den Listener auswirken. Reproduzieren Sie das Problem mit einem ReactPHP SecureServer und einem abrupt beendeten TLS-Client, während Schreibvorgänge in der Warteschlange stehen. Als abgeschlossen gilt die Änderung, wenn ein stilles false beim Schreiben den Stream schließt, während ein Schreiben mit dem Ergebnis null das bestehende warnungsabhängige Verhalten beibehält und keine CPU-Schleife mehr verursacht.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

On PHP 8.x with react/socket's SecureServer (TLS), when a remote client disconnects abruptly, epoll reports EPOLLOUT|EPOLLHUP on the dead socket FD. WritableResourceStream::handleWrite() calls fwrite(), which returns false because the kernel write() returns -1 EPIPE. However, PHP's OpenSSL stream wrapper does not call php_error_docref() for SSL_ERROR_SYSCALL+EPIPE errors in all code paths. The set_error_handler capture therefore gets nothing ($error === null).

The guard evaluates to false, so close() is never called. WritableResourceStream then silently passes false to substr() (implicit cast to 0 in non-strict mode), leaving the write buffer unchanged and the write listener active. epoll keeps returning EPOLLOUT|EPOLLHUP, fwrite() keeps returning false silently - 100% CPU lockup.

Reproduction: use react/socket SecureServer with TLS, kill a client with kill -9 while the server has queued writes to that client.

Fix: separate the $sent === false case (hard error - always close) from $sent === 0 (may be transient EAGAIN/WANT_WRITE - only close when PHP warning is present):

// Before
if (($sent === 0 || $sent === false) && $error !== null) {

// After
if ($sent === false || ($sent === 0 && $error !== null)) {

This issue was investigated with the help of AI, but the 100% CPU usage issue is real and after applying the above fix on production, it seems to have disappeared.

Vorherrschende Sprache
PHP
Sterne
692
Forks
63
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus reactphp/stream

Alle Issues in reactphp/stream

Ähnliche Issues

Weitere Issues zu PHP

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.