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
143 lines
5.3 KiB
Java
143 lines
5.3 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for thunderbird-0004: nsImapFlagAndUidState Contains/IndexOf O(N) on sorted array
|
|
*
|
|
* The UID array is maintained sorted. The file already uses binary search
|
|
* (IndexOfFirstElementGt) in one method but uses linear Contains()/IndexOf()
|
|
* in HasMessage() and GetMessageFlagsByUid().
|
|
*
|
|
* Defective: O(N) linear scan per lookup on sorted data
|
|
* Fixed: O(log N) binary search (already available in the codebase)
|
|
*/
|
|
public class ThunderbirdImapFlagUidStateTest {
|
|
|
|
// Sorted UID array (simulating fUids)
|
|
private int[] sortedUids;
|
|
private int[] flags;
|
|
|
|
ThunderbirdImapFlagUidStateTest(int size) {
|
|
sortedUids = new int[size];
|
|
flags = new int[size];
|
|
for (int i = 0; i < size; i++) {
|
|
sortedUids[i] = (i + 1) * 3; // sorted UIDs: 3, 6, 9, ...
|
|
flags[i] = (i % 5 == 0) ? 0x200000 : 0; // some deleted
|
|
}
|
|
}
|
|
|
|
// --- Defective: O(N) linear Contains ---
|
|
boolean hasMessageDefective(int uid) {
|
|
for (int u : sortedUids) {
|
|
if (u == uid) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --- Fixed: O(log N) binary search ---
|
|
boolean hasMessageFixed(int uid) {
|
|
int idx = Arrays.binarySearch(sortedUids, uid);
|
|
return idx >= 0;
|
|
}
|
|
|
|
// --- Defective: O(N) linear IndexOf ---
|
|
int getFlagsByUidDefective(int uid) {
|
|
for (int i = 0; i < sortedUids.length; i++) {
|
|
if (sortedUids[i] == uid) return flags[i];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// --- Fixed: O(log N) binary search ---
|
|
int getFlagsByUidFixed(int uid) {
|
|
int idx = Arrays.binarySearch(sortedUids, uid);
|
|
if (idx >= 0) return flags[idx];
|
|
return -1;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== thunderbird-0004: nsImapFlagAndUidState linear on sorted O(N) ===\n");
|
|
|
|
// Correctness
|
|
ThunderbirdImapFlagUidStateTest state = new ThunderbirdImapFlagUidStateTest(1000);
|
|
// UID 15 = index 4, exists (5*3=15)
|
|
assert state.hasMessageDefective(15) == state.hasMessageFixed(15);
|
|
assert state.hasMessageDefective(15) == true;
|
|
assert state.hasMessageDefective(16) == false;
|
|
assert state.hasMessageFixed(16) == false;
|
|
assert state.getFlagsByUidDefective(15) == state.getFlagsByUidFixed(15);
|
|
System.out.println("PASS correctness: HasMessage and GetFlagsByUid match");
|
|
|
|
// Benchmark HasMessage
|
|
int[] sizes = {1000, 5000, 10000, 50000};
|
|
System.out.println("\n--- HasMessage benchmark ---");
|
|
for (int N : sizes) {
|
|
state = new ThunderbirdImapFlagUidStateTest(N);
|
|
Random rng = new Random(42);
|
|
int[] lookups = new int[1000];
|
|
for (int i = 0; i < lookups.length; i++) {
|
|
lookups[i] = rng.nextInt(N * 3 + 10); // mix of hits and misses
|
|
}
|
|
|
|
// Warmup
|
|
for (int w = 0; w < 3; w++) {
|
|
for (int uid : lookups) {
|
|
state.hasMessageDefective(uid);
|
|
state.hasMessageFixed(uid);
|
|
}
|
|
}
|
|
|
|
int iters = Math.max(1, 500000 / N);
|
|
long t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++)
|
|
for (int uid : lookups) state.hasMessageDefective(uid);
|
|
long defTime = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++)
|
|
for (int uid : lookups) state.hasMessageFixed(uid);
|
|
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=%6d defective=%8dns fixed=%8dns ratio=%.1fx%n",
|
|
status, N, defTime / iters, fixTime / iters, ratio);
|
|
}
|
|
|
|
// Benchmark GetFlagsByUid
|
|
System.out.println("\n--- GetFlagsByUid benchmark ---");
|
|
for (int N : sizes) {
|
|
state = new ThunderbirdImapFlagUidStateTest(N);
|
|
Random rng = new Random(42);
|
|
int[] lookups = new int[1000];
|
|
for (int i = 0; i < lookups.length; i++) {
|
|
lookups[i] = (rng.nextInt(N) + 1) * 3; // valid UIDs
|
|
}
|
|
|
|
// Warmup
|
|
for (int w = 0; w < 3; w++) {
|
|
for (int uid : lookups) {
|
|
state.getFlagsByUidDefective(uid);
|
|
state.getFlagsByUidFixed(uid);
|
|
}
|
|
}
|
|
|
|
int iters = Math.max(1, 500000 / N);
|
|
long t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++)
|
|
for (int uid : lookups) state.getFlagsByUidDefective(uid);
|
|
long defTime = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int r = 0; r < iters; r++)
|
|
for (int uid : lookups) state.getFlagsByUidFixed(uid);
|
|
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=%6d defective=%8dns fixed=%8dns ratio=%.1fx%n",
|
|
status, N, defTime / iters, fixTime / iters, ratio);
|
|
}
|
|
|
|
System.out.println("\nAll tests passed.");
|
|
}
|
|
}
|