clementine: 2 CWE-407 defects; mpd + rhythmbox CLEAN
clementine-0001: LibraryWatcher ScanSubdirectory FindSongByPath O(F*S) + files_on_disk.contains O(S*F) — linear scan with TODO comment, fix with HashMap + HashSet. HIGH severity, 250x at N=1000. clementine-0002: SongSender indexOf(s) O(N^2) in SendAlbum/SendPlaylist/ SendUrls loops — fix with integer counter + QSet for requested_ids. MEDIUM severity, 500x at N=1000. MPD: CLEAN — uses std::set, bitmask arrays, std::map throughout. Rhythmbox: CLEAN — uses g_hash_table for all membership checks. 4/4 unit tests PASS.
This commit is contained in:
parent
a9cd5686fb
commit
669ed13408
6 changed files with 438 additions and 0 deletions
109
defects/clementine/test/ClementineLibraryWatcherTest.java
Normal file
109
defects/clementine/test/ClementineLibraryWatcherTest.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CWE-407 simulation: Clementine LibraryWatcher::ScanSubdirectory
|
||||
* Two defects:
|
||||
* 1. FindSongByPath: linear scan of songs_in_db per file -> O(F*S)
|
||||
* 2. files_on_disk.contains: linear scan per song -> O(S*F)
|
||||
*
|
||||
* Fix: HashMap for song-by-path lookup, HashSet for files_on_disk membership.
|
||||
*/
|
||||
public class ClementineLibraryWatcherTest {
|
||||
|
||||
// --- Defective: linear scan to find song by path ---
|
||||
static String findSongByPathLinear(List<String> songsInDb, String path) {
|
||||
for (String song : songsInDb) {
|
||||
if (song.equals(path)) return song;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static long scanSubdirectoryDefective(List<String> filesOnDisk, List<String> songsInDb) {
|
||||
long ops = 0;
|
||||
|
||||
// Phase 1: For each file on disk, find matching song (linear scan)
|
||||
for (String file : filesOnDisk) {
|
||||
for (String song : songsInDb) {
|
||||
ops++;
|
||||
if (song.equals(file)) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: For each song in DB, check if file exists on disk (linear scan)
|
||||
for (String song : songsInDb) {
|
||||
for (String file : filesOnDisk) {
|
||||
ops++;
|
||||
if (file.equals(song)) break;
|
||||
}
|
||||
}
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
// --- Fixed: HashMap + HashSet ---
|
||||
static long scanSubdirectoryFixed(List<String> filesOnDisk, List<String> songsInDb) {
|
||||
long ops = 0;
|
||||
|
||||
// Build hash map for O(1) song-by-path lookup
|
||||
Map<String, String> songsByPath = new HashMap<>();
|
||||
for (String song : songsInDb) {
|
||||
songsByPath.put(song, song);
|
||||
ops++;
|
||||
}
|
||||
|
||||
// Build hash set for O(1) file-on-disk membership
|
||||
Set<String> filesOnDiskSet = new HashSet<>(filesOnDisk);
|
||||
ops += filesOnDisk.size();
|
||||
|
||||
// Phase 1: For each file, O(1) lookup
|
||||
for (String file : filesOnDisk) {
|
||||
songsByPath.get(file);
|
||||
ops++;
|
||||
}
|
||||
|
||||
// Phase 2: For each song, O(1) contains
|
||||
for (String song : songsInDb) {
|
||||
filesOnDiskSet.contains(song);
|
||||
ops++;
|
||||
}
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int[] sizes = {100, 250, 500, 1000};
|
||||
|
||||
System.out.println("=== Clementine LibraryWatcher ScanSubdirectory CWE-407 Test ===");
|
||||
System.out.println("Defect: FindSongByPath O(F*S) linear scan + files_on_disk.contains O(S*F)");
|
||||
System.out.println();
|
||||
System.out.printf("%-8s %-15s %-15s %-10s %-6s%n",
|
||||
"N", "Defective ops", "Fixed ops", "Ratio", "PASS");
|
||||
|
||||
boolean allPass = true;
|
||||
for (int n : sizes) {
|
||||
// Create matching file lists (worst case: all files exist in DB)
|
||||
List<String> filesOnDisk = new ArrayList<>();
|
||||
List<String> songsInDb = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
String path = "/music/artist/album/track" + String.format("%04d", i) + ".mp3";
|
||||
filesOnDisk.add(path);
|
||||
songsInDb.add(path);
|
||||
}
|
||||
// Shuffle DB order to avoid best-case
|
||||
Collections.shuffle(songsInDb, new Random(42));
|
||||
|
||||
long defectiveOps = scanSubdirectoryDefective(filesOnDisk, songsInDb);
|
||||
long fixedOps = scanSubdirectoryFixed(filesOnDisk, songsInDb);
|
||||
double ratio = (double) defectiveOps / fixedOps;
|
||||
boolean pass = ratio > 5.0;
|
||||
allPass &= pass;
|
||||
|
||||
System.out.printf("%-8d %-15d %-15d %-10.1fx %-6s%n",
|
||||
n, defectiveOps, fixedOps, ratio, pass ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println("Overall: " + (allPass ? "PASS" : "FAIL"));
|
||||
System.exit(allPass ? 0 : 1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue