mirror of
https://github.com/pi-hole/FTL.git
synced 2026-08-25 02:15:23 +01:00
pi_hole_extra_headers is a global char[1024] buffer written by API handlers and read/cleared by civetweb's send_additional_header(), but civetweb runs up to 50 worker threads concurrently. When multiple threads handle authenticated requests in parallel, one thread can overwrite or clear another's header data, causing wrong Set-Cookie headers to be sent to wrong clients or cookies to be dropped entirely. Make pi_hole_extra_headers _Thread_local so each worker thread gets its own buffer. This is safe because civetweb handles each request entirely within a single thread. The auth_data session array has a similar race: concurrent threads read/modify sessions without synchronization. Add a pthread mutex protecting all auth_data access, using AUTOLOCK/AUTOUNLOCK macros based on __attribute__((cleanup)) for RAII-style auto-unlock — ensuring the mutex is released on every exit path, including hidden returns inside JSON macros that do `return 500` on allocation failure. Change api->session from a pointer into auth_data to an embedded struct copy so downstream API handlers read from a per-request snapshot rather than shared state. Use JSON_COPY_STR_TO_OBJECT for auth_data strings so the JSON tree owns its own copies after the lock is released. Fixes: #2824 Signed-off-by: Dominik <dl6er@dl6er.de>
90 lines
3.3 KiB
Diff
90 lines
3.3 KiB
Diff
diff --git a/src/webserver/civetweb/civetweb.c b/src/webserver/civetweb/civetweb.c
|
|
index 81f642be..ed360a76 100644
|
|
--- a/src/webserver/civetweb/civetweb.c
|
|
+++ b/src/webserver/civetweb/civetweb.c
|
|
@@ -4135,6 +4135,14 @@ send_additional_header(struct mg_connection *conn)
|
|
if (header && header[0]) {
|
|
mg_response_header_add_lines(conn, header);
|
|
}
|
|
+
|
|
+ /*************** Pi-hole modification ****************/
|
|
+ if (pi_hole_extra_headers[0] != '\0') {
|
|
+ mg_response_header_add_lines(conn, pi_hole_extra_headers);
|
|
+ // Invalidate extra headers after having sent them to avoid repetitions
|
|
+ pi_hole_extra_headers[0] = '\0';
|
|
+ }
|
|
+ /*****************************************************/
|
|
}
|
|
|
|
|
|
@@ -4530,6 +4538,48 @@ mg_send_http_error_impl(struct mg_connection *conn,
|
|
}
|
|
|
|
|
|
+/************************************** Pi-hole method **************************************/
|
|
+CIVETWEB_API int
|
|
+my_send_http_error_headers(struct mg_connection *conn,
|
|
+ int status, const char* mime_type,
|
|
+ long long content_length)
|
|
+{
|
|
+ if ((mime_type == NULL) || (*mime_type == 0)) {
|
|
+ /* No content type defined: default to text/html */
|
|
+ mime_type = "text/html";
|
|
+ }
|
|
+
|
|
+ mg_response_header_start(conn, status);
|
|
+ send_no_cache_header(conn);
|
|
+ send_additional_header(conn);
|
|
+ mg_response_header_add(conn, "Content-Type", mime_type, -1);
|
|
+ if (content_length < 0) {
|
|
+ /* Size not known. Use chunked encoding (HTTP/1.x) */
|
|
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
|
|
+ /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
|
|
+ mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
|
|
+ }
|
|
+ } else {
|
|
+ char len[32];
|
|
+ int trunc = 0;
|
|
+ mg_snprintf(conn,
|
|
+ &trunc,
|
|
+ len,
|
|
+ sizeof(len),
|
|
+ "%" UINT64_FMT,
|
|
+ (uint64_t)content_length);
|
|
+ if (!trunc) {
|
|
+ /* Since 32 bytes is enough to hold any 64 bit decimal number,
|
|
+ * !trunc is always true */
|
|
+ mg_response_header_add(conn, "Content-Length", len, -1);
|
|
+ }
|
|
+ }
|
|
+ mg_response_header_send(conn);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+/********************************************************************************************/
|
|
+
|
|
CIVETWEB_API int
|
|
mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
|
|
{
|
|
diff --git a/src/webserver/civetweb/civetweb.h b/src/webserver/civetweb/civetweb.h
|
|
index 7ea45fb2..f879ff3e 100644
|
|
--- a/src/webserver/civetweb/civetweb.h
|
|
+++ b/src/webserver/civetweb/civetweb.h
|
|
@@ -963,6 +964,16 @@ CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
|
|
PRINTF_FORMAT_STRING(const char *fmt),
|
|
...) PRINTF_ARGS(3, 4);
|
|
|
|
+/************************************** Pi-hole method **************************************/
|
|
+int my_send_http_error_headers(struct mg_connection *conn,
|
|
+ int status, const char* mime_type,
|
|
+ long long content_length);
|
|
+
|
|
+// Buffer used for additional "Set-Cookie" headers
|
|
+#define PIHOLE_HEADERS_MAXLEN 1024
|
|
+extern _Thread_local char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN];
|
|
+/********************************************************************************************/
|
|
+
|
|
|
|
/* Send "HTTP 200 OK" response header.
|
|
* After calling this function, use mg_write or mg_send_chunk to send the
|