From edb3f9daf1ee605d30711f306e07738e0ddbdcdc Mon Sep 17 00:00:00 2001 From: "nilesh.kale" Date: Mon, 2 Mar 2026 12:00:03 +0530 Subject: [PATCH] fix(esp_http_client): check http_parser errno after execute to avoid DoS loop --- components/esp_http_client/esp_http_client.c | 14 ++++++++++++++ components/esp_http_server/src/httpd_parse.c | 6 ++++++ components/http_parser/http_parser.h | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index 97dee14eea6..2ff4c31c6d1 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -157,6 +157,16 @@ static esp_err_t _clear_connection_info(esp_http_client_handle_t client); #define ASYNC_TRANS_CONNECTING 0 #define ASYNC_TRANS_CONNECT_PASS 1 +/* Check http_parser for errors, returns ret_val on parse failure. */ +#define HTTP_PARSER_RETURN_ON_ERROR(tag, parser, ret_val) \ + do { \ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + ESP_LOGE(tag, "HTTP parser error: %s", \ + http_errno_description(HTTP_PARSER_ERRNO(parser))); \ + return (ret_val); \ + } \ + } while (0) + static const char *DEFAULT_HTTP_USER_AGENT = "ESP32 HTTP Client/1.0"; static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1"; static const char *DEFAULT_HTTP_PATH = "/"; @@ -1400,6 +1410,7 @@ static int esp_http_client_get_data(esp_http_client_handle_t client) // invalid state. if (!(client->is_async && rlen == 0)) { http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen); + HTTP_PARSER_RETURN_ON_ERROR(TAG, client->parser, ESP_FAIL); } } return rlen; @@ -1465,6 +1476,7 @@ int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len) if (rlen == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN && client->response->is_chunked) { /* Explicit call to parser for invoking `message_complete` callback */ http_parser_execute(client->parser, client->parser_settings, res_buffer->data, 0); + HTTP_PARSER_RETURN_ON_ERROR(TAG, client->parser, ESP_FAIL); /* ...and lowering the message severity, as closed connection from server side is expected in chunked transport */ sev = ESP_LOG_DEBUG; } @@ -1495,6 +1507,7 @@ int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len) } res_buffer->output_ptr = buffer + ridx; http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen); + HTTP_PARSER_RETURN_ON_ERROR(TAG, client->parser, ESP_FAIL); ridx += res_buffer->raw_len; need_read -= res_buffer->raw_len; @@ -1677,6 +1690,7 @@ int64_t esp_http_client_fetch_headers(esp_http_client_handle_t client) return ESP_FAIL; } http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len); + HTTP_PARSER_RETURN_ON_ERROR(TAG, client->parser, ESP_FAIL); } client->state = HTTP_STATE_RES_ON_DATA_START; ESP_LOGD(TAG, "content_length = %"PRId64, client->response->content_length); diff --git a/components/esp_http_server/src/httpd_parse.c b/components/esp_http_server/src/httpd_parse.c index 0f806707230..88d2d88d908 100644 --- a/components/esp_http_server/src/httpd_parse.c +++ b/components/esp_http_server/src/httpd_parse.c @@ -649,6 +649,12 @@ static int parse_block(http_parser *parser, size_t offset, size_t length) ESP_LOGW(TAG, LOG_FMT("incomplete (%"NEWLIB_NANO_COMPAT_FORMAT"/%"NEWLIB_NANO_COMPAT_FORMAT") with parser error = %d"), NEWLIB_NANO_COMPAT_CAST(nparsed), NEWLIB_NANO_COMPAT_CAST(length), parser->http_errno); return -1; + } else if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + /* http_parser error */ + data->error = HTTPD_400_BAD_REQUEST; + data->status = PARSING_FAILED; + ESP_LOGE(TAG, LOG_FMT("parser error: %s"), http_errno_description(HTTP_PARSER_ERRNO(parser))); + return -1; } /* Return with the total length of the request packet diff --git a/components/http_parser/http_parser.h b/components/http_parser/http_parser.h index 105ae510a8a..0fe11d5a24e 100644 --- a/components/http_parser/http_parser.h +++ b/components/http_parser/http_parser.h @@ -53,7 +53,7 @@ typedef unsigned __int64 uint64_t; # define HTTP_PARSER_STRICT 1 #endif -/* Maximium header size allowed. If the macro is not defined +/* Maximum header size allowed. If the macro is not defined * before including this header then the default is used. To * change the maximum header size, define the macro in the build * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove @@ -78,7 +78,7 @@ typedef struct http_parser_settings http_parser_settings; * chunked' headers that indicate the presence of a body. * * Returning `2` from on_headers_complete will tell parser that it should not - * expect neither a body nor any futher responses on this connection. This is + * expect neither a body nor any further responses on this connection. This is * useful for handling responses to a CONNECT request which may not contain * `Upgrade` or `Connection: upgrade` headers. *