weechat-0001: nicklist dedup O(N^2) linear search on join
gui_nicklist_add_nick calls gui_nicklist_search_nick which linearly scans the nick linked list for each add. Joining a 2000-user IRC channel causes O(N^2) work. Fix: hash table for O(1) dedup. 999x ratio.
This commit is contained in:
parent
53ce6d7ab6
commit
128d349072
3 changed files with 100 additions and 0 deletions
27
defects/weechat-0001/patch/weechat-0001.patch
Normal file
27
defects/weechat-0001/patch/weechat-0001.patch
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
--- a/src/gui/gui-nicklist.c
|
||||
+++ b/src/gui/gui-nicklist.c
|
||||
@@ -596,7 +596,12 @@
|
||||
*
|
||||
* Return pointer to new nick, NULL if error.
|
||||
*/
|
||||
-
|
||||
+/*
|
||||
+ * CWE-407: gui_nicklist_search_nick() performs O(N) linear scan
|
||||
+ * through the nick linked list for dedup check on every add.
|
||||
+ * When bulk-adding N nicks (e.g., joining a large IRC channel),
|
||||
+ * this yields O(N^2) total work.
|
||||
+ * Fix: maintain a hash table of nick names per buffer for O(1) lookup.
|
||||
+ */
|
||||
struct t_gui_nick *
|
||||
gui_nicklist_add_nick (struct t_gui_buffer *buffer, long long id,
|
||||
struct t_gui_nick_group *group,
|
||||
@@ -604,7 +609,8 @@
|
||||
{
|
||||
struct t_gui_nick *new_nick;
|
||||
|
||||
- if (!buffer || !name || gui_nicklist_search_nick (buffer, NULL, name))
|
||||
+ /* O(N) linear scan for dedup => O(N^2) when adding N nicks */
|
||||
+ if (!buffer || !name || gui_nicklist_search_nick (buffer, NULL, name))
|
||||
return NULL;
|
||||
|
||||
new_nick = malloc (sizeof (*new_nick));
|
||||
BIN
defects/weechat-0001/test/WeechatNicklistDedupTest.class
Normal file
BIN
defects/weechat-0001/test/WeechatNicklistDedupTest.class
Normal file
Binary file not shown.
73
defects/weechat-0001/test/WeechatNicklistDedupTest.java
Normal file
73
defects/weechat-0001/test/WeechatNicklistDedupTest.java
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* CWE-407 unit test for WeeChat gui-nicklist.c nick dedup defect.
|
||||
*
|
||||
* Defect: gui_nicklist_add_nick() calls gui_nicklist_search_nick() which
|
||||
* performs O(N) linear scan through the nick linked list for each add.
|
||||
* When bulk-adding N nicks (e.g., joining a large IRC channel with
|
||||
* thousands of users), this yields O(N^2) total work.
|
||||
*
|
||||
* Fix: Maintain a hash table (name -> nick) per buffer for O(1) dedup.
|
||||
*
|
||||
* This test models the defect pattern in Java:
|
||||
* - Defective: LinkedList + contains() for dedup on each add
|
||||
* - Fixed: HashSet for O(1) dedup
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
public class WeechatNicklistDedupTest {
|
||||
|
||||
/** Defective: linear scan through existing nicks for dedup */
|
||||
static long defective(String[] nicks) {
|
||||
LinkedList<String> nicklist = new LinkedList<>();
|
||||
long ops = 0;
|
||||
for (String nick : nicks) {
|
||||
// gui_nicklist_search_nick_name: linear scan O(N)
|
||||
ops += nicklist.size();
|
||||
if (!nicklist.contains(nick)) {
|
||||
nicklist.add(nick);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/** Fixed: hash set for O(1) dedup */
|
||||
static long fixed(String[] nicks) {
|
||||
HashSet<String> nickSet = new HashSet<>();
|
||||
LinkedList<String> nicklist = new LinkedList<>();
|
||||
long ops = 0;
|
||||
for (String nick : nicks) {
|
||||
ops++; // O(1) hash lookup
|
||||
if (nickSet.add(nick)) {
|
||||
nicklist.add(nick);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Simulate joining a large IRC channel with 2000 unique users
|
||||
int N = 2000;
|
||||
String[] nicks = new String[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
nicks[i] = "user_" + i;
|
||||
}
|
||||
|
||||
long defOps = defective(nicks);
|
||||
long fixOps = fixed(nicks);
|
||||
|
||||
double ratio = (double) defOps / fixOps;
|
||||
|
||||
System.out.println("=== WeeChat gui-nicklist.c Nick Dedup Test ===");
|
||||
System.out.println("Nicks added: " + N);
|
||||
System.out.println("Defective ops: " + defOps);
|
||||
System.out.println("Fixed ops: " + fixOps);
|
||||
System.out.printf("Ratio (defective/fixed): %.1fx%n", ratio);
|
||||
|
||||
// Verify: defective should be significantly more expensive
|
||||
boolean pass = ratio >= 5.0;
|
||||
System.out.println("RESULT: " + (pass ? "PASS" : "FAIL")
|
||||
+ " (ratio >= 5.0 required)");
|
||||
|
||||
if (!pass) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue