grafana: 3 CWE-407 defects; loki: 1 CWE-407 defect; 4/4 PASS
grafana-0001: getDashboardsSharedWithUser dashboard UID dedup O(P^2) MEDIUM 2.5x grafana-0002: folder UID dedup slices.Contains O(P^2) 4 sites MEDIUM 4x grafana-0003: deduplicateAvailableFolders ContainsFunc O(F*A) MEDIUM 9.3x loki-0001: DAG AddEdge/Eliminate slices.Contains O(E^2) LOW 2.1x
This commit is contained in:
parent
cf2c734de1
commit
689ec25d05
6 changed files with 577 additions and 66 deletions
125
defects/loki/unit/LokiTest.java
Normal file
125
defects/loki/unit/LokiTest.java
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Java simulation unit test for Loki CWE-407 defect.
|
||||
* Simulates the O(E^2) edge dedup pattern in Loki's DAG implementation.
|
||||
*
|
||||
* loki-0001: DAG AddEdge slices.Contains on children/parents O(E^2)
|
||||
*
|
||||
* In practice, Loki query plans have small fan-out (1-3 children per node),
|
||||
* so this is LOW severity. The test uses synthetic high fan-out to
|
||||
* demonstrate the algorithmic defect.
|
||||
*/
|
||||
public class LokiTest {
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// loki-0001: DAG AddEdge edge dedup O(E^2) vs O(E)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/** BEFORE: linear scan of children list for uniqueness */
|
||||
static class DagBefore {
|
||||
Map<String, List<String>> children = new HashMap<>();
|
||||
Map<String, List<String>> parents = new HashMap<>();
|
||||
|
||||
void addEdge(String parent, String child) {
|
||||
children.computeIfAbsent(parent, k -> new ArrayList<>());
|
||||
parents.computeIfAbsent(child, k -> new ArrayList<>());
|
||||
|
||||
List<String> ch = children.get(parent);
|
||||
if (!ch.contains(child)) { // O(E) linear scan
|
||||
ch.add(child);
|
||||
}
|
||||
List<String> pa = parents.get(child);
|
||||
if (!pa.contains(parent)) { // O(E) linear scan
|
||||
pa.add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** AFTER: parallel set for O(1) membership */
|
||||
static class DagAfter {
|
||||
Map<String, List<String>> children = new HashMap<>();
|
||||
Map<String, List<String>> parents = new HashMap<>();
|
||||
Map<String, Set<String>> childrenSet = new HashMap<>();
|
||||
Map<String, Set<String>> parentSet = new HashMap<>();
|
||||
|
||||
void addEdge(String parent, String child) {
|
||||
children.computeIfAbsent(parent, k -> new ArrayList<>());
|
||||
parents.computeIfAbsent(child, k -> new ArrayList<>());
|
||||
childrenSet.computeIfAbsent(parent, k -> new HashSet<>());
|
||||
parentSet.computeIfAbsent(child, k -> new HashSet<>());
|
||||
|
||||
if (childrenSet.get(parent).add(child)) {
|
||||
children.get(parent).add(child);
|
||||
}
|
||||
if (parentSet.get(child).add(parent)) {
|
||||
parents.get(child).add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testLoki0001() {
|
||||
int E = 500; // edges from one parent to many children
|
||||
String parent = "root";
|
||||
|
||||
// Warm up
|
||||
for (int w = 0; w < 3; w++) {
|
||||
DagBefore db = new DagBefore();
|
||||
DagAfter da = new DagAfter();
|
||||
for (int i = 0; i < E; i++) {
|
||||
db.addEdge(parent, "child-" + i);
|
||||
da.addEdge(parent, "child-" + i);
|
||||
}
|
||||
}
|
||||
|
||||
int ITER = 200;
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
for (int it = 0; it < ITER; it++) {
|
||||
DagBefore db = new DagBefore();
|
||||
for (int i = 0; i < E; i++) {
|
||||
db.addEdge(parent, "child-" + i);
|
||||
}
|
||||
// Also add duplicates to exercise the contains path
|
||||
for (int i = 0; i < E; i++) {
|
||||
db.addEdge(parent, "child-" + i);
|
||||
}
|
||||
}
|
||||
long befNs = System.nanoTime() - t0;
|
||||
|
||||
t0 = System.nanoTime();
|
||||
for (int it = 0; it < ITER; it++) {
|
||||
DagAfter da = new DagAfter();
|
||||
for (int i = 0; i < E; i++) {
|
||||
da.addEdge(parent, "child-" + i);
|
||||
}
|
||||
for (int i = 0; i < E; i++) {
|
||||
da.addEdge(parent, "child-" + i);
|
||||
}
|
||||
}
|
||||
long aftNs = System.nanoTime() - t0;
|
||||
|
||||
// Correctness check
|
||||
DagBefore db = new DagBefore();
|
||||
DagAfter da = new DagAfter();
|
||||
for (int i = 0; i < E; i++) {
|
||||
db.addEdge(parent, "child-" + i);
|
||||
da.addEdge(parent, "child-" + i);
|
||||
}
|
||||
assert db.children.get(parent).size() == da.children.get(parent).size()
|
||||
: "loki-0001 correctness: children sizes differ";
|
||||
assert db.children.get(parent).equals(da.children.get(parent))
|
||||
: "loki-0001 correctness: children order differs";
|
||||
|
||||
double ratio = (double) befNs / aftNs;
|
||||
System.out.printf("loki-0001 dag-edge-dedup BEFORE=%,dns AFTER=%,dns ratio=%.1fx %s%n",
|
||||
befNs, aftNs, ratio, ratio > 2.0 ? "PASS" : "FAIL");
|
||||
assert ratio > 2.0 : "loki-0001: expected >2x speedup, got " + ratio;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
testLoki0001();
|
||||
System.out.println("\nAll 1 Loki CWE-407 test PASSED.");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue