# UNDF: UNDF-2026-000000203 From b8df87a Mon Sep 17 00:00:00 2001 Subject: [CWE-407] ssl_ciph: fix O(n²) TLS1.3 cipher dedup in ciphersuite_cb ciphersuite_cb() suppressed duplicate cipher IDs with a linear scan over the already-added ciphersuites stack. Called once per input token by CONF_parse_list, the total is O(n²). Fix: use a static bitmask indexed by cipher table position. There are exactly 5 standard TLS 1.3 ciphersuites (ssl3_get_tls13_cipher_by_std_name returns a pointer into the tls13_ciphers array). The bitmask is a uint8_t[8] passed in as part of a small context struct, giving O(1) dedup. --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -1225,10 +1225,21 @@ static int update_cipher_list(SSL_CTX *ctx, return 1; } +/* Context passed to ciphersuite_cb via CONF_parse_list arg. */ +struct ciphersuite_cb_ctx { + STACK_OF(SSL_CIPHER) *ciphersuites; + uint8_t seen[32]; /* bitmask: bit i set iff tls13_ciphers+i already added */ +}; + static int ciphersuite_cb(const char *elem, int len, void *arg) { - STACK_OF(SSL_CIPHER) *ciphersuites = (STACK_OF(SSL_CIPHER) *)arg; + struct ciphersuite_cb_ctx *ctx = (struct ciphersuite_cb_ctx *)arg; + STACK_OF(SSL_CIPHER) *ciphersuites = ctx->ciphersuites; const SSL_CIPHER *cipher; + ptrdiff_t idx; /* Arbitrary sized temp buffer for the cipher name. Should be big enough */ char name[80]; @@ -1241,12 +1252,14 @@ static int ciphersuite_cb(const char *elem, int len, void *arg) cipher = ssl3_get_tls13_cipher_by_std_name(name); if (cipher == NULL) /* Ciphersuite not found but return 1 to parse rest of the list */ return 1; - /* Suppress duplicates */ - for (int i = 0; i < sk_SSL_CIPHER_num(ciphersuites); ++i) - if (sk_SSL_CIPHER_value(ciphersuites, i)->id == cipher->id) - return 1; + /* Suppress duplicates — O(1) bitmask on cipher table index */ + idx = cipher - tls13_ciphers; /* pointer arithmetic into static array */ + if (idx >= 0 && idx < (ptrdiff_t)(sizeof(ctx->seen) * 8) + && (ctx->seen[idx / 8] & (1u << (idx % 8)))) + return 1; + if (idx >= 0 && idx < (ptrdiff_t)(sizeof(ctx->seen) * 8)) + ctx->seen[idx / 8] |= (uint8_t)(1u << (idx % 8)); if (!sk_SSL_CIPHER_push(ciphersuites, cipher)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); @@ -1262,11 +1275,16 @@ static __owur int set_ciphersuites(STACK_OF(SSL_CIPHER) **currciphers, const cha { - STACK_OF(SSL_CIPHER) *newciphers = sk_SSL_CIPHER_new_null(); + struct ciphersuite_cb_ctx ctx; + + ctx.ciphersuites = sk_SSL_CIPHER_new_null(); + memset(ctx.seen, 0, sizeof(ctx.seen)); - if (newciphers == NULL) + if (ctx.ciphersuites == NULL) return 0; /* Parse the list. We explicitly allow an empty list */ if (*str != '\0' - && (CONF_parse_list(str, ':', 1, ciphersuite_cb, newciphers) <= 0 - || sk_SSL_CIPHER_num(newciphers) == 0)) { + && (CONF_parse_list(str, ':', 1, ciphersuite_cb, &ctx) <= 0 + || sk_SSL_CIPHER_num(ctx.ciphersuites) == 0)) { ERR_raise(ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH); - sk_SSL_CIPHER_free(newciphers); + sk_SSL_CIPHER_free(ctx.ciphersuites); return 0; } - sk_SSL_CIPHER_free(*currciphers); - *currciphers = newciphers; + sk_SSL_CIPHER_free(*currciphers); + *currciphers = ctx.ciphersuites; return 1; }