From 01874b0e5a17d25b4101699dab18562bffcd10e0 Mon Sep 17 00:00:00 2001 From: Azreyo <58790873+Azreyo@users.noreply.github.com> Date: Sun, 2 Nov 2025 12:22:12 +0100 Subject: [PATCH] Enhance error handling for HTTP/2 file size, validate mmap cache entries, improve WebSocket connection handling, enforce maximum URI length, and limit WebSocket payload size. --- src/http2.c | 13 ++++++++++++ src/performance.c | 6 ++++++ src/server.c | 50 +++++++++++++++++++++++++++++++++++++++++------ src/websocket.c | 8 ++++++++ 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/http2.c b/src/http2.c index 5aca634..c6226a4 100644 --- a/src/http2.c +++ b/src/http2.c @@ -233,6 +233,19 @@ static int on_frame_recv_callback(nghttp2_session *session, break; } + if (st.st_size < 0 || st.st_size > 0x7FFFFFFFFFFFFFFF) + { + close(fd); + log_event("HTTP/2: File size out of bounds"); + + // Send 500 error + nghttp2_nv hdrs[] = { + {(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL); + break; + } + // Get MIME type char *mime_type = get_mime_type(filepath); if (!mime_type) diff --git a/src/performance.c b/src/performance.c index 804e55e..5b7a8d1 100644 --- a/src/performance.c +++ b/src/performance.c @@ -122,6 +122,12 @@ mmap_cache_entry_t *get_cached_file(const char *path) { if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0) { + if (mmap_cache[i].mmap_data == NULL || mmap_cache[i].size == 0) + { + pthread_mutex_unlock(&mmap_cache_mutex); + return NULL; + } + mmap_cache[i].last_access = time(NULL); mmap_cache[i].ref_count++; pthread_mutex_unlock(&mmap_cache_mutex); diff --git a/src/server.c b/src/server.c index 9961ce2..ad2c140 100644 --- a/src/server.c +++ b/src/server.c @@ -452,6 +452,11 @@ static int is_websocket_upgrade(const char *request) static void *handle_websocket(void *arg) { ws_connection_t *conn = (ws_connection_t *)arg; + + if (!conn) + { + pthread_exit(NULL); + } log_event("WebSocket connection established"); @@ -471,7 +476,9 @@ static void *handle_websocket(void *arg) if (bytes_received <= 0) { - break; + ws_close_connection(conn, 1000); + free(conn); + pthread_exit(NULL); } ws_frame_header_t header; @@ -482,7 +489,9 @@ static void *handle_websocket(void *arg) { log_event("Failed to parse WebSocket frame"); free(payload); - break; + ws_close_connection(conn, 1002); + free(conn); + pthread_exit(NULL); } switch (header.opcode) @@ -645,9 +654,18 @@ void *handle_http_client(void *arg) } char filepath[512]; - snprintf(filepath, sizeof(filepath), "%s%s", config.www_path, + int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path, (*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url); free(sanitized_url); + + if (written < 0 || written >= (int)sizeof(filepath)) + { + log_event("Path too long, potential buffer overflow attempt"); + const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n"; + send(client_socket, error_response, strlen(error_response), 0); + close(client_socket); + pthread_exit(NULL); + } // Get MIME type char *mime_type = get_mime_type(filepath); @@ -1530,10 +1548,22 @@ char *sanitize_url(const char *url) } else if (c == '%') { - // URL encoding - only allow safe encoded characters + // URL encoding - decode and validate the character if (i + 2 < url_len && isxdigit(url[i + 1]) && isxdigit(url[i + 2])) { - sanitized[j++] = c; + char hex[3] = {url[i + 1], url[i + 2], 0}; + int decoded = (int)strtol(hex, NULL, 16); + + // Block encoded directory traversal characters and control characters + if (decoded == '.' || decoded == '/' || decoded == '\\' || + decoded == 0x00 || decoded < 0x20 || decoded > 0x7E) + { + free(sanitized); + return NULL; + } + + sanitized[j++] = (char)decoded; + i += 2; } else { @@ -1642,6 +1672,8 @@ void cleanup_thread_pool() return; } + pthread_mutex_lock(&thread_pool_mutex); + for (int i = 0; i < thread_pool_size; i++) { if (thread_pool[i].busy) @@ -1650,9 +1682,15 @@ void cleanup_thread_pool() pthread_join(thread_pool[i].thread, NULL); } } - free(thread_pool); + + ThreadInfo *temp = thread_pool; thread_pool = NULL; thread_pool_size = 0; + + pthread_mutex_unlock(&thread_pool_mutex); + + // Free after releasing lock and nullifying pointer + free(temp); } void cache_file(const char *path, const char *data, size_t size, const char *mime_type) diff --git a/src/websocket.c b/src/websocket.c index 00bf146..d05b01d 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -116,6 +116,9 @@ int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_ // Parse WebSocket frame int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload) { + // Maximum allowed WebSocket payload size (10MB) + #define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024) + if (len < 2) { return -1; @@ -151,6 +154,11 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u header->payload_length = payload_len; } + if (header->payload_length > MAX_WEBSOCKET_PAYLOAD) + { + return -1; + } + if (header->mask) { if (len < offset + 4)