LibRequests: Protect Request callbacks against stopped requests

When the request is stopped, we clear its internal stream data. There is
a window where RequestServer may have sent an IPC message whose callback
will try to access that data in the time between the data being cleared
and RS receiving the stop signal. When this happens, just bail from IPC.
This commit is contained in:
Timothy Flynn 2025-03-30 16:08:25 -04:00
parent 9ad8e7782d
commit 0cb506277f

View file

@ -30,6 +30,10 @@ bool Request::stop()
void Request::set_request_fd(Badge<Requests::RequestClient>, int fd)
{
// If the request was stopped while this IPC was in-flight, just bail.
if (!m_internal_stream_data)
return;
VERIFY(m_fd == -1);
m_fd = fd;
@ -117,6 +121,10 @@ void Request::set_up_internal_stream_data(DataReceived on_data_available)
auto user_on_finish = move(on_finish);
on_finish = [this](auto total_size, auto const& timing_info, auto network_error) {
// If the request was stopped while this IPC was in-flight, just bail.
if (!m_internal_stream_data)
return;
m_internal_stream_data->total_size = total_size;
m_internal_stream_data->network_error = network_error;
m_internal_stream_data->timing_info = timing_info;
@ -125,6 +133,10 @@ void Request::set_up_internal_stream_data(DataReceived on_data_available)
};
m_internal_stream_data->on_finish = [this, user_on_finish = move(user_on_finish)]() {
// If the request was stopped while this IPC was in-flight, just bail.
if (!m_internal_stream_data)
return;
if (!m_internal_stream_data->user_finish_called && (!m_internal_stream_data->read_stream || m_internal_stream_data->read_stream->is_eof())) {
m_internal_stream_data->user_finish_called = true;
user_on_finish(m_internal_stream_data->total_size, m_internal_stream_data->timing_info, m_internal_stream_data->network_error);
@ -135,6 +147,10 @@ void Request::set_up_internal_stream_data(DataReceived on_data_available)
static constexpr size_t buffer_size = 256 * KiB;
static char buffer[buffer_size];
// If the request was stopped while this IPC was in-flight, just bail.
if (!m_internal_stream_data)
return;
do {
auto result = m_internal_stream_data->read_stream->read_some({ buffer, buffer_size });
if (result.is_error() && (!result.error().is_errno() || (result.error().is_errno() && result.error().code() != EINTR)))