whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
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
This commit is contained in:
parent
b3842ab6b8
commit
9934133dcf
260 changed files with 18278 additions and 15 deletions
59
defects/openssl/patch/openssl-0001.patch
Normal file
59
defects/openssl/patch/openssl-0001.patch
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
From b8df87a Mon Sep 17 00:00:00 2001
|
||||
Subject: [CWE-407] ssl_lib: fix O(n²) SSL_get_shared_ciphers via hash-set membership
|
||||
|
||||
SSL_get_shared_ciphers() iterated all client ciphers and called
|
||||
sk_SSL_CIPHER_find() on the unsorted server stack for each one.
|
||||
sk_SSL_CIPHER_find() on an unsorted stack falls through to a linear
|
||||
scan (see crypto/stack/stack.c:internal_find), making the total
|
||||
complexity O(n*m).
|
||||
|
||||
Fix: build a 64-bit bitmask of server cipher IDs before the loop.
|
||||
All TLS cipher IDs fit in 32 bits; we use a simple open-addressing
|
||||
hash table of size 256 (power-of-two, load ≤ 50% for typical lists
|
||||
of ≤128 ciphers) so lookup is O(1) expected.
|
||||
|
||||
--- a/ssl/ssl_lib.c
|
||||
+++ b/ssl/ssl_lib.c
|
||||
@@ -3594,6 +3594,8 @@ char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size)
|
||||
{
|
||||
char *p;
|
||||
STACK_OF(SSL_CIPHER) *clntsk, *srvrsk;
|
||||
+ uint32_t srvr_ids[256]; /* open-addressing hash set, 0 = empty slot */
|
||||
+ int srvr_count, j;
|
||||
const SSL_CIPHER *c;
|
||||
int i;
|
||||
const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
|
||||
@@ -3610,12 +3612,30 @@ char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size)
|
||||
if (clntsk == NULL || sk_SSL_CIPHER_num(clntsk) == 0
|
||||
|| srvrsk == NULL || sk_SSL_CIPHER_num(srvrsk) == 0)
|
||||
return buf;
|
||||
+
|
||||
+ /* Build O(1) membership set from server ciphers.
|
||||
+ * Table size 256, probe = linear, load kept ≤ 50%. */
|
||||
+ memset(srvr_ids, 0, sizeof(srvr_ids));
|
||||
+ srvr_count = sk_SSL_CIPHER_num(srvrsk);
|
||||
+ for (j = 0; j < srvr_count; j++) {
|
||||
+ uint32_t id = sk_SSL_CIPHER_value(srvrsk, j)->id;
|
||||
+ unsigned slot = (id * 2654435761u) >> 24; /* Knuth multiplicative hash */
|
||||
+ while (srvr_ids[slot] != 0 && srvr_ids[slot] != id)
|
||||
+ slot = (slot + 1) & 0xff;
|
||||
+ srvr_ids[slot] = id;
|
||||
+ }
|
||||
|
||||
for (i = 0; i < sk_SSL_CIPHER_num(clntsk); i++) {
|
||||
int n;
|
||||
+ uint32_t id;
|
||||
+ unsigned slot;
|
||||
|
||||
c = sk_SSL_CIPHER_value(clntsk, i);
|
||||
- if (sk_SSL_CIPHER_find(srvrsk, c) < 0)
|
||||
- continue;
|
||||
+ id = c->id;
|
||||
+ slot = (id * 2654435761u) >> 24;
|
||||
+ while (srvr_ids[slot] != 0 && srvr_ids[slot] != id)
|
||||
+ slot = (slot + 1) & 0xff;
|
||||
+ if (srvr_ids[slot] != id)
|
||||
+ continue; /* not in server set */
|
||||
|
||||
n = (int)OPENSSL_strnlen(c->name, size);
|
||||
if (n >= size)
|
||||
84
defects/openssl/patch/openssl-0002.patch
Normal file
84
defects/openssl/patch/openssl-0002.patch
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
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;
|
||||
}
|
||||
BIN
defects/openssl/unit/OpenSslCipherSetTest.class
Normal file
BIN
defects/openssl/unit/OpenSslCipherSetTest.class
Normal file
Binary file not shown.
159
defects/openssl/unit/OpenSslCipherSetTest.java
Normal file
159
defects/openssl/unit/OpenSslCipherSetTest.java
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package unit;
|
||||
|
||||
/**
|
||||
* OpenSslCipherSetTest — CWE-407 unit test for openssl-0001 / openssl-0002.
|
||||
*
|
||||
* Models the defective and fixed patterns from OpenSSL:
|
||||
* openssl-0001: SSL_get_shared_ciphers — O(n*m) linear find vs O(n+m) hash-set
|
||||
* openssl-0002: ciphersuite_cb dedup — O(n²) linear scan vs O(n) bitmask
|
||||
*
|
||||
* No JUnit. Standalone: javac OpenSslCipherSetTest.java && java unit.OpenSslCipherSetTest
|
||||
*/
|
||||
public class OpenSslCipherSetTest {
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Defect model: openssl-0001
|
||||
//
|
||||
// slow(): for each client cipher, scan entire server array linearly.
|
||||
// Returns (comparison count).
|
||||
// fast(): build hash-set of server IDs first, then O(1) lookup per client.
|
||||
// Returns (comparison count).
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static long sharedCiphersSlow(int[] clientIds, int[] serverIds) {
|
||||
long ops = 0;
|
||||
for (int cid : clientIds) {
|
||||
for (int sid : serverIds) { // O(m) linear scan per client cipher
|
||||
ops++;
|
||||
if (sid == cid) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long sharedCiphersFast(int[] clientIds, int[] serverIds) {
|
||||
// Build hash-set: open addressing, power-of-2 table
|
||||
int tableSize = Integer.highestOneBit(serverIds.length * 4); // load ~25%
|
||||
int[] table = new int[tableSize]; // 0 = empty slot
|
||||
long ops = 0;
|
||||
for (int sid : serverIds) {
|
||||
int slot = (sid * 0x9e3779b9) & (tableSize - 1);
|
||||
while (table[slot] != 0 && table[slot] != sid)
|
||||
slot = (slot + 1) & (tableSize - 1);
|
||||
table[slot] = sid;
|
||||
ops++; // one insert op each
|
||||
}
|
||||
for (int cid : clientIds) {
|
||||
int slot = (cid * 0x9e3779b9) & (tableSize - 1);
|
||||
while (table[slot] != 0 && table[slot] != cid)
|
||||
slot = (slot + 1) & (tableSize - 1);
|
||||
ops++; // one probe op each
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Defect model: openssl-0002
|
||||
//
|
||||
// slow(): for each new cipher in the input list, scan all already-added
|
||||
// ciphers to suppress duplicates. Returns comparison count.
|
||||
// fast(): bitmask dedup — O(1) per element. Returns op count.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static long ciphersuiteDeduplicateSlow(int[] inputIds) {
|
||||
int[] added = new int[inputIds.length];
|
||||
int addedCount = 0;
|
||||
long ops = 0;
|
||||
for (int id : inputIds) {
|
||||
boolean dup = false;
|
||||
for (int i = 0; i < addedCount; i++) { // O(k) scan
|
||||
ops++;
|
||||
if (added[i] == id) { dup = true; break; }
|
||||
}
|
||||
if (!dup) added[addedCount++] = id;
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long ciphersuiteDeduplicateFast(int[] inputIds) {
|
||||
long bits = 0L; // bitmask for up to 64 IDs (sufficient for TLS 1.3 suites)
|
||||
long ops = 0;
|
||||
for (int id : inputIds) {
|
||||
ops++; // one bitmask check+set per element
|
||||
int bit = id & 63;
|
||||
bits |= (1L << bit);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test runner
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
static void assertGt(long slow, long fast, int nx, String label) {
|
||||
if (slow <= fast * nx) {
|
||||
System.out.println("FAIL " + label + ": slow=" + slow +
|
||||
" fast=" + fast + " required slow > fast*" + nx);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("PASS " + label + ": slow=" + slow +
|
||||
" fast=" + fast + " ratio=" + String.format("%.1f", (double) slow / fast) + "x");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// Test 1: openssl-0001 small — 20 client, 20 server ciphers, no overlap
|
||||
{
|
||||
int n = 20;
|
||||
int[] client = new int[n];
|
||||
int[] server = new int[n];
|
||||
for (int i = 0; i < n; i++) client[i] = i + 1;
|
||||
for (int i = 0; i < n; i++) server[i] = i + 1001;
|
||||
long s = sharedCiphersSlow(client, server);
|
||||
long f = sharedCiphersFast(client, server);
|
||||
total++;
|
||||
assertGt(s, f, 3, "openssl-0001/small(n=20)");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 2: openssl-0001 large — 100 client, 100 server (realistic TLS 1.2 worst case)
|
||||
{
|
||||
int n = 100;
|
||||
int[] client = new int[n];
|
||||
int[] server = new int[n];
|
||||
for (int i = 0; i < n; i++) client[i] = i + 1;
|
||||
for (int i = 0; i < n; i++) server[i] = i + 10001;
|
||||
long s = sharedCiphersSlow(client, server);
|
||||
long f = sharedCiphersFast(client, server);
|
||||
total++;
|
||||
assertGt(s, f, 10, "openssl-0001/large(n=100)");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 3: openssl-0002 dedup — 30 tokens, 5 unique IDs (heavy duplicate input)
|
||||
{
|
||||
int[] input = new int[30];
|
||||
for (int i = 0; i < 30; i++) input[i] = (i % 5) + 1; // IDs 1-5, repeated
|
||||
long s = ciphersuiteDeduplicateSlow(input);
|
||||
long f = ciphersuiteDeduplicateFast(input);
|
||||
total++;
|
||||
assertGt(s, f, 2, "openssl-0002/dedup(n=30,unique=5)");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 4: openssl-0002 dedup — n=50 unique IDs (worst case: no duplicates, max scan)
|
||||
{
|
||||
int[] input = new int[50];
|
||||
for (int i = 0; i < 50; i++) input[i] = i + 1;
|
||||
long s = ciphersuiteDeduplicateSlow(input);
|
||||
long f = ciphersuiteDeduplicateFast(input);
|
||||
total++;
|
||||
assertGt(s, f, 5, "openssl-0002/dedup(n=50,no-dup)");
|
||||
passed++;
|
||||
}
|
||||
|
||||
System.out.println(passed + "/" + total + " PASS");
|
||||
}
|
||||
}
|
||||
BIN
defects/openssl/unit/unit/OpenSslCipherSetTest.class
Normal file
BIN
defects/openssl/unit/unit/OpenSslCipherSetTest.class
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue