← ALL ADVISORIES

HIGH BYTERAY-2026-0220Patched

wolfSSL — Heap Out-of-Bounds Write in Silicon Labs ECC Import

On the Silicon Labs Secure Element port, wolfSSL sized an ECC import from the encoded point and selected the curve separately by identifier. A 133-byte point on a P-256 session wrote past the 96-byte key_raw field during certificate processing, before the handshake was authenticated.

Advisory
BYTERAY-2026-0220
CWE
CWE-122
Vendor
wolfSSL
Product
wolfSSL
Reported
2026-09-20
Disclosure
2026-10-12
Affected
wolfSSL 5.0.1 through 5.9.4, non-Vault builds with WOLFSSL_SILABS_SE_ACCEL
Fixed in
Master commit c46e620c2a70677da7694cf06d241444c6cfe6bb (PR 11530, merged 2026-09-28). Not in the v5.9.4-stable tag.
Credit
Mehrun P. Hunter and Shahriyar Jalayeri, ByteRay Ltd.

Executive Summary

The Silicon Labs port stores an ECC key in ecc_key.key_raw, a fixed field. On non-Vault parts that field is 96 bytes, three 32-byte P-256 values. The X9.63 import does not use 32. It halves the length of the encoded point and passes that width to silabs_ecc_import(), while wc_ecc_set_curve() picks the curve from the curve identifier alone.

A 133-byte uncompressed point is 04 || X[66] || Y[66]. The import keeps P-256 and then copies 66-byte coordinates into the 96-byte field. On wolfSSL 5.9.2 the public import writes X and Y, 132 bytes into a 96-byte field, 36 bytes past the end. On wolfSSL 5.0.1 the same function also writes the scalar, 198 bytes in total, 102 bytes past the end.

The copy happens while the peer certificate or key share is being parsed. The on-curve check, where a build has one, runs after the write. Default builds do not compile this port.

Affected Products and Versions

  • Product: wolfSSL / wolfCrypt, Silicon Labs Secure Element ECC port (WOLFSSL_SILABS_SE_ACCEL).
  • Heap overflow: non-Vault configuration, where ECC_MAX_CRYPTO_HW_SIZE is 32 and key_raw is 3 * 32 bytes. Vault parts size the same field for P-521 (3 * 66 bytes). This 133-byte point fits in that larger field. The missing comparison with the selected curve is still present there.
  • Tested: wolfSSL 5.0.1, and wolfSSL 5.9.2 (d95483fff72d6e987394b72edca433d4b580a0f4).
  • The v5.9.4-stable tag still has the unbounded import.
  • Fixed on master by c46e620c2a70677da7694cf06d241444c6cfe6bb, merged in PR 11530 on 2026-09-28, after the 5.9.4 release. No fixed release tag was available on 2026-10-12.

A default ./configure build does not include this code.

Technical Details

wc_ecc_import_x963_ex2() derives the width from the encoded point and then asks for the curve:

keysize = (int)(inLen >> 1);
err = wc_ecc_set_curve(key, keysize, curve_id);

wc_ecc_set_curve() rejects keysize above ECC_MAXSIZE (66). Below that, an explicit curve id is accepted without comparing keysize to dp->size. A P-256 import of a 133-byte point therefore continues with keysize == 66 and dp->size == 32.

On current master, before the fix, the Silicon Labs branch is:

#elif defined(WOLFSSL_SILABS_SE_ACCEL)
    if (err == MP_OKAY)
        err = silabs_ecc_import(key, keysize, 1, 0);

silabs_ecc_import() pointed the secure-element key descriptor at key->key_raw and exported each coordinate with wc_export_int(). For a public import the writes are:

key_raw + 0         X, keysize bytes
key_raw + keysize   Y, keysize bytes

With keysize 66, Y ends at offset 132. The field ends at offset 96. The private-key form of the same function adds a third write at key_raw + (2 * keysize). wolfSSL 5.0.1 took that path from wc_ecc_import_x963_ex() and wrote X, Y, and the scalar.

wc_ecc_check_key() is behind WOLFSSL_VALIDATE_ECC_IMPORT and sits after the Silicon Labs import, so it does not stop the copy.

The same sink is reached from private-key import with key->dp->size rather than the wire length. On a non-Vault build a real P-384 or P-521 key is already larger than the 32-byte stride the field was sized for. The upstream check rejects those too.

Reachability and Preconditions

  • The library is built with WOLFSSL_SILABS_SE_ACCEL, and the part is not a Vault configuration with the 66-byte hardware size.
  • The attacker is a TLS peer, or anyone else who can supply an ECC point to import (a certificate, a key share, or CMS).
  • No authentication has completed. In the certificate path the point is the subject public key of a certificate the peer presents.
  • ByteRay reproduced it with a two-certificate chain. The root is a normal P-256 CA. The intermediate is signed by that root and carries a 133-byte uncompressed point in its subject public key. The client trusts the root, verifies the intermediate's signature, and then decodes that public key.

The decode stack on the 5.0.1 proof of concept was:

DoCertificate
  ProcessPeerCerts
    ProcessPeerCertParse
      ParseCertRelative
        ConfirmSignature
          wc_EccPublicKeyDecode
            wc_ecc_import_x963_ex
              silabs_ecc_import
                wc_export_int

AddressSanitizer, on that build, reported a 66-byte write in wc_export_int, reached from silabs_ecc_import, at the end of the heap ecc_key allocated by ConfirmSignature. The same chain with a normal 65-byte P-256 point completed certificate verification and produced no sanitizer report.

The proof of concept ran on a Linux host. The Gecko SDK calls were stubbed: randomness from /dev/urandom, real SHA for the DRBG health tests, and a verify stub. The overflow itself is in wolfSSL's copy into key_raw, which runs before the hardware operation.

Impact

An unauthenticated heap out-of-bounds write during handshake parsing. On 5.9.2 the public import writes 36 bytes past key_raw. On 5.0.1 the three-part export writes 102 bytes past it. The bytes are the coordinates from the certificate or key share, so the peer chooses them.

A crash is the reliable result. Whether those bytes become code execution depends on the allocator on the device. This port is built for Silicon Labs parts, which are typical embedded targets.

Remediation and Mitigation

Take a master build that contains c46e620c2a70677da7694cf06d241444c6cfe6bb (PR 11530). silabs_ecc_import() now returns ECC_BAD_ARG_E (-170) before any write when keysize is 0, greater than ECC_MAX_CRYPTO_HW_SIZE, or different from key->dp->size. silabs_ecc_make_key() uses dp->size and applies the same capacity limit. silabs_ecc_export_public() rejects a curve that does not fit the field.

The 5.9.4 stable tag does not contain that commit. Until a release does, the workaround is to leave WOLFSSL_SILABS_SE_ACCEL off. That drops hardware ECC acceleration. It does not affect builds that were never compiled with the port.

Timeline

  • 2026-09-20 — Reported to wolfSSL, against wolfSSL 5.0.1, with the same missing check noted on current master.
  • 2026-09-23 — wolfSSL opened PR 11530. The pull request is where they describe the bug: the curve is taken from the curve id, and a caller-supplied key size that does not match it can write past the fixed hardware key field. The same day, ByteRay reviewed the import check against master d95483fff and confirmed to David Garske that it covers the report.
  • 2026-09-28 — PR 11530 merged to master.
  • 2026-10-09 — David Garske closed the wolfSSL ticket. He noted that the pull request had been merged.
  • 2026-10-12 — This advisory.

Credits

Discovered and reported by Mehrun P. Hunter and Shahriyar Jalayeri of ByteRay Ltd.

wolfSSL credited ByteRay Ltd. for reports from this review in PR 11606. The Silicon Labs fix itself is PR 11530.

References