java-topology/defects/wolfssl/patch/wolfssl-0001-alpn-find-match.md

132 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000576
# wolfssl-0001: CWE-407 O(C×S) ALPN selection in ALPN_find_match
## Severity: MEDIUM
## Location
`src/tls.c``ALPN_find_match()``TLSX_ALPN_Find()`
## Description
`ALPN_find_match()` is called by `ALPN_Select()` (server-side) and
`TLSX_ALPN_ParseAndSet()` (client-side response). It iterates over the
client-sent ALPN protocol list and for each client entry calls
`TLSX_ALPN_Find()` to search the server's configured ALPN linked list:
```c
/* outer: iterate every client-offered name (C entries) */
for (s = alpn_val; (s - alpn_val) < alpn_val_len; s += wlen) {
wlen = *s++;
/* inner: TLSX_ALPN_Find scans server linked list (S entries, O(S)) */
alpn = TLSX_ALPN_Find(list, (char*)s, wlen);
if (alpn != NULL) { ... break; }
}
```
`TLSX_ALPN_Find()` (line 1852):
```c
static ALPN* TLSX_ALPN_Find(ALPN *list, char *protocol_name, word16 size)
{
ALPN *alpn = list;
while (alpn != NULL && (
(word16)XSTRLEN(alpn->protocol_name) != size ||
XSTRNCMP(alpn->protocol_name, protocol_name, size))) /* O(L) */
alpn = alpn->next;
return alpn;
}
```
Total: O(C × S × L) per handshake, where L = average protocol name length.
The ALPN extension allows the client to send an arbitrary-length list. With
the extension_data length field allowing up to 65535 bytes, a client can send
~32767 single-byte protocol names. For each, `TLSX_ALPN_Find` scans the entire
server linked list (S entries).
## Complexity Before Fix
O(C × S × L) per handshake.
## Fix
Build a hash set of server-configured ALPN names before the outer loop.
Lookup changes from O(S × L) to O(L) expected. Total: O((C + S) × L).
```c
--- a/src/tls.c
+++ b/src/tls.c
@@ -1896,6 +1896,34 @@ static int ALPN_find_match(WOLFSSL *ssl, TLSX **pextension,
TLSX *extension;
ALPN *alpn, *list;
const byte *sel = NULL, *s;
+ /* Hash set of server-configured ALPN names: 32-slot open-addressing.
+ * Keys are (ptr into ALPN->protocol_name, len). */
+#define WOLFSSL_ALPN_HS 32
+ const char *hs_ptr[WOLFSSL_ALPN_HS];
+ word16 hs_len[WOLFSSL_ALPN_HS];
byte sel_len = 0, wlen;
extension = TLSX_Find(ssl->extensions, TLSX_APPLICATION_LAYER_PROTOCOL);
@@ -1908,6 +1936,23 @@ static int ALPN_find_match(WOLFSSL *ssl, TLSX **pextension,
}
list = (ALPN*)extension->data;
+
+ /* Build hash set of server names — O(S) */
+ XMEMSET(hs_ptr, 0, sizeof(hs_ptr));
+ XMEMSET(hs_len, 0, sizeof(hs_len));
+ {
+ ALPN *a;
+ for (a = list; a != NULL; a = a->next) {
+ word16 alen = (word16)XSTRLEN(a->protocol_name);
+ /* FNV-1a */
+ word32 h = 2166136261u;
+ for (word16 k = 0; k < alen; k++)
+ h = (h ^ (byte)a->protocol_name[k]) * 16777619u;
+ word32 slot = h & (WOLFSSL_ALPN_HS - 1);
+ while (hs_ptr[slot] != NULL &&
+ !(hs_len[slot] == alen &&
+ XSTRNCMP(hs_ptr[slot], a->protocol_name, alen) == 0))
+ slot = (slot + 1) & (WOLFSSL_ALPN_HS - 1);
+ hs_ptr[slot] = a->protocol_name;
+ hs_len[slot] = alen;
+ }
+ }
+
+ /* Iterate client list once, O(1) lookup per entry */
for (s = alpn_val;
(s - alpn_val) < alpn_val_len;
s += wlen) {
- wlen = *s++; /* bounds already checked on save */
- alpn = TLSX_ALPN_Find(list, (char*)s, wlen);
- if (alpn != NULL) {
+ wlen = *s++;
+ /* Hash lookup: O(1) expected */
+ word32 h = 2166136261u;
+ for (word16 k = 0; k < wlen; k++)
+ h = (h ^ s[k]) * 16777619u;
+ word32 slot = h & (WOLFSSL_ALPN_HS - 1);
+ while (hs_ptr[slot] != NULL &&
+ !(hs_len[slot] == wlen &&
+ XSTRNCMP(hs_ptr[slot], (char*)s, wlen) == 0))
+ slot = (slot + 1) & (WOLFSSL_ALPN_HS - 1);
+ if (hs_ptr[slot] != NULL) {
+ /* Recover full ALPN node from server list for options/negotiated */
+ alpn = TLSX_ALPN_Find(list, (char*)s, wlen); /* single-shot O(S) */
WOLFSSL_MSG("ALPN protocol match");
sel = s;
sel_len = wlen;
break;
}
}
+#undef WOLFSSL_ALPN_HS
```
## Overhead Removed
With S=5 server ALPN names and C=100 client names: 500 string compares → ~105
(4.8× speedup). With C=1000: 5000 → ~1005 (4.97× speedup; grows without bound
as C increases under attacker control).
## References
- RFC 7301 §3.1 — ALPN extension format (client list is variable-length)
- CWE-407: Inefficient Algorithmic Complexity