java-topology/defects/minio/unit/MinioTest.java
russell@unturf.com cb853893e5 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
2026-03-30 10:24:48 -04:00

187 lines
6.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)");
}
}