minio: 3 CWE-407 defects; etcd: CLEAN (deeper scan)

minio-0001: healingTracker.isHealed() slices.Contains O(B×H) MEDIUM 25x
minio-0002: isBucketDecommissioned() slices.Contains O(P×D) MEDIUM 24x
minio-0003: isGroupDescEqual/isUserInfoEqual slices.Contains O(M²) MEDIUM 13x
etcd: maps + interval trees throughout; no CWE-407 defects found
This commit is contained in:
russell@unturf.com 2026-03-30 10:24:48 -04:00
parent ae8621d527
commit cb853893e5
6 changed files with 376 additions and 0 deletions

View file

@ -0,0 +1,187 @@
import java.util.*;
/**
* CWE-407 unit tests for MinIO defects.
*
* minio-0001: cmd/background-newdisks-heal-ops.go healingTracker.isHealed()
* slices.Contains(HealedBuckets, bucket) inside heal loop → O(B×H).
* Fix: maintain map[string]struct{} for O(1) lookup.
*
* minio-0002: cmd/erasure-server-pool-decom.go isBucketDecommissioned()
* slices.Contains(DecommissionedBuckets, bucket) inside decom loop → O(P×D).
* Fix: maintain map[string]struct{} for O(1) lookup.
*
* minio-0003: cmd/site-replication.go isGroupDescEqual/isUserInfoEqual
* slices.Contains(g2.Members, v1) inside member loop → O(M²).
* Fix: build map from one side, probe with the other.
*/
public class MinioTest {
// ==================== minio-0001 ====================
/** Defect: linear scan over healedBuckets for each bucket */
static int healLoop_list(List<String> allBuckets, List<String> healedBuckets) {
int skipped = 0;
for (String bucket : allBuckets) {
if (healedBuckets.contains(bucket)) { // O(H) per bucket
skipped++;
}
}
return skipped;
}
/** Fix: map lookup for O(1) */
static int healLoop_map(List<String> allBuckets, Set<String> healedSet) {
int skipped = 0;
for (String bucket : allBuckets) {
if (healedSet.contains(bucket)) { // O(1)
skipped++;
}
}
return skipped;
}
static void testMinio0001() throws Exception {
int B = 1000;
List<String> allBuckets = new ArrayList<>();
List<String> healedBuckets = new ArrayList<>();
for (int i = 0; i < B; i++) {
allBuckets.add("bucket-" + i);
if (i < B / 2) healedBuckets.add("bucket-" + i);
}
Set<String> healedSet = new HashSet<>(healedBuckets);
// correctness
int r1 = healLoop_list(allBuckets, healedBuckets);
int r2 = healLoop_map(allBuckets, healedSet);
assert r1 == r2 : "minio-0001 correctness: " + r1 + " vs " + r2;
// performance
long t0 = System.nanoTime();
for (int r = 0; r < 500; r++) healLoop_list(allBuckets, healedBuckets);
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 500; r++) healLoop_map(allBuckets, healedSet);
long tMap = System.nanoTime() - t0;
double ratio = (double) tList / tMap;
System.out.printf("minio-0001 heal-tracker: list=%.3fs map=%.3fs ratio=%.1f×%n",
tList / 1e9, tMap / 1e9, ratio);
assert ratio > 3 : "minio-0001: expected >3× speedup, got " + ratio;
}
// ==================== minio-0002 ====================
/** Defect: decommission loop checks each pending bucket against decommissioned list */
static int decomLoop_list(List<String> pendingBuckets, List<String> decomBuckets) {
int skipped = 0;
for (String bucket : pendingBuckets) {
if (decomBuckets.contains(bucket)) { // O(D) per bucket
skipped++;
}
}
return skipped;
}
/** Fix: map lookup */
static int decomLoop_map(List<String> pendingBuckets, Set<String> decomSet) {
int skipped = 0;
for (String bucket : pendingBuckets) {
if (decomSet.contains(bucket)) { // O(1)
skipped++;
}
}
return skipped;
}
static void testMinio0002() throws Exception {
int B = 1000;
List<String> pendingBuckets = new ArrayList<>();
List<String> decomBuckets = new ArrayList<>();
for (int i = 0; i < B; i++) {
pendingBuckets.add("bucket-" + i);
if (i < B / 2) decomBuckets.add("bucket-" + i);
}
Set<String> decomSet = new HashSet<>(decomBuckets);
int r1 = decomLoop_list(pendingBuckets, decomBuckets);
int r2 = decomLoop_map(pendingBuckets, decomSet);
assert r1 == r2 : "minio-0002 correctness: " + r1 + " vs " + r2;
long t0 = System.nanoTime();
for (int r = 0; r < 500; r++) decomLoop_list(pendingBuckets, decomBuckets);
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 500; r++) decomLoop_map(pendingBuckets, decomSet);
long tMap = System.nanoTime() - t0;
double ratio = (double) tList / tMap;
System.out.printf("minio-0002 decom-tracker: list=%.3fs map=%.3fs ratio=%.1f×%n",
tList / 1e9, tMap / 1e9, ratio);
assert ratio > 3 : "minio-0002: expected >3× speedup, got " + ratio;
}
// ==================== minio-0003 ====================
/** Defect: isGroupDescEqual uses slices.Contains in member loop → O(M²) */
static boolean isGroupDescEqual_list(List<String> g1Members, List<String> g2Members) {
if (g1Members.size() != g2Members.size()) return false;
for (String v1 : g1Members) {
if (!g2Members.contains(v1)) { // O(M) per member
return false;
}
}
return true;
}
/** Fix: build map from one side */
static boolean isGroupDescEqual_map(List<String> g1Members, List<String> g2Members) {
if (g1Members.size() != g2Members.size()) return false;
Set<String> memberSet = new HashSet<>(g2Members);
for (String v1 : g1Members) {
if (!memberSet.contains(v1)) { // O(1)
return false;
}
}
return true;
}
static void testMinio0003() throws Exception {
int M = 1000;
List<String> g1Members = new ArrayList<>();
List<String> g2Members = new ArrayList<>();
for (int i = 0; i < M; i++) {
g1Members.add("user-" + i);
g2Members.add("user-" + i);
}
// Shuffle g2 so order differs (forces full scan on match)
Collections.shuffle(g2Members);
boolean r1 = isGroupDescEqual_list(g1Members, g2Members);
boolean r2 = isGroupDescEqual_map(g1Members, g2Members);
assert r1 == r2 : "minio-0003 correctness: " + r1 + " vs " + r2;
assert r1 : "minio-0003: should be equal";
long t0 = System.nanoTime();
for (int r = 0; r < 500; r++) isGroupDescEqual_list(g1Members, g2Members);
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 500; r++) isGroupDescEqual_map(g1Members, g2Members);
long tMap = System.nanoTime() - t0;
double ratio = (double) tList / tMap;
System.out.printf("minio-0003 site-repl-eq: list=%.3fs map=%.3fs ratio=%.1f×%n",
tList / 1e9, tMap / 1e9, ratio);
assert ratio > 3 : "minio-0003: expected >3× speedup, got " + ratio;
}
public static void main(String[] args) throws Exception {
testMinio0001();
testMinio0002();
testMinio0003();
System.out.println("ALL PASS (3/3)");
}
}