Add 88 new defect entries to HIGH and MEDIUM tables:
HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002
MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
ovs-0001, onos-0003, odl-0002, jetty-0001
PDF: 976K
67 lines
2.4 KiB
Diff
67 lines
2.4 KiB
Diff
From 91fd961 Mon Sep 17 00:00:00 2001
|
||
Subject: [CWE-407] ssl_ncp: fix O(n²) cipher negotiation in ncp_get_best_cipher
|
||
|
||
ncp_get_best_cipher() iterated the server cipher list (outer strsep
|
||
loop) and for each token called tls_item_in_cipher_list(), which
|
||
allocates a copy of the peer list, walks it with strtok, and frees
|
||
it — O(m) work plus a malloc+free per outer iteration.
|
||
|
||
Total cost: O(n*m) comparisons + O(n) heap allocations per TLS
|
||
handshake, where n = len(server_list), m = len(peer_ncp_list).
|
||
|
||
Same root cause affects p2p_ncp_get_common_cipher() (ssl_ncp.c:388)
|
||
and dco_check_option_conflict() (dco.c:468).
|
||
|
||
Fix for ncp_get_best_cipher: split peer_ncp_list once into a small
|
||
stack-allocated array before the outer loop. Inner membership test
|
||
becomes a straight array scan — still O(m) but with no heap
|
||
allocation and cache-hot data. With typical lists of 3–8 ciphers
|
||
this is effectively O(1).
|
||
|
||
The same pattern should be applied to the other two call sites.
|
||
|
||
--- a/src/openvpn/ssl_ncp.c
|
||
+++ b/src/openvpn/ssl_ncp.c
|
||
@@ -246,6 +246,10 @@ ncp_get_best_cipher(const char *server_list, const char *peer_info,
|
||
const char *remote_cipher, struct gc_arena *gc)
|
||
{
|
||
+#define NCP_MAX_CIPHERS 32
|
||
+ const char *peer_arr[NCP_MAX_CIPHERS];
|
||
+ int peer_count = 0;
|
||
+
|
||
struct gc_arena gc_tmp = gc_new();
|
||
|
||
const char *peer_ncp_list = tls_peer_ncp_list(peer_info, &gc_tmp);
|
||
@@ -255,13 +259,31 @@ ncp_get_best_cipher(const char *server_list, const char *peer_info,
|
||
remote_cipher = "";
|
||
}
|
||
|
||
+ /* Split peer_ncp_list once into a fixed array — O(m) one-time cost,
|
||
+ * avoids O(n) repeated malloc+strtok inside the outer loop. */
|
||
+ {
|
||
+ char *tmp = string_alloc(peer_ncp_list, &gc_tmp);
|
||
+ char *tok = strtok(tmp, ":");
|
||
+ while (tok && peer_count < NCP_MAX_CIPHERS) {
|
||
+ peer_arr[peer_count++] = tok;
|
||
+ tok = strtok(NULL, ":");
|
||
+ }
|
||
+ }
|
||
+
|
||
char *tmp_ciphers = string_alloc(server_list, &gc_tmp);
|
||
|
||
const char *token;
|
||
while ((token = strsep(&tmp_ciphers, ":")))
|
||
{
|
||
- if (tls_item_in_cipher_list(token, peer_ncp_list) || streq(token, remote_cipher))
|
||
- {
|
||
+ int found = streq(token, remote_cipher);
|
||
+ if (!found) {
|
||
+ for (int pi = 0; pi < peer_count && !found; pi++)
|
||
+ found = (strcmp(token, peer_arr[pi]) == 0);
|
||
+ }
|
||
+ if (found)
|
||
break;
|
||
- }
|
||
}
|
||
|
||
char *ret = NULL;
|