- Add fail-closed wolfSSL small-math policy and vectors - Backport DHCP, EMS, and X.509 allocation fixes - Extend source override validation and operational documentation
23 KiB
Focused installed ESP-IDF security applicability review
Implementation addendum — 2026-09-15
Implemented, with host/build evidence; not target validation or a completed advisory review. Source checked against tools/security_overrides.py, cmake/security_overrides.cmake and the existing generated copies:
- CVE-2026-45160: pinned DHCP backport handles PAD/END before length access, validates remaining payload length and minimum type/IP lengths, and advances by the validated length. Remaining-length comparisons deliberately avoid forming out-of-object pointers.
- CVE-2026-50581: TLS 1.2 EMS
calc_verifyfailure now returnsretbefore derivation. This is the TLS 1.2 hunk only, not the upstream TLS 1.3 correction or a global mbedTLS update. - CVE-2026-34874: X.509 OID allocation failure now returns
MBEDTLS_ERR_X509_ALLOC_FAILEDbeforememcpy(). - The registry now has seven entries: the four historical entries below plus
dhcpserveronlwip,mbedtls_ssl_tlson nested targetmbedtls, andmbedtls_x509_createon nested targetmbedx509. Nested-target selection validates the allowlist and owning SDK library directory, rejects imported/alias targets, and retains the exactly-one-source/property-preservation checks. All seven generated copies were read-only verified against rendered pinned inputs; each carries the project modification notice dated 2026-09-15, retaining upstream notices. - Still unpatched: CVE-2026-45541 WS negotiation and CVE-2025-48965 ASN.1 named-data state. Their baseline applicability qualifications below remain; the three backports do not fix them.
Supplied parent evidence: pio run PASS, 94,340 B linked RAM / 1,767,217 B flash (unchanged RAM, −64,092 B flash from the 9C 1,831,309 B baseline; includes the wolf crypto policy). Parent reports all four focused commands in hardening validation passed; independent reviewer reports the SDK-override and wolf-crypto-policy suites passed with no blocking implementation defects. These commands were not rerun for this documentation update. No target evidence, dependency upgrade, asset regeneration or device operation is claimed. Broader advisory review and whole-phase acceptance remain open.
Historical pre-mitigation research baseline — 2026-09-15
The remainder preserves the original investigation, hashes, applicability and proposed tests. Statements such as “not implemented,” “current,” four entries and original compilation inputs describe that earlier snapshot, not the implementation above.
Review date: 2026-09-15. Scope: the installed /home/mscholz/.platformio/packages/framework-espidf, current application source, and checked-in/generated security overrides. Review and recommendations only; no fixes implemented. No build, test compilation, upload, monitor, erase, or device operations were performed.
This is not a complete IDF/mbedTLS advisory inventory, certification, or statement that an old version is safe. Advisory release tables are not sufficient to identify downstream backports: the installed implementation and actual source registration were inspected.
Decision summary
| Issue | Confirmed local finding | Application applicability | Recommended priority |
|---|---|---|---|
| CVE-2026-45160, DHCP option parsing | Original vulnerable parser compiled; no override | Exposed whenever the project SoftAP/DHCP server is active; adjacent associated client, no application login required | First correction: backport the release/v5.5 bounds fix |
| CVE-2026-45541, WS subprotocol parsing | Vulnerable SDK function compiled; no override | Faulty tokenization loop not reached by current application upgrades, which pass NULL; both routes are ordinary HTTP handlers |
Defense-in-depth backport; preserve current admission-before-101 design |
| CVE-2026-50581, ignored EMS error | Missing early return in installed ssl_compute_master() |
TLS 1.2/EMS path used by HTTPS; hash/allocation failure prerequisite. Cipher restriction and disabled renegotiation do not remove this path | Prompt fail-closed correction alongside DHCP/X.509 work |
| CVE-2025-48965, ASN.1 named-data state | Zero-length replacement clears pointer but leaves stale length | Current certificate names are trusted, single nonempty CNs; malicious repeated-OID/empty-value trigger not provided by current name generation | Lower-priority library correction/regression coverage |
| CVE-2026-34874, X.509 name allocation failure (3.6.6 fix) | Unchecked allocation immediately followed by memcpy() |
Used by local certificate creation, even with the safe fixed CN; OOM can trigger it | Prompt correction; fixed input is not a mitigation |
Important correction to issue classification: CVE-2025-48965 is not the X.509 allocation-failure bug. Its official advisory describes a stale val.len after a zero-length replacement. CVE-2026-34874 is the separate unchecked allocation fixed in 3.6.6. Do not merge their applicability or mark either fixed by the project's HTTPD scratch allocator changes.
Exact reviewed baseline and registration
Installed components/esp_common/include/esp_idf_version.h defines 5.5.0. components/mbedtls/mbedtls/include/mbedtls/build_info.h defines 3.6.3, 0x03060300. The following SHA-256 values were calculated from installed bytes, not inferred from version labels. Paths are relative to the installed IDF root:
| Installed source | SHA-256 |
|---|---|
components/esp_common/include/esp_idf_version.h |
30fdfbed6cc0055a89f3454f61b56d5e5643bbc46c0774aae63e41869cf2947a |
components/lwip/apps/dhcpserver/dhcpserver.c |
953f46189bc64680ea5fa761e75511fadb3aebf698a0d9dff251d77166d78b80 |
components/esp_http_server/src/httpd_ws.c |
e514f603a696e3e775573bc8882a350691e31fcae96e435f9062248e7c91677c |
components/mbedtls/mbedtls/library/ssl_tls.c |
b726c0c55bc5f32255f129d55f9f2fface85ce83de90a2d16c9017b93b738bff |
components/mbedtls/mbedtls/library/asn1write.c |
6667aebb1e5f0500ef4a281fe2752fc7bc59610e7bfc01d2916d224e5bc5c3e3 |
components/mbedtls/mbedtls/library/x509_create.c |
fd399239aee30384786a19b47bfe5dd22b979d5d89bb38f29f0c82a3d81daaf7 |
Checked-in edits are part of the effective source
Root CMakeLists.txt:15–16 includes cmake/security_overrides.cmake after project(). That file invokes tools/security_overrides.py, loads its manifest, and replaces exactly one source on the resolved component target, preserving source properties and original quoted-include directory. Version/source/hash/edit ambiguity fails configuration.
The registry currently has exactly four entries:
| Entry | Component | Protected original SHA-256 |
|---|---|---|
wolfssh_internal |
wolfssl__wolfssh |
81ff1f9166708abd5c2911e9fe57c0aee01c88b5d3f68c909ee8a856d37f36a9 |
https_server |
esp_https_server |
6481942b62e51125e2a43441fa0900cbda74bd2ea05c82f0c29eb4933c31946e |
httpd_parse |
esp_http_server |
6bba77064aaa68a06f8d4c01432064a1b050c91ed22741c547785b0d8a6c07d8 |
esp_tls_mbedtls |
esp-tls |
09210c5a601647ca5775d127a2951bab2f3e509192b53487bbea8a93d8731b78 |
Read-only Python inspection called verify_version() and render_entry() in memory (not generate()), verified all four original pins, and compared rendered bytes with the existing generated files: all four matched exactly. This did not rewrite installed or generated files.
The existing .pio/build/esp32-s3-devkitc-1-n16r8/security_overrides/manifest.cmake and narrowly filtered compile_commands.json register those four generated sources, but register original dhcpserver.c, httpd_ws.c, ssl_tls.c, asn1write.c, and x509_create.c. Thus HTTPS cleanup, HTTPD scratch wiping/null handling, TLS policy, and wolfSSH fixes do not constitute fixes for these five findings.
This confirms existing configured compilation inputs, not freshness of the executable, successful rebuilding, or the firmware installed on a device.
mbedTLS registration constraint
Installed components/mbedtls/CMakeLists.txt imports upstream nested targets. The existing compile database's object paths confirm:
ssl_tls.c→ targetmbedtls(.../CMakeFiles/mbedtls.dir/ssl_tls.c.obj).x509_create.c→ targetmbedx509.asn1write.c→ targetmbedcrypto.
The current replacement helper resolves an IDF component's COMPONENT_LIB; it does not resolve these nested library targets. Simply adding Entry(..., "mbedtls", ...) would select the component wrapper, not the actual owning target, and should fail its exactly-one-source check. A future patch must minimally add an explicit, validated target-selection mechanism for these three nested targets while retaining current component resolution for existing entries. Do not weaken the match check or add duplicate source compilation to the wrapper.
CVE-2026-45160 — DHCP server parse_options()
Official GHSA-g764-gwc3-75m5 and fetched release/v5.5 patch.
Exact upstream fix commit: d51b1076092487e533eadf8b48c9c8579d3a6712.
Confirmed installed dhcpserver.c:902–952:
- Loop only checks
optptr < end. - Message type reads
optptr[2]without validating a length byte or payload. - Requested IP compares four bytes without a declared/remaining-length check.
- Advancement reads
optptr[1]without ensuring it exists. - PAD is not treated as a one-byte option.
The GHSA lists later affected point releases and a planned 5.5.5 fix; that is not evidence excluding 5.5.0. The old implementation is directly present here.
Reachability
src/wifi_manager.c:create_default_wifi_netifs() explicitly builds ESP_NETIF_DEFAULT_WIFI_AP() and installs default AP handlers. Installed esp_netif_defaults.h:59–61 sets ESP_NETIF_DHCP_SERVER | ESP_NETIF_FLAG_AUTOUP. Existing generated configuration has CONFIG_LWIP_DHCPS=1. start_radio_and_policy() selects AP/APSTA when policy requires it; set_runtime_ap_enabled() also permits STA→APSTA activation.
This is therefore an applicable exposure when SoftAP is active, not a hypothetical unused SDK feature. A station-only moment is not a permanent mitigation if fallback or administrative policy can enable AP later. The official impact is adjacent-network denial of service, not demonstrated heap disclosure: the advisory says overread bytes are not echoed. WPA association controls reduce access but do not fix the parser. No live attack or actual device AP state was tested.
Minimal recommended edits (not implemented)
Add a pinned idf entry for components/lwip/apps/dhcpserver/dhcpserver.c, component lwip, using the exact original hash above. Backport the official patch's PAD definition and parser changes:
- Skip PAD; terminate at END before reading a length.
- Require a length byte and the entire declared payload to fit.
- Require message-type payload length ≥1 and requested-IP length ≥4.
- Advance using the already-validated
opt_len; remove the obsolete end flag. - Retain notices and existing post-loop state behavior.
For a strict-C bounds implementation, consider remaining-length comparisons (end - optptr >= 2, then opt_len <= remaining - 2) rather than constructing a pointer beyond the object in the upstream optptr + 2 + opt_len > end check. If chosen, document this small deviation and test equivalence; this review did not apply or test either form.
Future tests: empty options, lone option code, missing length, oversized payload, PAD sequences, END at boundary, short type/IP fields, valid DISCOVER/REQUEST/RELEASE and option combinations. Verify actual generated lwip compilation input, no duplicate original, and no changes to AP policy, broker, UART0, or USB recovery.
CVE-2026-45541 — WS subprotocol negotiation
Official GHSA-3j8v-xgrq-5vg8 and fetched release/v5.5 patch.
Exact upstream fix commit: 00a2f7fbbbd8fe6d04729022e1d5c9a49435bfe8.
Installed httpd_ws.c:70–95 has strtok_r() followed by a do loop that dereferences its possibly NULL result. It also compares using sizeof(subprotocol), the pointer size rather than protocol length. A nonempty delimiter-only header can yield no tokens.
Current route registration avoids the defective loop
- Existing configuration has
CONFIG_HTTPD_WS_SUPPORT=1; disabling WS at compile time is not the reason for non-reachability. src/web_server.c:518–539defines serial and admin upgrade routes with.is_websocket = false; omittedsupported_subprotocolmembers of these static initializers are NULL.start_server()actually registers the serial handler array and optional admin route.src/web_serial_transport.c:745andsrc/web_admin_transport.c:570useweb_httpd_upgrade()after admission.src/web_httpd_adapter.c:174callshttpd_ws_respond_server_handshake(request, NULL).- Installed
httpd_ws_get_response_subprotocol()returns at itssupported_subprotocol == NULLcheck before tokenization. The optional registration adapter also rejectsis_websocketorsupported_subprotocol.
Conclusion: vulnerable library bytes are present, but this CVE's NULL-token dereference is not reachable through the current application upgrade paths. The official generic warning about pre-application-authentication crashes applies to automatic SDK WS negotiation with a non-NULL supported protocol; it should not be copied onto these manual-admission routes without this qualification. TLS alone is not a general mitigation.
Minimal recommended edits (not implemented)
Add a separately pinned httpd_ws.c entry for component esp_http_server; do not conflate it with the existing httpd_parse.c entry. Backport the upstream while (s != NULL) loop, exact-length match and explicit next-token step, retaining notices. Do not enable automatic upgrades or add a subprotocol to justify the patch.
Future tests: empty/delimiter-only headers, NULL supported protocol, unsupported protocol, exact match and prefix mismatch, plus current cookie/Origin/ticket admission-before-101 behavior. Mark it as a defense-in-depth fix, not closure of a demonstrated unauthenticated application crash.
Bounded mbedTLS 3.6.3 review
CVE-2026-50581 — EMS calculation failure ignored
Official advisory, published 2026-07-07. Affects through 3.6.6; first fixed 3.6.x release is 3.6.7, not 3.6.6.
Fetched basic fix: f595df4569c1a1650ad9d077e2f2e819e9f1dddb. Advisory gives test/documentation range 338572c1d805a31b875a448536bcb50d72f9bc40..27065ceb643a4266888ba1f200e4f20845e801cf; that range was not reviewed here.
Confirmed installed ssl_tls.c:7041–7085: session_hash[48], initial seed_len=64, and handshake->calc_verify() error only logged; execution continues. The override edits esp-tls/esp_tls_mbedtls.c, not this file. The server's two ECDHE-ECDSA AES-GCM suites use SHA-256/SHA-384 and TLS 1.2; EMS remains relevant even without renegotiation. Installed esp_config.h:1094–1097 enables EMS when CONFIG_MBEDTLS_TLS_ENABLED, which is 1 in the existing generated header.
A calculation/allocation failure can leave an uninitialized hash and potentially retain length 64, causing a 16-byte buffer overread. Per upstream, this does not directly disclose stack contents or the master secret; a correctly functioning peer rejects the incorrect Finished message. Higher-level master-secret uniqueness attacks require additional conditions, including both peers affected and induced failures. No direct HTTPS login bypass, practical remote fault trigger, or device exploit was established. Low historical memory minima justify fault testing, not an assertion that this failure occurred.
Minimal recommendation: pinned ssl_tls.c replacement on actual target mbedtls, inserting return ret; immediately after the existing MBEDTLS_SSL_DEBUG_RET(1, "calc_verify", ret) inside the error branch. Preserve EMS; do not disable it as a shortcut. The fetched commit also changes ssl_tls13_server.c for a separate resumption-secret error: do not claim the one-line TLS 1.2 backport implements that second fix or globally updates mbedTLS. Current HTTPS policy is TLS 1.2 only.
Future tests should inject calc_verify failure for both SHA paths, ensure PRF/key derivation and successful handshake progression do not occur, preserve success behavior, and exercise cleanup. Exact driver/backend failure inducibility on the target remains unresolved; no preprocessing/cryptographic or fault-injection test was run.
CVE-2025-48965 — ASN.1 named-data inconsistency, not OOM
Official advisory and 3.6.4 release notes. Affects through 3.6.3; fixed in 3.6.4.
Installed asn1write.c:374–435 checks allocation failures when creating a node/OID/value and preserves old data until replacement allocation succeeds. However, the val_len == 0 branch frees cur->val.p and sets it NULL without resetting cur->val.len. Reusing the same OID or writing the inconsistent list can dereference NULL. Official examples include DC=foo,DC=#0000 and DC=foo,DC=#0000,DC=bar.
src/web_security.c:92–111,128–257 builds CN=ESP32 SAK <six hex digits> from the MAC, initializes a fresh writer, and sets subject and issuer separately. There is no repeated OID or empty value in either name. Thus the faulty primitive exists and is indirectly called, but the official hostile-name trigger is not supplied by this certificate-generation path. Do not say “not applicable because the project never creates certificates”; it does. Do not treat separate subject/issuer lists as repeated attributes in one list.
Minimal recommendation: ensure cur->val.len = 0 when clearing the value, on actual target mbedcrypto, with a pin on the installed file. Retain the existing preserve-old-on-allocation-failure logic. The official advisory/release was fetched, but an exact upstream CVE-2025-48965 fix commit was not resolved or patch-verified in this bounded review; obtain and compare the specific 3.6.4 change before implementing/pinning a claimed official backport. Test nonempty→empty→same-length replacement, list serialization after empty replacement, repeated OIDs, and allocation failures independently.
CVE-2026-34874 — x509_string_to_names() allocation failure
Official advisory and 3.6.6 release notes. Affects 3.5.0–3.6.5; fixed in 3.6.6.
Fetched basic fix: bfaf4a47fd33da860796feaba6235847acb71127. The advisory identifies tests/documentation through 4704b6b4bd963f1331582374e881184addf8f523; those additional changes were not reviewed here.
Installed x509_create.c:306–310 allocates the known attribute's OID and calls memcpy(oid.p, ...) without checking NULL. The project's CN takes exactly this recognized-attribute branch through both name setters. Trusted names avoid the previous CVE, not this OOM defect. Application return-code checks cannot catch a fault that happens inside the setter before it returns.
Minimal recommendation: pinned x509_create.c replacement on actual target mbedx509; directly after oid.p = mbedtls_calloc(1, oid.len), add the official three-line NULL guard returning MBEDTLS_ERR_X509_ALLOC_FAILED. Preserve existing application cleanup and fail-closed identity publication. No new buffer or policy change is required.
The upstream advisory discusses arbitrary code execution on microcontrollers where address zero is writable. That impact was not demonstrated for this ESP32-S3 memory map; confirmed here is the unchecked write path and potential failure during certificate generation, not practical code execution. Test OID allocation failure at each setter, clean writer destruction, no persistence/publication of incomplete material, preservation of the old identity on failed rotation, and successful retry.
Actionable next correction and remaining evidence
- Implement the pinned DHCP parser backport first. It is the clearest currently reachable unauthenticated network-input defect in this scope. Do not merely change advisory wording or a version label.
- In the same security correction effort, support explicit nested-target overrides and backport the EMS early return and X.509 allocation guard. Verify each protected source appears exactly once on its real owner target; retain all existing four corrections and their source properties.
- Backport WS defensively and add registration/NULL-subprotocol regression coverage without changing application admission. Resolve the exact ASN.1 3.6.4 patch before adding that lower-priority correction.
- Before acceptance, run focused parser/allocation/error-propagation tests against the generated compilation inputs, then a firmware build and target checks under separately authorized implementation/validation work. None were performed for this review. Ensure failures cannot compromise UART0 recovery, native USB independence, broker ownership, or bounded transport isolation.
- A full IDF upgrade is an alternative maintenance strategy, but must rebase/re-audit all existing source pins, private HTTPD contracts, and TLS behavior. Updating only to mbedTLS 3.6.6 would leave CVE-2026-50581 unresolved. This document does not certify any upgrade candidate against the full advisory set.
Unresolved boundaries: exact ASN.1 fix commit; target-specific OOM/hash failure reproducibility and impact; actual running firmware/configuration; complete mbedTLS/IDF advisory coverage and downstream release integration. Existing source registration and generated-byte equality are confirmed; patch application, tests, linking, and device behavior are not.
Review mechanics: project code-map/current-state read first; targeted source, configuration and compilation-input inspection; official advisories and four exact fix patches fetched. No large release-history index was used. Some GitHub API requests timed out/returned no results and direct ReadTheDocs access via Python returned HTTP 403; official raw advisory sources and individual release pages supplied the evidence above. One read-only inspection command hit its 10-second timeout; a linear-time replacement completed the inspection. No source modifications were made, and this review owns only this document.