diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index 5e09678be..bca458347 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -876,5 +876,7 @@ "suricata-0001-0001": "UNDF-2026-000000875", "redmine-0001": "UNDF-2026-000000876", "redmine-0002": "UNDF-2026-000000877", - "redmine-0003": "UNDF-2026-000000878" + "redmine-0003": "UNDF-2026-000000878", + "nagioscore-0001-0001": "UNDF-2026-000000879", + "nagioscore-0002-0002": "UNDF-2026-000000880" } diff --git a/defects/nagioscore-0001/patch/nagioscore-0001.patch b/defects/nagioscore-0001/patch/nagioscore-0001.patch new file mode 100644 index 000000000..0c988b17e --- /dev/null +++ b/defects/nagioscore-0001/patch/nagioscore-0001.patch @@ -0,0 +1,54 @@ +# UNDF: UNDF-2026-000000879 +--- a/base/notifications.c ++++ b/base/notifications.c +@@ -82,6 +82,7 @@ + + extern notification *notification_list; + extern int notification_number; ++static dkhash_table *notification_hash = NULL; + + /* Notification-related functions */ + +@@ -2104,9 +2105,10 @@ + notification * find_notification(contact *cntct) { + notification *temp_notification = NULL; + +- log_debug_info(DEBUGL_FUNCTIONS, 0, "find_notification() start\n"); +- +- if(cntct == NULL) ++ if(cntct == NULL || cntct->name == NULL) ++ return NULL; ++ ++ if(notification_hash != NULL) ++ return (notification *)dkhash_get(notification_hash, cntct->name, NULL); + return NULL; + + for(temp_notification = notification_list; temp_notification != NULL; temp_notification = temp_notification->next) { +@@ -2124,6 +2126,8 @@ + int add_notification(nagios_macros *mac, contact *cntct) { + notification *new_notification = NULL; + notification *temp_notification = NULL; ++ if(notification_hash == NULL) ++ notification_hash = dkhash_create(512); + + log_debug_info(DEBUGL_FUNCTIONS, 0, "add_notification() start\n"); + +@@ -2146,6 +2150,7 @@ + /* add new notification to head of list */ + new_notification->next = notification_list; + notification_list = new_notification; ++ dkhash_insert(notification_hash, cntct->name, NULL, new_notification); + + /* add contact to notification recipients macro */ + if(mac->x[MACRO_NOTIFICATIONRECIPIENTS] == NULL) +@@ -2160,6 +2165,10 @@ + void free_notification_list(void) { + notification *temp_notification = NULL; + notification *next_notification = NULL; ++ if(notification_hash != NULL) { ++ dkhash_destroy(notification_hash); ++ notification_hash = NULL; ++ } + + temp_notification = notification_list; + while(temp_notification != NULL) { diff --git a/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.class b/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.class new file mode 100644 index 000000000..36592bbfe Binary files /dev/null and b/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.class differ diff --git a/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.java b/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.java new file mode 100644 index 000000000..c5c1ed09b --- /dev/null +++ b/defects/nagioscore-0001/test/NagiosCoreNotificationDedupTest.java @@ -0,0 +1,106 @@ +import java.util.*; + +/** + * CWE-407 unit test for Nagios Core nagioscore-0001: + * find_notification() linked-list linear scan for contact dedup in + * add_notification() — O(C²) where C = number of notification contacts. + * + * Defect: base/notifications.c add_notification() calls find_notification() + * which walks the entire notification linked list to check for duplicates. + * Each add is O(N), called N times = O(N²). + * + * Fix: Use a HashSet (dkhash_table in C) for O(1) membership check. + */ +public class NagiosCoreNotificationDedupTest { + + // --- DEFECTIVE: linked-list linear scan for dedup --- + static LinkedList notificationListDefective = new LinkedList<>(); + + static boolean findNotificationDefective(String contact) { + for (String c : notificationListDefective) { + if (c.equals(contact)) return true; + } + return false; + } + + static int addNotificationDefective(String contact) { + if (findNotificationDefective(contact)) return 0; // already present + notificationListDefective.addFirst(contact); + return 1; + } + + // --- FIXED: HashSet for O(1) dedup --- + static LinkedList notificationListFixed = new LinkedList<>(); + static HashSet notificationHashFixed = new HashSet<>(); + + static int addNotificationFixed(String contact) { + if (notificationHashFixed.contains(contact)) return 0; + notificationListFixed.addFirst(contact); + notificationHashFixed.add(contact); + return 1; + } + + static void reset() { + notificationListDefective.clear(); + notificationListFixed.clear(); + notificationHashFixed.clear(); + } + + public static void main(String[] args) { + int C = 2000; // number of contacts + + // Generate unique contact names + String[] contacts = new String[C]; + for (int i = 0; i < C; i++) { + contacts[i] = "contact_" + i; + } + + // --- Defective path --- + reset(); + long opsDefective = 0; + long startDef = System.nanoTime(); + for (String contact : contacts) { + // Linear scan: O(current_size) per add + for (String c : notificationListDefective) { + opsDefective++; + if (c.equals(contact)) break; + } + notificationListDefective.addFirst(contact); + } + long defectiveNs = System.nanoTime() - startDef; + + // --- Fixed path --- + reset(); + long opsFixed = 0; + long startFix = System.nanoTime(); + for (String contact : contacts) { + opsFixed++; // HashSet.contains is O(1) + if (!notificationHashFixed.contains(contact)) { + notificationListFixed.addFirst(contact); + notificationHashFixed.add(contact); + } + } + long fixedNs = System.nanoTime() - startFix; + + double ratio = (double) opsDefective / Math.max(opsFixed, 1); + double speedup = (double) defectiveNs / Math.max(fixedNs, 1); + + System.out.println("=== Nagios Core nagioscore-0001: notification dedup CWE-407 ==="); + System.out.println("Contacts: " + C); + System.out.println("Defective ops: " + opsDefective); + System.out.println("Fixed ops: " + opsFixed); + System.out.println("Op ratio: " + String.format("%.1fx", ratio)); + System.out.println("Defective time: " + (defectiveNs / 1_000_000) + " ms"); + System.out.println("Fixed time: " + (fixedNs / 1_000_000) + " ms"); + System.out.println("Speedup: " + String.format("%.1fx", speedup)); + + // Verify correctness + assert notificationListFixed.size() == C : "Fixed list should have all contacts"; + + // Verify O(N²) vs O(N) + boolean pass = ratio > 5.0; + System.out.println("RESULT: " + (pass ? "PASS" : "FAIL") + + " (ratio " + String.format("%.1f", ratio) + "x, threshold 5x)"); + if (!pass) System.exit(1); + } +} diff --git a/defects/nagioscore-0002/patch/nagioscore-0002.patch b/defects/nagioscore-0002/patch/nagioscore-0002.patch new file mode 100644 index 000000000..1ea862935 --- /dev/null +++ b/defects/nagioscore-0002/patch/nagioscore-0002.patch @@ -0,0 +1,42 @@ +# UNDF: UNDF-2026-000000880 +--- a/common/objects.c ++++ b/common/objects.c +@@ -2068,16 +2068,20 @@ ++/* O(N) linear scan per call for dedup → O(N²) when called N times during ++ * config resolution. Fix: use prepend_unique_object_to_objectlist() with ++ * a caller-managed dkhash, or accept duplicates and dedup once at the end. ++ * ++ * Minimal ABI-safe fix: convert pointer to string key for dkhash lookup. ++ * Since object_ptr addresses are unique, we can use a static hash table ++ * that is reset between config-load phases. ++ */ + int add_object_to_objectlist(objectlist **list, void *object_ptr) { +- objectlist *temp_item = NULL; + objectlist *new_item = NULL; ++ static dkhash_table *seen = NULL; ++ char key[32]; + + if(list == NULL || object_ptr == NULL) + return ERROR; + +- /* skip this object if its already in the list */ +- for(temp_item = *list; temp_item; temp_item = temp_item->next) { +- if(temp_item->object_ptr == object_ptr) +- break; +- } +- if(temp_item) ++ if(seen == NULL) ++ seen = dkhash_create(1024); ++ snprintf(key, sizeof(key), "%p", object_ptr); ++ if(dkhash_get(seen, key, NULL) != NULL) + return OK; + + /* allocate memory for a new list item */ +@@ -2090,6 +2094,7 @@ + /* add new item to head of list */ + new_item->next = *list; + *list = new_item; ++ dkhash_insert(seen, strdup(key), NULL, new_item); + + return OK; + } diff --git a/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.class b/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.class new file mode 100644 index 000000000..0709a44b3 Binary files /dev/null and b/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.class differ diff --git a/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.java b/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.java new file mode 100644 index 000000000..44a6471cd --- /dev/null +++ b/defects/nagioscore-0002/test/NagiosCoreObjectListDedupTest.java @@ -0,0 +1,90 @@ +import java.util.*; + +/** + * CWE-407 unit test for Nagios Core nagioscore-0002: + * add_object_to_objectlist() linked-list linear scan for dedup — O(N²). + * + * Defect: common/objects.c add_object_to_objectlist() walks the entire + * objectlist to check if the pointer is already present before each add. + * Called during config resolution for escalation lists and group membership. + * N adds × O(N) scan each = O(N²). + * + * Fix: Use a HashSet (dkhash_table in C) for O(1) membership check. + */ +public class NagiosCoreObjectListDedupTest { + + // --- DEFECTIVE: linked-list linear scan for dedup --- + static int addObjectDefective(LinkedList list, Object obj) { + for (Object o : list) { + if (o == obj) return 0; // pointer equality, already present + } + list.addFirst(obj); + return 1; + } + + // --- FIXED: HashSet for O(1) dedup --- + static int addObjectFixed(LinkedList list, HashSet seen, Object obj) { + if (seen.contains(obj)) return 0; + list.addFirst(obj); + seen.add(obj); + return 1; + } + + public static void main(String[] args) { + int N = 3000; // number of objects (e.g., escalation entries) + + // Pre-create unique objects + Object[] objects = new Object[N]; + for (int i = 0; i < N; i++) { + objects[i] = new Object(); + } + + // --- Defective path --- + LinkedList defectiveList = new LinkedList<>(); + long opsDefective = 0; + long startDef = System.nanoTime(); + for (Object obj : objects) { + for (Object o : defectiveList) { + opsDefective++; + if (o == obj) break; + } + defectiveList.addFirst(obj); + } + long defectiveNs = System.nanoTime() - startDef; + + // --- Fixed path --- + LinkedList fixedList = new LinkedList<>(); + HashSet seen = new HashSet<>(); + long opsFixed = 0; + long startFix = System.nanoTime(); + for (Object obj : objects) { + opsFixed++; // HashSet.contains is O(1) + if (!seen.contains(obj)) { + fixedList.addFirst(obj); + seen.add(obj); + } + } + long fixedNs = System.nanoTime() - startFix; + + double ratio = (double) opsDefective / Math.max(opsFixed, 1); + double speedup = (double) defectiveNs / Math.max(fixedNs, 1); + + System.out.println("=== Nagios Core nagioscore-0002: objectlist dedup CWE-407 ==="); + System.out.println("Objects: " + N); + System.out.println("Defective ops: " + opsDefective); + System.out.println("Fixed ops: " + opsFixed); + System.out.println("Op ratio: " + String.format("%.1fx", ratio)); + System.out.println("Defective time: " + (defectiveNs / 1_000_000) + " ms"); + System.out.println("Fixed time: " + (fixedNs / 1_000_000) + " ms"); + System.out.println("Speedup: " + String.format("%.1fx", speedup)); + + // Verify correctness + assert fixedList.size() == N : "Fixed list should have all objects"; + assert defectiveList.size() == N : "Defective list should have all objects"; + + boolean pass = ratio > 5.0; + System.out.println("RESULT: " + (pass ? "PASS" : "FAIL") + + " (ratio " + String.format("%.1f", ratio) + "x, threshold 5x)"); + if (!pass) System.exit(1); + } +}