fix(protocol): harden STATUS_ERROR_DETAIL receive path
Address review/security findings in the 2.21.0 error-detail feature: - Keepalive drain no longer erases the terminal detail: capture/clear is skipped for STATUS_KEEPALIVE so the reason the peer just sent survives the owed keepalive replies. - Replace the capture path with a dedicated protocol_receive_error_detail: the declared length is validated against MAX_ERROR_DETAIL_BYTES before any allocation, over-cap bodies are drained through a fixed scratch buffer (so the stream never desyncs), in-cap bodies read straight into the thread-local detail buffer, and session->max_alloc is never raised. Lengths beyond MAX_STRING_SIZE are treated as a fatal framing error. - The detail body now honors the caller's deadline (timed/keepalive paths) and polls the abort callback between drain chunks. - Escape peer-controlled detail text with output_escape before logging it in client_send.c and config.c. - Clear io_error_detail in io_set_fds so a new connection on the same thread cannot inherit a stale reason. - Add unit tests for the keepalive-survival, over-cap drain, absurd-length fatal framing, and deadline-clamped body read cases.
This commit is contained in:
+8
-3
@@ -1239,10 +1239,15 @@ bool config_send(int file_descriptor, const Config* config) {
|
||||
}
|
||||
if (status != STATUS_OK) {
|
||||
const char* detail = protocol_last_error();
|
||||
if (detail && detail[0] != '\0')
|
||||
log_message(LOG_LEVEL_ERROR, "Error transmitting config: %s", detail);
|
||||
else
|
||||
if (detail && detail[0] != '\0') {
|
||||
/* The detail is peer-controlled: escape it before logging. */
|
||||
char* escaped = output_escape(detail, log_get_8_bit_output());
|
||||
log_message(LOG_LEVEL_ERROR, "Error transmitting config: %s",
|
||||
escaped ? escaped : "<allocation failed>");
|
||||
free(escaped);
|
||||
} else {
|
||||
log_message(LOG_LEVEL_ERROR, "Error transmitting config");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user