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
210 lines
8.4 KiB
Java
210 lines
8.4 KiB
Java
package unit;
|
||
import java.util.*;
|
||
|
||
/**
|
||
* MemcachedTest — CWE-407 benchmark for memcached-0001
|
||
*
|
||
* memcached-0001: slabs_clsid() O(n) linear scan over sorted slabclass[] array
|
||
* SLOW: O(n) — while loop walking up to 63 slab classes
|
||
* FAST: O(log n) — binary search, ⌈log₂63⌉ = 6 comparisons max
|
||
*
|
||
* slabs_clsid is called on every item allocation (do_item_alloc) making it
|
||
* a hot path under write load.
|
||
*/
|
||
public class MemcachedTest {
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Benchmark harness
|
||
// -------------------------------------------------------------------------
|
||
|
||
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);
|
||
}
|
||
|
||
// =========================================================================
|
||
// Model: slab class array (mirrors slabclass[].size in memcached)
|
||
//
|
||
// Sizes are generated with factor=1.25 starting at 96 bytes (memcached
|
||
// default: chunk_size=48, align to 8 → starts at 96 with use_cas).
|
||
// MAX_NUMBER_OF_SLAB_CLASSES = 64 (1..63 + power_largest).
|
||
// =========================================================================
|
||
|
||
static final int POWER_SMALLEST = 1;
|
||
static final int MAX_CLASSES = 64;
|
||
|
||
static int[] buildSlabSizes() {
|
||
int[] sizes = new int[MAX_CLASSES];
|
||
double size = 96.0;
|
||
double factor = 1.25;
|
||
int chunkMax = 1024 * 1024; // 1 MB item_size_max
|
||
for (int i = POWER_SMALLEST; i < MAX_CLASSES - 1; i++) {
|
||
// align to 8 bytes
|
||
int aligned = ((int) size + 7) & ~7;
|
||
sizes[i] = aligned;
|
||
if (aligned >= chunkMax / factor) {
|
||
// fill remaining classes with chunkMax
|
||
for (int j = i + 1; j < MAX_CLASSES; j++) sizes[j] = chunkMax;
|
||
break;
|
||
}
|
||
size *= factor;
|
||
}
|
||
sizes[MAX_CLASSES - 1] = chunkMax;
|
||
return sizes;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// SLOW: linear scan (current memcached code)
|
||
// -------------------------------------------------------------------------
|
||
|
||
/** Returns number of comparisons performed. */
|
||
static long slabs_clsid_slow(int[] sizes, int powerLargest, int querySize) {
|
||
int res = POWER_SMALLEST;
|
||
long ops = 0;
|
||
while (querySize > sizes[res]) {
|
||
ops++;
|
||
if (res++ == powerLargest) return ops;
|
||
}
|
||
ops++; // final comparison that passes
|
||
return ops;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// FAST: binary search (the fix)
|
||
// -------------------------------------------------------------------------
|
||
|
||
/** Returns number of comparisons performed. */
|
||
static long slabs_clsid_fast(int[] sizes, int powerLargest, int querySize) {
|
||
int lo = POWER_SMALLEST, hi = powerLargest;
|
||
long ops = 0;
|
||
while (lo < hi) {
|
||
int mid = lo + (hi - lo) / 2;
|
||
ops++;
|
||
if (sizes[mid] < querySize)
|
||
lo = mid + 1;
|
||
else
|
||
hi = mid;
|
||
}
|
||
ops++; // final check lo==hi
|
||
return ops;
|
||
}
|
||
|
||
// =========================================================================
|
||
// Main
|
||
// =========================================================================
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("MemcachedTest — CWE-407");
|
||
System.out.println();
|
||
|
||
int[] sizes = buildSlabSizes();
|
||
// Find actual power_largest
|
||
int powerLargest = MAX_CLASSES - 1;
|
||
for (int i = POWER_SMALLEST; i < MAX_CLASSES; i++) {
|
||
if (sizes[i] == 0) { powerLargest = i - 1; break; }
|
||
}
|
||
System.out.printf(" Slab classes: POWER_SMALLEST=%d power_largest=%d%n",
|
||
POWER_SMALLEST, powerLargest);
|
||
System.out.printf(" size[1]=%d size[%d]=%d%n",
|
||
sizes[1], powerLargest, sizes[powerLargest]);
|
||
System.out.println();
|
||
|
||
int passed = 0, total = 0;
|
||
|
||
// --- Scenario 1: worst case — query fits only in the largest class ---
|
||
{
|
||
int querySize = sizes[powerLargest]; // must scan all the way
|
||
final long[] sOps = {0}, fOps = {0};
|
||
final int[] ps = {powerLargest};
|
||
final int[] qs = {querySize};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 5_000_000; r++)
|
||
ops += slabs_clsid_slow(sizes, ps[0], qs[0]);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 5_000_000; r++)
|
||
ops += slabs_clsid_fast(sizes, ps[0], qs[0]);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("memcached-0001 slabs_clsid worst-case size=max (5M allocs)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// Expect linear >> log; linear = ~63 ops, log = ~6 ops => ~10x
|
||
boolean ok = sOps[0] >= fOps[0] * 5;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.1fx (need >=5x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// --- Scenario 2: mixed query sizes (realistic workload) ---
|
||
{
|
||
// Queries uniformly distributed over all slab sizes
|
||
int numSizes = powerLargest - POWER_SMALLEST + 1;
|
||
int[] queries = new int[numSizes];
|
||
for (int i = 0; i < numSizes; i++) queries[i] = sizes[POWER_SMALLEST + i];
|
||
|
||
final long[] sOps = {0}, fOps = {0};
|
||
final int[] ps = {powerLargest};
|
||
Runnable slow = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 200_000; r++)
|
||
for (int q : queries)
|
||
ops += slabs_clsid_slow(sizes, ps[0], q);
|
||
sOps[0] = ops;
|
||
};
|
||
Runnable fast = () -> {
|
||
long ops = 0;
|
||
for (int r = 0; r < 200_000; r++)
|
||
for (int q : queries)
|
||
ops += slabs_clsid_fast(sizes, ps[0], q);
|
||
fOps[0] = ops;
|
||
};
|
||
slow.run(); fast.run();
|
||
bench("memcached-0001 slabs_clsid mixed sizes (200k×all)", slow, fast, sOps[0], fOps[0]);
|
||
total++;
|
||
// Average linear: ~(63+1)/2 ≈ 32 ops; average binary: ~5 ops
|
||
boolean ok = sOps[0] >= fOps[0] * 4;
|
||
System.out.printf(" [%s] slow=%,d fast=%,d ratio=%.1fx (need >=4x)%n",
|
||
ok ? "PASS" : "FAIL", sOps[0], fOps[0], (double) sOps[0] / fOps[0]);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// --- Scenario 3: correctness check — both find the same class ---
|
||
{
|
||
Random rng = new Random(0);
|
||
int maxSize = sizes[powerLargest];
|
||
int mismatches = 0;
|
||
for (int i = 0; i < 100_000; i++) {
|
||
int q = 1 + rng.nextInt(maxSize);
|
||
// find expected class with slow (reference)
|
||
int res_s = POWER_SMALLEST;
|
||
while (q > sizes[res_s] && res_s < powerLargest) res_s++;
|
||
// find with fast
|
||
int lo = POWER_SMALLEST, hi = powerLargest;
|
||
while (lo < hi) {
|
||
int mid = lo + (hi - lo) / 2;
|
||
if (sizes[mid] < q) lo = mid + 1;
|
||
else hi = mid;
|
||
}
|
||
int res_f = lo;
|
||
if (res_s != res_f) mismatches++;
|
||
}
|
||
total++;
|
||
boolean ok = mismatches == 0;
|
||
System.out.printf(" %-56s [%s] mismatches=%d%n",
|
||
"memcached-0001 correctness (100k random queries)",
|
||
ok ? "PASS" : "FAIL", mismatches);
|
||
if (ok) passed++;
|
||
}
|
||
|
||
System.out.println();
|
||
System.out.printf("%d/%d PASS%n", passed, total);
|
||
if (passed < total) System.exit(1);
|
||
}
|
||
}
|