Merge branch 'fix/fix_ws_server_subprotocols_null_dereference_v5.5' into 'release/v5.5'

fix: fixes websocket server possible null dereference (v5.5)

See merge request espressif/esp-idf!46918
This commit is contained in:
Mahavir Jain
2026-03-30 10:31:32 +05:30
5 changed files with 67 additions and 7 deletions
@@ -844,7 +844,8 @@ esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_p
* @note
* - This function is necessary in order to handle multiple requests simultaneously.
* See examples/async_requests for example usage.
* - You must call httpd_req_async_handler_complete() when you are done with the request.
* - You must call httpd_req_async_handler_complete() when you are done with the request
* and also on any error conditions.
*
* @param[in] r The request to create an async copy of
* @param[out] out A newly allocated request which can be used on an async thread
@@ -213,6 +213,9 @@ esp_err_t httpd_unregister_uri_handler(httpd_handle_t handle,
ESP_LOGD(TAG, LOG_FMT("[%d] removing %s"), i, hd->hd_calls[i]->uri);
free((char*)hd->hd_calls[i]->uri);
#ifdef CONFIG_HTTPD_WS_SUPPORT
free((char*)hd->hd_calls[i]->supported_subprotocol);
#endif // CONFIG_HTTPD_WS_SUPPORT
free(hd->hd_calls[i]);
hd->hd_calls[i] = NULL;
@@ -251,6 +254,9 @@ esp_err_t httpd_unregister_uri(httpd_handle_t handle, const char *uri)
ESP_LOGD(TAG, LOG_FMT("[%d] removing %s"), i, uri);
free((char*)hd->hd_calls[i]->uri);
#ifdef CONFIG_HTTPD_WS_SUPPORT
free((char*)hd->hd_calls[i]->supported_subprotocol);
#endif // CONFIG_HTTPD_WS_SUPPORT
free(hd->hd_calls[i]);
hd->hd_calls[i] = NULL;
found = true;
@@ -282,6 +288,9 @@ void httpd_unregister_all_uri_handlers(struct httpd_data *hd)
ESP_LOGD(TAG, LOG_FMT("[%d] removing %s"), i, hd->hd_calls[i]->uri);
free((char*)hd->hd_calls[i]->uri);
#ifdef CONFIG_HTTPD_WS_SUPPORT
free((char*)hd->hd_calls[i]->supported_subprotocol);
#endif // CONFIG_HTTPD_WS_SUPPORT
free(hd->hd_calls[i]);
hd->hd_calls[i] = NULL;
}
+7 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -82,12 +82,15 @@ static bool httpd_ws_get_response_subprotocol(const char *supported_subprotocol,
/* Get first subprotocol from comma separated list */
char *rest = NULL;
char *s = strtok_r(subprotocol, ", ", &rest);
do {
if (strncmp(s, supported_subprotocol, sizeof(subprotocol)) == 0) {
int supported_subprotocol_len = strlen(supported_subprotocol);
while (s != NULL) {
if (strlen(s) == supported_subprotocol_len &&
strncmp(s, supported_subprotocol, supported_subprotocol_len) == 0) {
ESP_LOGD(TAG, "Requested subprotocol supported: %s", s);
return true;
}
} while ((s = strtok_r(NULL, ", ", &rest)) != NULL);
s = strtok_r(NULL, ", ", &rest);
}
ESP_LOGW(TAG, "Sec-WebSocket-Protocol %s not supported, supported subprotocol is %s", subprotocol, supported_subprotocol);
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,6 +8,7 @@
#include <stdbool.h>
#include <esp_system.h>
#include <esp_http_server.h>
#include <esp_heap_caps.h>
#include "unity.h"
#include "test_utils.h"
@@ -31,6 +32,21 @@ httpd_uri_t handler_limit_uri (char* path)
return uri;
};
#ifdef CONFIG_HTTPD_WS_SUPPORT
static httpd_uri_t handler_limit_ws_uri(char *path, const char *subprotocol)
{
httpd_uri_t uri = {
.uri = path,
.method = HTTP_GET,
.handler = null_func,
.user_ctx = NULL,
.is_websocket = true,
.supported_subprotocol = subprotocol,
};
return uri;
}
#endif /* CONFIG_HTTPD_WS_SUPPORT */
static inline unsigned num_digits(unsigned x)
{
unsigned digits = 1;
@@ -81,6 +97,36 @@ void test_handler_limit(httpd_handle_t hd)
TEST_ASSERT(httpd_unregister_uri_handler(hd, uris[i].uri, uris[i].method) == ESP_OK);
}
basic_sanity = false;
#ifdef CONFIG_HTTPD_WS_SUPPORT
/* --- WS subprotocol memory leak check ---
* Each registration strdup's the supported_subprotocol string.
* Verify that unregistration frees it (expected leak per handler:
* strlen(subprotocol) + 1 bytes if the bug is present).
*/
char ws_paths[HTTPD_TEST_MAX_URI_HANDLERS][8];
httpd_uri_t ws_uris[HTTPD_TEST_MAX_URI_HANDLERS];
const char *subprotocol = "chat";
for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS; i++) {
sprintf(ws_paths[i], "/ws%d", i);
ws_uris[i] = handler_limit_ws_uri(ws_paths[i], subprotocol);
}
size_t heap_before = heap_caps_get_free_size(MALLOC_CAP_8BIT);
for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS; i++) {
TEST_ASSERT(httpd_register_uri_handler(hd, &ws_uris[i]) == ESP_OK);
}
for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS; i++) {
TEST_ASSERT(httpd_unregister_uri_handler(hd, ws_uris[i].uri, ws_uris[i].method) == ESP_OK);
}
size_t heap_after = heap_caps_get_free_size(MALLOC_CAP_8BIT);
int leaked = (int)heap_before - (int)heap_after;
TEST_ASSERT_MESSAGE(leaked <= 0, "Heap leaked after WS handler unregister");
#endif /* CONFIG_HTTPD_WS_SUPPORT */
}
/********************* Test Handler Limit End *******************/
@@ -100,7 +146,7 @@ httpd_handle_t test_httpd_start(uint16_t id)
/* Currently this only tests for the number of tasks.
* Heap leakage is not tested as LWIP allocates memory
* which may not be freed immedietly causing erroneous
* which may not be freed immediately causing erroneous
* evaluation. Another test to implement would be the
* monitoring of open sockets, but LWIP presently provides
* no such API for getting the number of open sockets.
@@ -7,3 +7,4 @@ CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_HTTPD_WS_SUPPORT=y