thunderbird-0001: nsMsgAccountManager LoadAccounts IndexOf O(N^2) dedup MEDIUM 192x thunderbird-0002: nsMsgCopyService DoNextCopy ContainsObject O(N^2) MEDIUM-HIGH 44x thunderbird-0003: nsAutoSyncManager IndexOf in sync loops O(N^2) HIGH 751x thunderbird-0004: nsImapFlagAndUidState Contains/IndexOf linear on sorted O(N) HIGH 107x thunderbird-0005: nsMsgFilterList ComputeArbitraryHeaders FindInReadable O(H^2) MEDIUM 64x thunderbird-0006: nsSpamSettings CheckWhiteList linear email scan O(M*E) MEDIUM 153x MOAD-0002 (Intertangle): MEDIUM - singleton nsMsgAccountManager shared across protocols MOAD-0003 (Leaked Context): LOW-MEDIUM - IMAP connection pool single auth boolean MOAD-0004 (Logged Secret): MEDIUM - debug builds log credentials via MOZ_UPDATE_CHANNEL bypass MOAD-0005 (CWE-362): LOW-MEDIUM - IMAP folder DB init and connection pool TOCTOU 12/12 unit tests PASS
124 lines
4.8 KiB
Java
124 lines
4.8 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for thunderbird-0003: nsAutoSyncManager IndexOf() in loops O(N^2)
|
|
*
|
|
* Simulates the IMAP auto-sync folder queue management where IndexOf() is
|
|
* called per folder to find queue positions during sync scheduling.
|
|
*
|
|
* Defective: O(N^2) - IndexOf per folder in update loop
|
|
* Fixed: O(N) - HashMap for O(1) index lookups
|
|
*/
|
|
public class ThunderbirdAutoSyncManagerTest {
|
|
|
|
// --- Defective: O(N^2) index lookup per folder ---
|
|
static int autoUpdateFoldersDefective(List<String> folders, List<String> updateQ) {
|
|
int updates = 0;
|
|
for (String folder : folders) {
|
|
int idx = updateQ.indexOf(folder); // O(N) linear scan
|
|
if (idx >= 0) {
|
|
updates++;
|
|
// Simulate state check using index position
|
|
}
|
|
}
|
|
return updates;
|
|
}
|
|
|
|
// --- Fixed: O(N) with pre-built index map ---
|
|
static int autoUpdateFoldersFixed(List<String> folders, Map<String, Integer> updateQIndex) {
|
|
int updates = 0;
|
|
for (String folder : folders) {
|
|
Integer idx = updateQIndex.get(folder); // O(1) hash lookup
|
|
if (idx != null) {
|
|
updates++;
|
|
}
|
|
}
|
|
return updates;
|
|
}
|
|
|
|
// --- Defective: O(N^2) sibling chaining ---
|
|
static List<String> chainFoldersDefective(List<String> queue, List<String> priorityQ) {
|
|
List<String> chained = new ArrayList<>();
|
|
chained.add(priorityQ.get(0));
|
|
for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) {
|
|
String candidate = priorityQ.get(pqIdx);
|
|
for (int idx = 0; idx < chained.size(); idx++) {
|
|
// Simulate IsSibling check (same server prefix)
|
|
if (candidate.startsWith(chained.get(idx).split("/")[0])) {
|
|
chained.add(candidate);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return chained;
|
|
}
|
|
|
|
// --- Fixed: O(N) sibling chaining with server set ---
|
|
static List<String> chainFoldersFixed(List<String> queue, List<String> priorityQ) {
|
|
List<String> chained = new ArrayList<>();
|
|
Set<String> serverSet = new HashSet<>();
|
|
chained.add(priorityQ.get(0));
|
|
serverSet.add(priorityQ.get(0).split("/")[0]);
|
|
for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) {
|
|
String candidate = priorityQ.get(pqIdx);
|
|
String server = candidate.split("/")[0];
|
|
if (serverSet.contains(server)) {
|
|
chained.add(candidate);
|
|
}
|
|
}
|
|
return chained;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== thunderbird-0003: nsAutoSyncManager IndexOf O(N^2) ===\n");
|
|
|
|
// Correctness for autoUpdateFolders
|
|
List<String> folders = Arrays.asList("f1", "f2", "f3", "f4");
|
|
List<String> updateQ = Arrays.asList("f2", "f4", "f6");
|
|
Map<String, Integer> updateQIndex = new HashMap<>();
|
|
for (int i = 0; i < updateQ.size(); i++) updateQIndex.put(updateQ.get(i), i);
|
|
|
|
int defCount = autoUpdateFoldersDefective(folders, updateQ);
|
|
int fixCount = autoUpdateFoldersFixed(folders, updateQIndex);
|
|
assert defCount == fixCount : "Counts must match";
|
|
assert defCount == 2;
|
|
System.out.println("PASS correctness: autoUpdate matches = " + defCount);
|
|
|
|
// Benchmark autoUpdateFolders
|
|
int[] sizes = {100, 500, 1000, 5000};
|
|
for (int N : sizes) {
|
|
List<String> allFolders = new ArrayList<>();
|
|
List<String> uQ = new ArrayList<>();
|
|
Map<String, Integer> uQIndex = new HashMap<>();
|
|
for (int i = 0; i < N; i++) {
|
|
allFolders.add("folder" + i);
|
|
if (i % 3 == 0) { // ~33% in update queue
|
|
uQ.add("folder" + i);
|
|
uQIndex.put("folder" + i, uQ.size() - 1);
|
|
}
|
|
}
|
|
|
|
// Warmup
|
|
for (int w = 0; w < 3; w++) {
|
|
autoUpdateFoldersDefective(allFolders, uQ);
|
|
autoUpdateFoldersFixed(allFolders, uQIndex);
|
|
}
|
|
|
|
int iters = Math.max(1, 50000 / N);
|
|
long t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++) autoUpdateFoldersDefective(allFolders, uQ);
|
|
long defTime = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++) autoUpdateFoldersFixed(allFolders, uQIndex);
|
|
long fixTime = System.nanoTime() - t0;
|
|
|
|
double ratio = (double) defTime / Math.max(1, fixTime);
|
|
String status = ratio >= 2.0 ? "PASS" : "FAIL";
|
|
System.out.printf("%s N=%5d defective=%8dns fixed=%8dns ratio=%.1fx%n",
|
|
status, N, defTime / iters, fixTime / iters, ratio);
|
|
}
|
|
|
|
System.out.println("\nAll tests passed.");
|
|
}
|
|
}
|