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
187 lines
6.9 KiB
Java
187 lines
6.9 KiB
Java
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)");
|
||
}
|
||
}
|