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 allBuckets, List 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 allBuckets, Set 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 allBuckets = new ArrayList<>(); List healedBuckets = new ArrayList<>(); for (int i = 0; i < B; i++) { allBuckets.add("bucket-" + i); if (i < B / 2) healedBuckets.add("bucket-" + i); } Set 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 pendingBuckets, List 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 pendingBuckets, Set 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 pendingBuckets = new ArrayList<>(); List decomBuckets = new ArrayList<>(); for (int i = 0; i < B; i++) { pendingBuckets.add("bucket-" + i); if (i < B / 2) decomBuckets.add("bucket-" + i); } Set 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 g1Members, List 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 g1Members, List g2Members) { if (g1Members.size() != g2Members.size()) return false; Set 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 g1Members = new ArrayList<>(); List 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)"); } }