Fix queued cURL resume lifetime

BI
7d ago
+90-3

Summary

A native-cURL multi handle can be destroyed after socket readiness queues a deferred coroutine resume but before that resume runs. The callback then reads the freed Multi object and can try to resume an arbitrary pointer, crashing the worker.

This is reachable when socket readiness and a curl_multi_select() timeout occur in the same reactor iteration. Hypervel encountered the same path through asynchronous Guzzle requests in its HTTP client destination-policy coverage.

Fix

Keep the Multi object alive while its deferred resume is pending. Releasing an active object now marks it for deletion, and the queued callback deletes it before reading the stored coroutine pointer. The three owning destruction paths use the same handoff.

Tests

Added a local TCP regression that overlaps socket readiness, the select timeout, and multi-handle destruction. It segfaults on unchanged 6.1 and exits normally with this change.

Also ran the nearby multi-handle close, destructor, select cancellation, select timeout, and socket lifecycle tests.