Skip to content

Fix resource leak and unchecked mbedTLS errors in ml_derp_connect() - #37

Open
antonmeyer wants to merge 1 commit into
CamM2325:mainfrom
antonmeyer:fix/ml-derp-connect-resource-leak
Open

Fix resource leak and unchecked mbedTLS errors in ml_derp_connect()#37
antonmeyer wants to merge 1 commit into
CamM2325:mainfrom
antonmeyer:fix/ml-derp-connect-resource-leak

Conversation

@antonmeyer

Copy link
Copy Markdown

Summary

Two related bugs found in ml_derp_connect() (components/microlink/src/ml_derp.c) while investigating an internal-SRAM exhaustion incident on real ESP32-S3 hardware running this component (mothership role, alongside esp_http_server/an MQTT broker):

  1. Unchecked mbedTLS setup return values. mbedtls_ctr_drbg_seed(), mbedtls_ssl_config_defaults(), and mbedtls_ssl_setup()'s return values were never checked. A failure in any of them — plausible under low memory, since these are exactly the calls that allocate — let the code continue straight into mbedtls_ssl_handshake() with a partially/incorrectly initialized SSL context. On our hardware this surfaced as a confusing, misleading MBEDTLS_ERR_SSL_BAD_INPUT_DATA ("Bad input parameters to function") repeating on every retry, rather than the real, earlier failure being logged.

  2. Resource leak on every failed connect attempt. Every failure path after mbedtls_ssl_init()/etc. (TLS handshake failure, HTTP-upgrade write failure, upgrade-response timeout/read-failure/rejection) only ever called ml_close_sock() on the raw socket — never mbedtls_ssl_free()/mbedtls_ssl_config_free()/mbedtls_ctr_drbg_free()/mbedtls_entropy_free(). Those four are only freed by ml_derp_disconnect(), which runs solely on an established connection later dropping — a connect attempt that never reached ML_EVT_DERP_CONNECTED leaked all four structures, permanently, every single time. On a link unstable enough to fail DERP connects repeatedly (which is what we were independently chasing down on our end), this leaks on every retry — a direct, mechanical path from "network is flaky" to slow memory exhaustion.

Fix

Refactored to a single goto fail cleanup path, matching the pattern mbedTLS's own reference example (programs/ssl/ssl_client1.c) uses for exactly this kind of setup. Every failure past mbedtls_ssl_init() now frees all four structures before returning, and the three previously-unchecked setup calls now check their return value and log the real mbedTLS error string instead of silently continuing.

Testing

Verified building clean as part of a full ESP-IDF application (esp32s3 target) using this component. Not independently reproduced as a standalone minimal repro — found via production hardware showing internal-SRAM exhaustion (heap_caps_get_minimum_free_size dropping to a few KB) correlated with these exact error signatures in the field, on a device with an already-unstable DERP/DISCO link to one peer.

Two related bugs in the DERP TLS connection setup:

1. mbedtls_ctr_drbg_seed()/mbedtls_ssl_config_defaults()/mbedtls_ssl_setup()'s
   return values were never checked. A failure in any of them - plausible
   under low memory, since these are exactly the calls that allocate - let
   the code continue straight into mbedtls_ssl_handshake() with a
   partially/incorrectly initialized SSL context, producing a confusing
   downstream MBEDTLS_ERR_SSL_BAD_INPUT_DATA ("Bad input parameters to
   function") instead of surfacing the real, earlier failure.

2. Every failure path after mbedtls_ssl_init()/etc. (TLS handshake
   failure, HTTP-upgrade write failure, upgrade-response timeout/read-
   failure/rejection) only ever called ml_close_sock() on the raw socket -
   never mbedtls_ssl_free()/mbedtls_ssl_config_free()/mbedtls_ctr_drbg_free()/
   mbedtls_entropy_free(). Those four are only freed by
   ml_derp_disconnect(), which runs solely on an *established* connection
   later dropping - a connect attempt that never reached
   ML_EVT_DERP_CONNECTED leaked all four, permanently, every single time.
   On a link unstable enough to fail DERP connects repeatedly, this leaks
   on every retry - a direct, mechanical path from "network is flaky" to
   slow memory exhaustion, found via exactly that real-world scenario.

Fixed by refactoring to a single `goto fail` cleanup path, matching the
pattern mbedTLS's own reference example (programs/ssl/ssl_client1.c)
uses: every failure past mbedtls_ssl_init() now frees all four
structures before returning, and the three previously-unchecked setup
calls now check their return value and log the real mbedTLS error
string instead of silently continuing.

Verified building clean as part of a full ESP-IDF app (esp32s3 target)
using this component; not independently reproduced as a standalone
crash here, found via production hardware showing internal-SRAM
exhaustion correlated with these exact error signatures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant