Repository navigation
Fix segfault when dropping multi handle with active transfers - #645
Conversation
Fix a potential segfault that can occur when dropping a `Multi` before dropping an `Easy2Handle` that is attached to the multi handle and has an active transfer. This can happen because dropping a `Multi` will also always drop its inner `MultiData`, which holds socket and timer callback functions supplied by the user, but the underlying `CURLM` handle is _not_ dropped until both the `Multi` and all of its attached `Easy2Handle`s are dropped. If the `Multi` is dropped first, then the socket and timer callback functions will be dropped while the `CURLM` handle is still active, which can lead to a segfault if the callbacks are invoked after they have been dropped. The libcurl docs have [this to say about `CURLMOPT_SOCKETFUNCTION`](https://curl.se/libcurl/c/CURLMOPT_SOCKETFUNCTION.html): > This callback may get invoked at any time when interacting with libcurl. This may even happen after all transfers are done and is *likely* to happen *during* a call to curl_multi_cleanup when cached connections are shut down. This paragraph is clear that libcurl _may_ invoke our `CURLMOPT_SOCKETFUNCTION` callback when `curl_multi_cleanup` is invoked, which in our case, might be called after we've already freed our callback function. To fix this, add a drop handler to `Multi` that removes any custom socket and timer callback functions. This ensures that if any easy handles outlive the `Multi`, they will not attempt to invoke callbacks that have already been dropped.
…simple of a patch as the previous one.
|
I've updated this PR with an alternative implementation. This implementation is a bit less minimal and uses Generally I would avoid
Note that You can compare the new implementation with the old one: |
Implement a workaround for a bug in curl-rust where dropping an `Easy2Handle` after its attached `Multi` has already been dropped can cause a segfault, if that handle has one or more open sockets with pending operations. Our workaround is to ensure that we drop all easy handles first before proceeding with the normal drop order. I have also proposed a fix for this bug upstream (<sagebind/curl-rust#645>), but that will only apply to future versions of curl-rust. This workaround here in Isahc will avoid the issue for all versions of curl-rust that are affected by this bug that a user may be using. This should fix #459.
Implement a workaround for a bug in curl-rust where dropping an `Easy2Handle` after its attached `Multi` has already been dropped can cause a segfault, if that handle has one or more open sockets with pending operations. Our workaround is to ensure that we drop all easy handles first before proceeding with the normal drop order. I have also proposed a fix for this bug upstream (<sagebind/curl-rust#645>), but that will only apply to future versions of curl-rust. This workaround here in Isahc will avoid the issue for all versions of curl-rust that are affected by this bug that a user may be using. This should fix #459.
Fix a potential segfault that can occur when dropping a
Multibefore dropping anEasy2Handlethat is attached to the multi handle and has an active transfer. This can happen because dropping aMultiwill also always drop its innerMultiData, which holds socket and timer callback functions supplied by the user, but the underlyingCURLMhandle is not dropped until both theMultiand all of its attachedEasy2Handles are dropped. If theMultiis dropped first, then the socket and timer callback functions will be dropped while theCURLMhandle is still active, which can lead to a segfault if the callbacks are invoked after they have been dropped.The libcurl docs have this to say about
CURLMOPT_SOCKETFUNCTION:This paragraph is clear that libcurl may invoke our
CURLMOPT_SOCKETFUNCTIONcallback whencurl_multi_cleanupis invoked, which in our case, is done byRawMulti::dropwhich might be called after we've already freed our callback function.To fix this, use
RcandRefCellto extend the life of the configured callbacks until both theMultiand all attachedEasy2Handles are dropped, avoiding the need to unset the callbacks until all socket activity is done. (MultiWakerstill theoretically may cause the callbacks to be invoked, so we still unset them.)Generally I would avoid
RefCellas a way of solving lifetime problems, but in this case it seems likeRefCellmodels libcurl's actual mutability contract decently well, so I think it works.This issue can be easily reproduced with the following program, which ends in a
SIGSEGV100% of the time on my machine:Thanks to @joelposti for reporting this issue on sagebind/isahc#459 and supplying a larger reproduction of the issue with detailed steps, which made it much easier for me to find the root cause here.