116 lines
5.3 KiB
Java
116 lines
5.3 KiB
Java
package unit;
|
||
import java.util.*;
|
||
|
||
/**
|
||
* MattermostTest — CWE-407 benchmark
|
||
*
|
||
* Defects:
|
||
* 0001: CheckRolesExist() nested loop O(n×m) — linear scan of roles slice per role name
|
||
* (server/channels/app/role.go:258–278)
|
||
*/
|
||
public class MattermostTest {
|
||
|
||
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;
|
||
double r = fOps > 0 ? (double)sOps/fOps : 0;
|
||
System.out.printf(" %-56s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
||
label, sMs, sOps, fMs, fOps, r);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Defect 0001: CheckRolesExist nested loop
|
||
// n = role names to check, m = roles returned from DB
|
||
// -------------------------------------------------------------------------
|
||
static long slowCheckRolesExist(List<String> roleNames, List<String> roles) {
|
||
long ops = 0;
|
||
for (String name : roleNames) { // outer O(n)
|
||
boolean found = false;
|
||
for (String role : roles) { // inner O(m)
|
||
ops++;
|
||
if (name.equals(role)) { found = true; break; }
|
||
}
|
||
// found check omitted for pure measurement
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
static long fastCheckRolesExist(List<String> roleNames, List<String> roles) {
|
||
long ops = 0;
|
||
Set<String> roleSet = new HashSet<>(roles);
|
||
ops += roles.size(); // O(m) to build set
|
||
for (String name : roleNames) {
|
||
ops++;
|
||
roleSet.contains(name); // O(1)
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("MattermostTest — CWE-407");
|
||
|
||
// Realistic: 40 role names to check, 40 roles returned (same size)
|
||
// Stress: batch role assignment with 200 names
|
||
final int N_SMALL = 40;
|
||
final int M_SMALL = 40;
|
||
final int N_LARGE = 200;
|
||
final int M_LARGE = 200;
|
||
|
||
List<String> roleNamesSmall = new ArrayList<>(N_SMALL);
|
||
List<String> rolesSmall = new ArrayList<>(M_SMALL);
|
||
for (int i = 0; i < N_SMALL; i++) roleNamesSmall.add("role_name_" + i);
|
||
for (int i = 0; i < M_SMALL; i++) rolesSmall.add("role_name_" + i);
|
||
|
||
List<String> roleNamesLarge = new ArrayList<>(N_LARGE);
|
||
List<String> rolesLarge = new ArrayList<>(M_LARGE);
|
||
for (int i = 0; i < N_LARGE; i++) roleNamesLarge.add("role_name_" + i);
|
||
for (int i = 0; i < M_LARGE; i++) rolesLarge.add("role_name_" + i);
|
||
|
||
// Repeat many times to get measurable timing
|
||
final int ITERS = 50_000;
|
||
|
||
System.out.println("\n [0001] CheckRolesExist nested loop (n=" + N_SMALL + ", m=" + M_SMALL + ", x" + ITERS + " iters)");
|
||
bench("0001 roles nested loop vs map lookup",
|
||
() -> { for (int k = 0; k < ITERS; k++) slowCheckRolesExist(roleNamesSmall, rolesSmall); },
|
||
() -> { for (int k = 0; k < ITERS; k++) fastCheckRolesExist(roleNamesSmall, rolesSmall); },
|
||
(long) ITERS * N_SMALL * M_SMALL / 2,
|
||
(long) ITERS * (N_SMALL + M_SMALL));
|
||
|
||
System.out.println("\n [0001b] CheckRolesExist large batch (n=" + N_LARGE + ", m=" + M_LARGE + ", x" + ITERS + " iters)");
|
||
bench("0001b large batch nested loop vs map lookup",
|
||
() -> { for (int k = 0; k < ITERS; k++) slowCheckRolesExist(roleNamesLarge, rolesLarge); },
|
||
() -> { for (int k = 0; k < ITERS; k++) fastCheckRolesExist(roleNamesLarge, rolesLarge); },
|
||
(long) ITERS * N_LARGE * M_LARGE / 2,
|
||
(long) ITERS * (N_LARGE + M_LARGE));
|
||
|
||
// Assertions
|
||
int pass = 0, total = 2;
|
||
|
||
long slow1 = 0, fast1 = 0;
|
||
for (int k = 0; k < ITERS; k++) slow1 += slowCheckRolesExist(roleNamesSmall, rolesSmall);
|
||
for (int k = 0; k < ITERS; k++) fast1 += fastCheckRolesExist(roleNamesSmall, rolesSmall);
|
||
long slowPerIter = slow1 / ITERS;
|
||
long fastPerIter = fast1 / ITERS;
|
||
// slow should do ~N*M/2 ops, fast should do ~N+M ops per iter
|
||
if (slowPerIter > fastPerIter * (N_SMALL / 4)) {
|
||
pass++; System.out.println(" PASS 0001: slow ops/iter=" + slowPerIter + " >> fast ops/iter=" + fastPerIter);
|
||
} else {
|
||
System.out.println(" FAIL 0001: slow=" + slowPerIter + " fast=" + fastPerIter);
|
||
}
|
||
|
||
long slow2 = 0, fast2 = 0;
|
||
for (int k = 0; k < ITERS; k++) slow2 += slowCheckRolesExist(roleNamesLarge, rolesLarge);
|
||
for (int k = 0; k < ITERS; k++) fast2 += fastCheckRolesExist(roleNamesLarge, rolesLarge);
|
||
long slowPerIter2 = slow2 / ITERS;
|
||
long fastPerIter2 = fast2 / ITERS;
|
||
if (slowPerIter2 > fastPerIter2 * (N_LARGE / 4)) {
|
||
pass++; System.out.println(" PASS 0001b: slow ops/iter=" + slowPerIter2 + " >> fast ops/iter=" + fastPerIter2);
|
||
} else {
|
||
System.out.println(" FAIL 0001b: slow=" + slowPerIter2 + " fast=" + fastPerIter2);
|
||
}
|
||
|
||
System.out.printf("%n%d/%d PASS%n", pass, total);
|
||
if (pass < total) System.exit(1);
|
||
}
|
||
}
|