java-topology/defects/libtorrent/unit/LibtorrentTest.java
russell@unturf.com 0b5409ff95 libtorrent-0001/qbittorrent: CWE-407 scan — 1 defect, 1 CLEAN
libtorrent-0001: file_storage::get_or_add_path std::find on m_paths vector O(F*P) MEDIUM 250x
qBittorrent: CLEAN (QSet/QHash throughout for membership tests)
2026-03-30 10:39:32 -04:00

114 lines
3.9 KiB
Java

import java.util.*;
/**
* CWE-407 simulation: libtorrent file_storage::get_or_add_path
*
* Reproduces the O(F*P) linear-scan deduplication defect in
* file_storage::get_or_add_path where std::find scans m_paths
* vector for every file added to a torrent.
*
* libtorrent-0001: get_or_add_path uses std::find on vector O(F*P)
* Fix: unordered_map for O(1) lookup → O(F) total
*/
public class LibtorrentTest {
// === DEFECTIVE: linear scan dedup (std::find on vector) ===
static class FileStorageDefective {
private final List<String> paths = new ArrayList<>();
/** Simulates get_or_add_path with linear scan */
int getOrAddPath(String path) {
// std::find(m_paths.rbegin(), m_paths.rend(), path)
for (int i = paths.size() - 1; i >= 0; i--) {
if (paths.get(i).equals(path)) {
return i;
}
}
int ret = paths.size();
paths.add(path);
return ret;
}
}
// === FIXED: hash map dedup ===
static class FileStorageFixed {
private final List<String> paths = new ArrayList<>();
private final Map<String, Integer> pathIndex = new HashMap<>();
int getOrAddPath(String path) {
Integer idx = pathIndex.get(path);
if (idx != null) return idx;
int ret = paths.size();
paths.add(path);
pathIndex.put(path, ret);
return ret;
}
}
/**
* Simulate adding F files across P unique directory paths.
* Each file's directory is looked up via get_or_add_path.
*/
static long benchmarkDefective(int numFiles, int numPaths) {
FileStorageDefective fs = new FileStorageDefective();
String[] dirs = new String[numPaths];
for (int i = 0; i < numPaths; i++) {
dirs[i] = "dir" + i + "/subdir" + (i % 10);
}
long ops = 0;
for (int f = 0; f < numFiles; f++) {
String dir = dirs[f % numPaths];
// linear scan: worst case scans all P paths
for (int i = fs.paths.size() - 1; i >= 0; i--) {
ops++;
if (fs.paths.get(i).equals(dir)) break;
}
fs.getOrAddPath(dir);
}
return ops;
}
static long benchmarkFixed(int numFiles, int numPaths) {
FileStorageFixed fs = new FileStorageFixed();
String[] dirs = new String[numPaths];
for (int i = 0; i < numPaths; i++) {
dirs[i] = "dir" + i + "/subdir" + (i % 10);
}
long ops = 0;
for (int f = 0; f < numFiles; f++) {
String dir = dirs[f % numPaths];
ops++; // O(1) hash lookup
fs.getOrAddPath(dir);
}
return ops;
}
public static void main(String[] args) {
System.out.println("=== LibtorrentTest: CWE-407 file_storage::get_or_add_path ===\n");
// libtorrent-0001: get_or_add_path linear dedup
int numFiles = 50000;
int numPaths = 500;
long defectOps = benchmarkDefective(numFiles, numPaths);
long fixedOps = benchmarkFixed(numFiles, numPaths);
double ratio = (double) defectOps / fixedOps;
System.out.printf("libtorrent-0001: get_or_add_path linear path dedup%n");
System.out.printf(" F=%d files, P=%d unique paths%n", numFiles, numPaths);
System.out.printf(" defect ops: %,d%n", defectOps);
System.out.printf(" fixed ops: %,d%n", fixedOps);
System.out.printf(" ratio: %.1fx%n", ratio);
boolean pass = ratio > 10.0;
System.out.printf(" result: %s%n%n", pass ? "PASS" : "FAIL");
// Summary
System.out.println("=== SUMMARY ===");
System.out.printf("libtorrent-0001 get_or_add_path O(F*P) -> O(F): %s (%.1fx)%n",
pass ? "PASS" : "FAIL", ratio);
if (!pass) {
System.exit(1);
}
}
}