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

Đang mở Phù hợp với người mới
#189 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
76/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Ít trao đổi
Công nghệ
php
Lĩnh vực
backend, networking

Hướng nghiên cứu

Bắt đầu trong src/WritableResourceStream.php tại guard của handleWrite() quanh dòng 151, sau đó lần theo cách các kết quả của fwrite() và các cảnh báo được bắt lại ảnh hưởng đến bộ đệm ghi và listener. Tái hiện bằng một ReactPHP SecureServer và một máy khách TLS bị kết thúc đột ngột trong khi các thao tác ghi đang được xếp hàng. Được xem là hoàn tất khi một lần ghi trả về false mà không có cảnh báo sẽ đóng stream, còn lần ghi trả về zero vẫn giữ nguyên hành vi hiện có phụ thuộc vào cảnh báo và không còn gây ra vòng lặp CPU.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
PHP
Star
692
Fork
63
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của reactphp/stream

Tất cả issue của reactphp/stream

Issue tương tự

Thêm issue về PHP

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.