From 25c0c9da24305a0670c3c8fb12e34151b2976b26 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Fri, 27 Mar 2026 13:25:43 +0800 Subject: [PATCH 1/2] fix(esp_tls): check tls connection finished before read/write operation --- components/esp-tls/esp_tls.c | 22 ++++++++++++++++++++-- components/esp-tls/esp_tls.h | 11 +++++++++-- components/esp-tls/esp_tls_mbedtls.c | 1 + 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index f18484067c..1ca30e44dd 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -145,6 +145,15 @@ ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen) if (!tls) { return -1; } + if (!tls->read) { + return -1; + } +#if CONFIG_MBEDTLS_DYNAMIC_BUFFER + if (tls->is_tls && tls->conn_state != ESP_TLS_DONE) { + ESP_LOGE(TAG, "TLS handshake has not completed, read operation not permitted"); + return -1; + } +#endif return tls->read(tls, (char *)data, datalen); } @@ -153,6 +162,15 @@ ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen) if (!tls || !data) { return -1; } + if (!tls->write) { + return -1; + } +#if CONFIG_MBEDTLS_DYNAMIC_BUFFER + if (tls->is_tls && tls->conn_state != ESP_TLS_DONE) { + ESP_LOGE(TAG, "TLS handshake has not completed, write operation not permitted"); + return -1; + } +#endif return tls->write(tls, (char *)data, datalen); } @@ -574,12 +592,12 @@ int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp } else if (ret == -1) { ESP_LOGE(TAG, "Failed to open new connection"); return -1; - } else if (ret == 0 && cfg->timeout_ms >= 0) { + } else if (ret == 0 && cfg->timeout_ms > 0) { uint64_t elapsed_time_us = esp_tls_get_platform_time() - start_time_us; if ((elapsed_time_us / 1000) >= cfg->timeout_ms) { ESP_LOGW(TAG, "Failed to open new connection in specified timeout"); ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_ESP, ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT); - return 0; + return -1; } } } diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index 1b3af1a6c0..324007126d 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -420,10 +420,17 @@ esp_tls_t *esp_tls_init(void); * structure should be zero-initialized * @param[in] tls Pointer to esp-tls as esp-tls handle. * + * @note The cfg->timeout_ms parameter controls the connection timeout: + * - timeout_ms > 0: The connection attempt will be aborted if it does not + * complete within the specified duration. + * - timeout_ms <= 0: No application-level timeout is applied. The connection + * relies on the underlying socket timeout (ESP_TLS_DEFAULT_CONN_TIMEOUT). + * On timeout, the function returns -1 and records + * ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT in the error handle. + * * @return - * - -1 If connection establishment fails. + * - -1 If connection establishment fails (including timeout). * - 1 If connection establishment is successful. - * - 0 If connection state is in progress. */ int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls); diff --git a/components/esp-tls/esp_tls_mbedtls.c b/components/esp-tls/esp_tls_mbedtls.c index 4e13617ee8..deac408c8c 100644 --- a/components/esp-tls/esp_tls_mbedtls.c +++ b/components/esp-tls/esp_tls_mbedtls.c @@ -1196,6 +1196,7 @@ int esp_mbedtls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp return ESP_ERR_ESP_TLS_SERVER_HANDSHAKE_TIMEOUT; } } + tls->conn_state = ESP_TLS_DONE; return ret; } From f3238ec7d51a4421836ab5e6b6a9afafc369e1cb Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Fri, 27 Mar 2026 13:36:36 +0800 Subject: [PATCH 2/2] feat(esp_tls): extends esp-tls test apps --- .../esp-tls/test_apps/.build-test-rules.yml | 6 +++ components/esp-tls/test_apps/README.md | 4 +- .../esp-tls/test_apps/main/CMakeLists.txt | 4 ++ .../esp-tls/test_apps/main/test_esp_tls.c | 41 +++++++++++++++++++ .../esp-tls/test_apps/pytest_esp_tls.py | 12 ++++++ .../esp-tls/test_apps/sdkconfig.ci.default | 0 6 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 components/esp-tls/test_apps/.build-test-rules.yml create mode 100644 components/esp-tls/test_apps/pytest_esp_tls.py create mode 100644 components/esp-tls/test_apps/sdkconfig.ci.default diff --git a/components/esp-tls/test_apps/.build-test-rules.yml b/components/esp-tls/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..a453bcfcde --- /dev/null +++ b/components/esp-tls/test_apps/.build-test-rules.yml @@ -0,0 +1,6 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/esp-tls/test_apps: + disable: + - if: IDF_TARGET not in ["esp32c3"] + reason: Testing on one target is enough diff --git a/components/esp-tls/test_apps/README.md b/components/esp-tls/test_apps/README.md index d76348d52c..67844e9673 100644 --- a/components/esp-tls/test_apps/README.md +++ b/components/esp-tls/test_apps/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | +| Supported Targets | ESP32-C3 | +| ----------------- | -------- | diff --git a/components/esp-tls/test_apps/main/CMakeLists.txt b/components/esp-tls/test_apps/main/CMakeLists.txt index d57cfd5249..1449e3dbec 100644 --- a/components/esp-tls/test_apps/main/CMakeLists.txt +++ b/components/esp-tls/test_apps/main/CMakeLists.txt @@ -1,3 +1,7 @@ idf_component_register(SRC_DIRS "." PRIV_REQUIRES test_utils esp-tls unity WHOLE_ARCHIVE) + +# Expose esp-tls private headers for internal component tests +idf_component_get_property(esp_tls_dir esp-tls COMPONENT_DIR) +target_include_directories(${COMPONENT_LIB} PRIVATE "${esp_tls_dir}/private_include") diff --git a/components/esp-tls/test_apps/main/test_esp_tls.c b/components/esp-tls/test_apps/main/test_esp_tls.c index d3175a3063..2411aa4a77 100644 --- a/components/esp-tls/test_apps/main/test_esp_tls.c +++ b/components/esp-tls/test_apps/main/test_esp_tls.c @@ -7,6 +7,7 @@ #include "memory_checks.h" #include "esp_tls.h" #include "esp_tls_custom_stack.h" +#include "esp_tls_private.h" #include "unity.h" #include "esp_err.h" #include "esp_log.h" @@ -60,6 +61,46 @@ const char *test_key_pem = "-----BEGIN PRIVATE KEY-----\n"\ "Aogx44Fozd1t2hYcozPuZD4s\n"\ "-----END PRIVATE KEY-----\n"; +static ssize_t dummy_read(esp_tls_t *tls, char *data, size_t datalen) { return (ssize_t)datalen; } + +TEST_CASE("esp_tls_conn_write/read reject NULL tls", "[esp-tls]") +{ + char buf[16] = {0}; + TEST_ASSERT_EQUAL(-1, esp_tls_conn_write(NULL, buf, sizeof(buf))); + TEST_ASSERT_EQUAL(-1, esp_tls_conn_read(NULL, buf, sizeof(buf))); +} + +TEST_CASE("esp_tls_conn_write reject NULL data buffer", "[esp-tls]") +{ + esp_tls_t *tls = esp_tls_init(); + TEST_ASSERT_NOT_NULL(tls); + TEST_ASSERT_EQUAL(-1, esp_tls_conn_write(tls, NULL, 16)); + esp_tls_conn_destroy(tls); +} + +TEST_CASE("esp_tls_conn_read accepts zero datalen", "[esp-tls]") +{ + esp_tls_t *tls = esp_tls_init(); + TEST_ASSERT_NOT_NULL(tls); + tls->is_tls = true; + tls->read = dummy_read; + TEST_ASSERT_EQUAL(ESP_OK, esp_tls_set_conn_state(tls, ESP_TLS_DONE)); + /* datalen=0 on read is used in some projects, to get the data in the SSL buffers */ + TEST_ASSERT_EQUAL(0, esp_tls_conn_read(tls, NULL, 0)); + esp_tls_conn_destroy(tls); +} + +TEST_CASE("esp_tls_conn_write/read reject unconnected tls", "[esp-tls]") +{ + esp_tls_t *tls = esp_tls_init(); + TEST_ASSERT_NOT_NULL(tls); + /* read/write function pointers are NULL right after init */ + char buf[16] = {0}; + TEST_ASSERT_EQUAL(-1, esp_tls_conn_write(tls, buf, sizeof(buf))); + TEST_ASSERT_EQUAL(-1, esp_tls_conn_read(tls, buf, sizeof(buf))); + esp_tls_conn_destroy(tls); +} + #if CONFIG_ESP_TLS_USING_MBEDTLS TEST_CASE("esp-tls init deinit", "[esp-tls]") { diff --git a/components/esp-tls/test_apps/pytest_esp_tls.py b/components/esp-tls/test_apps/pytest_esp_tls.py new file mode 100644 index 0000000000..de7f6e1d34 --- /dev/null +++ b/components/esp-tls/test_apps/pytest_esp_tls.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('config', ['default'], indirect=['config']) +@idf_parametrize('target', ['esp32c3'], indirect=['target']) +def test_esp_tls(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp-tls/test_apps/sdkconfig.ci.default b/components/esp-tls/test_apps/sdkconfig.ci.default new file mode 100644 index 0000000000..e69de29bb2