package unit; import java.util.*; /** * weechat-0003 — CWE-407: irc_channel_search O(C) linked-list scan, no hash index * * Models src/plugins/irc/irc-channel.c irc_channel_search(): * Slow: linear walk of server->channels linked list → O(C) * called ~30+ times per IRC protocol message * Fast: HashMap → O(1) per lookup * * Impact: bots/clients joined to hundreds of channels see O(C × M) per message * where C = channels, M = channel search calls per message handler. */ public class WeechatChannelSearchTest { static class IrcChannel { final String name; IrcChannel next; IrcChannel(String name) { this.name = name.toLowerCase(); } } static class IrcServer { IrcChannel channels; // linked list head (slow) int channelCount; final Map channelsMap = new HashMap<>(); // fast } // Build linked list of C channels static void buildServer(IrcServer server, int C) { server.channels = null; server.channelCount = C; for (int i = C - 1; i >= 0; i--) { IrcChannel ch = new IrcChannel("#channel" + i); ch.next = server.channels; server.channels = ch; server.channelsMap.put(ch.name, ch); } } // --- SLOW: O(C) linear scan (defect) --- static IrcChannel channelSearchSlow(IrcServer server, String name) { String lower = name.toLowerCase(); for (IrcChannel ch = server.channels; ch != null; ch = ch.next) { if (ch.name.equals(lower)) return ch; } return null; } // --- FAST: O(1) hash lookup (fix) --- static IrcChannel channelSearchFast(IrcServer server, String name) { return server.channelsMap.get(name.toLowerCase()); } // Simulate a protocol message handler that calls irc_channel_search N times static long simulateMessageHandlerSlow(IrcServer server, String[] targets, int callsPerTarget) { long ops = 0; for (String target : targets) { for (int i = 0; i < callsPerTarget; i++) { // each channelSearchSlow = O(C) — count worst-case ops String lower = target.toLowerCase(); for (IrcChannel ch = server.channels; ch != null; ch = ch.next) { ops++; if (ch.name.equals(lower)) break; } } } return ops; } static long simulateMessageHandlerFast(IrcServer server, String[] targets, int callsPerTarget) { long ops = 0; for (String target : targets) { for (int i = 0; i < callsPerTarget; i++) { ops++; // O(1) hash lookup server.channelsMap.get(target.toLowerCase()); } } return ops; } public static void main(String[] args) { System.out.println("weechat-0003 CWE-407: irc_channel_search O(C) vs O(1)"); System.out.println("======================================================="); IrcServer server = new IrcServer(); // Parameters: C channels, M=3 search calls per message handler (conservative) int[][] params = { {100, 3}, {500, 5}, {1000, 10} }; for (int[] p : params) { int C = p[0], callsPerMsg = p[1]; buildServer(server, C); // Simulate 100 incoming IRC messages each needing callsPerMsg channel lookups String[] targets = new String[100]; for (int i = 0; i < 100; i++) targets[i] = "#channel" + (i % C); // random channels long slowOps = simulateMessageHandlerSlow(server, targets, callsPerMsg); long fastOps = simulateMessageHandlerFast(server, targets, callsPerMsg); double ratio = (double) slowOps / fastOps; System.out.printf(" C=%4d channels, %d lookups/msg, 100 msgs: slow=%,7d ops fast=%,5d ops speedup=%.0fx%n", C, callsPerMsg, slowOps, fastOps, ratio); assert ratio >= (double) C / 4 : "Expected speedup >= " + (C/4) + "x but got " + ratio + " (C=" + C + ")"; } System.out.println("\nPASS"); } }