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
255 lines
10 KiB
Java
255 lines
10 KiB
Java
package unit;
|
||
import java.util.*;
|
||
|
||
/**
|
||
* RedisTest — CWE-407 benchmarks for redis-0001 and redis-0002
|
||
*
|
||
* redis-0001: SINTER on listpack-encoded sets
|
||
* SLOW: O(N×M) — outer iterate N elements, inner lpFind O(M) per probe set
|
||
* FAST: O(N) — outer iterate N elements, inner HashSet.contains O(1)
|
||
*
|
||
* redis-0002: getUpcomingChannelList ACL channel superset check
|
||
* SLOW: O((S×C)²) — build flat list, listSearchKey O(n) per pattern
|
||
* FAST: O(S×C) — build HashSet, O(1) lookup per pattern
|
||
*/
|
||
public class RedisTest {
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Benchmark harness (matches project style)
|
||
// -------------------------------------------------------------------------
|
||
|
||
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
|
||
slow.run(); fast.run();
|
||
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
|
||
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
|
||
System.out.printf(" %-56s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||
label, sMs, sOps, fMs, fOps, fOps > 0 ? (double) sOps / fOps : 0);
|
||
}
|
||
|
||
// =========================================================================
|
||
// redis-0001: SINTER listpack O(N×M)
|
||
//
|
||
// Models sinterGenericCommand with listpack-encoded inner sets.
|
||
// "listpack membership" = linear scan (lpFindCbInternal).
|
||
// "htset membership" = O(1) HashSet lookup.
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Simulate SINTER(set0, set1) where set1 uses listpack encoding.
|
||
* Returns number of element comparisons performed.
|
||
*
|
||
* set0: List<String> to iterate
|
||
* set1AsArray: String[] simulating a packed listpack (linear scan required)
|
||
*/
|
||
static long sinter_slow(List<String> set0, String[] set1) {
|
||
long ops = 0;
|
||
for (String elem : set0) {
|
||
// listpack membership = linear scan from start
|
||
for (String s : set1) {
|
||
ops++;
|
||
if (s.equals(elem)) break;
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/**
|
||
* Simulate SINTER(set0, set1) after promoting set1 to a hash set.
|
||
* Returns number of element comparisons performed.
|
||
*/
|
||
static long sinter_fast(List<String> set0, Set<String> set1) {
|
||
long ops = 0;
|
||
for (String elem : set0) {
|
||
ops++; // O(1) hash lookup
|
||
set1.contains(elem);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// =========================================================================
|
||
// redis-0002: getUpcomingChannelList O((S×C)²)
|
||
//
|
||
// Models the upcoming-channel superset check.
|
||
// SLOW: build a list, scan linearly for each original-selector channel.
|
||
// FAST: build a HashSet, O(1) lookup.
|
||
// =========================================================================
|
||
|
||
/** Returns number of comparisons in the listSearchKey-style scan. */
|
||
static long upcomingChannels_slow(List<String> newChannels, List<String> originalChannels) {
|
||
// Build upcoming as a List (mirrors listAddNodeTail + listSearchKey)
|
||
List<String> upcoming = new ArrayList<>(newChannels);
|
||
long ops = 0;
|
||
for (String orig : originalChannels) {
|
||
// listSearchKey — O(upcoming.size())
|
||
for (String up : upcoming) {
|
||
ops++;
|
||
if (up.equals(orig)) break;
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** Returns number of comparisons using a HashSet (the fix). */
|
||
static long upcomingChannels_fast(List<String> newChannels, List<String> originalChannels) {
|
||
Set<String> upcoming = new HashSet<>(newChannels);
|
||
long ops = newChannels.size(); // one pass to build the set
|
||
for (String orig : originalChannels) {
|
||
ops++; // O(1) HashSet lookup
|
||
upcoming.contains(orig);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// =========================================================================
|
||
// Main
|
||
// =========================================================================
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("RedisTest — CWE-407");
|
||
System.out.println();
|
||
|
||
int passed = 0, total = 0;
|
||
|
||
// --- redis-0001 Scenario 1: N=128, M=128 (default listpack threshold) ---
|
||
{
|
||
int N = 128, M = 128;
|
||
List<String> set0 = new ArrayList<>(N);
|
||
String[] set1Array = new String[M];
|
||
Set<String> set1Set = new HashSet<>();
|
||
|
||
// set0 and set1 share half their elements (worst-case: all must be scanned)
|
||
for (int i = 0; i < N; i++) set0.add("elem:" + i);
|
||
for (int i = 0; i < M; i++) {
|
||
set1Array[i] = "elem:" + (i + N / 2); // partial overlap
|
||
set1Set.add(set1Array[i]);
|
||
}
|
||
|
||
final long[] sOps = {0}, fOps = {0};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 1000; r++) ops += sinter_slow(set0, set1Array);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 1000; r++) ops += sinter_fast(set0, set1Set);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("redis-0001 SINTER N=128 M=128 listpack (1k runs)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// Expect at least 50x speedup (theoretical max 128x, partial overlap ~64x)
|
||
boolean ok = sOps[0] >= fOps[0] * 50;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.0fx (need >=50x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// --- redis-0001 Scenario 2: N=128, M=128, no overlap (worst case: full scan) ---
|
||
{
|
||
int N = 128, M = 128;
|
||
List<String> set0 = new ArrayList<>(N);
|
||
String[] set1Array = new String[M];
|
||
Set<String> set1Set = new HashSet<>();
|
||
|
||
for (int i = 0; i < N; i++) set0.add("a:" + i);
|
||
for (int i = 0; i < M; i++) {
|
||
set1Array[i] = "b:" + i;
|
||
set1Set.add(set1Array[i]);
|
||
}
|
||
|
||
final long[] sOps = {0}, fOps = {0};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 1000; r++) ops += sinter_slow(set0, set1Array);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 1000; r++) ops += sinter_fast(set0, set1Set);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("redis-0001 SINTER N=128 M=128 no-overlap (1k runs)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// No overlap: every element scans all M=128 entries → 128×128=16384 vs 128
|
||
boolean ok = sOps[0] >= fOps[0] * 100;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.0fx (need >=100x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// --- redis-0002 Scenario 1: S=4 selectors, C=50 channels each ---
|
||
{
|
||
int S = 4, C = 50;
|
||
List<String> newChannels = new ArrayList<>();
|
||
List<String> origChannels = new ArrayList<>();
|
||
|
||
for (int s = 0; s < S; s++)
|
||
for (int c = 0; c < C; c++)
|
||
newChannels.add("chan:" + s + ":" + c);
|
||
|
||
// Original has same S×C channels (full overlap — must check all)
|
||
origChannels.addAll(newChannels);
|
||
Collections.shuffle(origChannels, new Random(42));
|
||
|
||
final long[] sOps = {0}, fOps = {0};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 500; r++) ops += upcomingChannels_slow(newChannels, origChannels);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 500; r++) ops += upcomingChannels_fast(newChannels, origChannels);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("redis-0002 ACL channels S=4 C=50 full-overlap (500r)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// 200 channels: slow ~200*100 avg = 20000 per call; fast = 200+200 = 400
|
||
boolean ok = sOps[0] >= fOps[0] * 20;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.0fx (need >=20x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// --- redis-0002 Scenario 2: S=10, C=100 — heavy compartmentalization ---
|
||
{
|
||
int S = 10, C = 100;
|
||
List<String> newChannels = new ArrayList<>();
|
||
List<String> origChannels = new ArrayList<>();
|
||
|
||
for (int s = 0; s < S; s++)
|
||
for (int c = 0; c < C; c++)
|
||
newChannels.add("ch:" + s + ":" + c);
|
||
|
||
origChannels.addAll(newChannels);
|
||
Collections.shuffle(origChannels, new Random(99));
|
||
|
||
final long[] sOps = {0}, fOps = {0};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 100; r++) ops += upcomingChannels_slow(newChannels, origChannels);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 100; r++) ops += upcomingChannels_fast(newChannels, origChannels);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("redis-0002 ACL channels S=10 C=100 (100 runs)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// 1000 channels: slow avg ~500 per lookup * 1000 checks = 500k; fast = 2000
|
||
boolean ok = sOps[0] >= fOps[0] * 100;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.0fx (need >=100x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
System.out.println();
|
||
System.out.printf("%d/%d PASS%n", passed, total);
|
||
if (passed < total) System.exit(1);
|
||
}
|
||
}
|