lib/curl/Adapter: catch and postpone exceptions in WriteFunction()

This fixes a std::terminate() crash in the CURL storage plugin when
PropfindOperation::OnHeaders() throws an exception after receiving a
non-207 status.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1559
This commit is contained in:
Max Kellermann 2022-07-01 12:38:49 +02:00
parent b88d1e6820
commit 5a5655b790
4 changed files with 29 additions and 1 deletions

2
NEWS
View File

@ -1,4 +1,6 @@
ver 0.23.8 (not yet released)
* storage
- curl: fix crash if web server does not understand WebDAV
ver 0.23.7 (2022/05/09)
* database

View File

@ -85,6 +85,12 @@ CurlResponseHandlerAdapter::FinishBody()
void
CurlResponseHandlerAdapter::Done(CURLcode result) noexcept
{
if (postponed_error) {
state = State::CLOSED;
handler.OnError(std::move(postponed_error));
return;
}
try {
if (result != CURLE_OK) {
StripRight(error_buffer);
@ -172,6 +178,13 @@ CurlResponseHandlerAdapter::DataReceived(const void *ptr,
return received_size;
} catch (CurlResponseHandler::Pause) {
return CURL_WRITEFUNC_PAUSE;
} catch (...) {
/* from inside this libCURL callback function, we
can't do much, so we remember the exception to be
handled later by Done(), and return 0, causing the
response to be aborted with CURLE_WRITE_ERROR */
postponed_error = std::current_exception();
return 0;
}
}

View File

@ -34,6 +34,7 @@
#include <curl/curl.h>
#include <cstddef>
#include <exception>
struct StringView;
class CurlEasy;
@ -46,6 +47,12 @@ class CurlResponseHandlerAdapter {
Curl::Headers headers;
/**
* An exception caught from within the WriteFunction() which
* will later be handled by Done().
*/
std::exception_ptr postponed_error;
/** error message provided by libcurl */
char error_buffer[CURL_ERROR_SIZE];

View File

@ -50,13 +50,19 @@ public:
/**
* Status line and headers have been received.
*
* Exceptions thrown by this method will be passed to
* OnError(), aborting the request.
*/
virtual void OnHeaders(unsigned status, Curl::Headers &&headers) = 0;
/**
* Response body data has been received.
*
* May throw #Pause (but nothing else).
* May throw #Pause.
*
* Other exceptions thrown by this method will be passed to
* OnError(), aborting the request.
*/
virtual void OnData(ConstBuffer<void> data) = 0;