197 lines
6.2 KiB
Diff
197 lines
6.2 KiB
Diff
# UNDF: UNDF-2026-000000012
|
|
From: CWE-407 patch <patch@undefect.com>
|
|
Date: 2026-03-26
|
|
Subject: [PATCH] ospf: replace SPF candidate sorted-list with binary min-heap
|
|
|
|
CWE-407: Algorithmic Complexity — Insufficient Algorithmic Complexity
|
|
|
|
DEFECT: BIRD-001 — HIGH
|
|
|
|
proto/ospf/rt.c add_cand() maintains oa->cand as a sorted doubly-linked
|
|
list. Insertion finds the sorted position via WALK_LIST — O(n) per call.
|
|
Called once per edge relaxation in ospf_rt_spfa(). Net complexity:
|
|
O(E * V) instead of O((E+V) log V).
|
|
|
|
FIX: replace oa->cand (list) with oa->cand_heap (pointer array, 1-based)
|
|
using BIRD's existing HEAP_* macros from lib/heap.h. Each top_hash_entry
|
|
gains a heap_pos field so that a decrease-key (HEAP_DECREASE) can
|
|
reposition a re-relaxed node in O(log n) without scanning. The heap array
|
|
is stack-allocated via alloca() to match BIRD's existing per-SPF-run
|
|
allocation patterns.
|
|
|
|
Complexity after patch: O((E+V) log V) — textbook Dijkstra.
|
|
|
|
--- a/proto/ospf/topology.h
|
|
+++ b/proto/ospf/topology.h
|
|
@@ -14,10 +14,10 @@ struct top_hash_entry
|
|
{
|
|
snode lsn;
|
|
- node cn; /* For adding into list of candidates
|
|
- in Dijkstra algorithm */
|
|
+ uint heap_pos; /* CWE-407 fix: 1-based position in cand_heap; 0 = not in heap */
|
|
struct top_hash_entry *next; /* Next in hash chain */
|
|
struct ospf_lsa_header lsa;
|
|
|
|
--- a/proto/ospf/ospf.h
|
|
+++ b/proto/ospf/ospf.h
|
|
@@ -261,7 +261,8 @@ struct ospf_area
|
|
node n;
|
|
u32 areaid;
|
|
- list cand; /* List of candidates for RT calc. */
|
|
+ struct top_hash_entry **cand_heap; /* CWE-407 fix: binary min-heap for Dijkstra candidates */
|
|
+ uint cand_num; /* element count (heap is 1-based) */
|
|
struct top_graph *gr; /* LSA graph */
|
|
|
|
--- a/proto/ospf/rt.c
|
|
+++ b/proto/ospf/rt.c
|
|
@@ -10,6 +10,7 @@
|
|
|
|
#include "ospf.h"
|
|
+#include "lib/heap.h" /* CWE-407 fix: binary heap macros */
|
|
|
|
static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint data, uint lif, uint nif);
|
|
static void rt_sync(struct ospf_proto *p);
|
|
@@ -626,12 +627,55 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
|
|
}
|
|
|
|
+/*
|
|
+ * CWE-407 fix: heap comparator and swap callbacks for HEAP_* macros.
|
|
+ *
|
|
+ * min-heap on dist; break ties by preferring router LSAs over network LSAs
|
|
+ * (RFC 2328 section 16.1, step 5: when two candidates have equal distance
|
|
+ * the router LSA vertex is processed first).
|
|
+ */
|
|
+static inline int
|
|
+cand_less(struct top_hash_entry *a, struct top_hash_entry *b)
|
|
+{
|
|
+ if (a->dist != b->dist)
|
|
+ return a->dist < b->dist;
|
|
+ return (a->lsa_type == LSA_T_RT) && (b->lsa_type != LSA_T_RT);
|
|
+}
|
|
+
|
|
+#define CAND_LESS(a, b) cand_less((a), (b))
|
|
+#define CAND_SWAP(heap, i, j, tmp) \
|
|
+ do { \
|
|
+ (tmp) = (heap)[i]; \
|
|
+ (heap)[i] = (heap)[j]; \
|
|
+ (heap)[j] = (tmp); \
|
|
+ (heap)[i]->heap_pos = (i); \
|
|
+ (heap)[j]->heap_pos = (j); \
|
|
+ } while (0)
|
|
+
|
|
+/* Push en onto the heap — O(log n). */
|
|
+static inline void
|
|
+cand_push(struct ospf_area *oa, struct top_hash_entry *en)
|
|
+{
|
|
+ oa->cand_num++;
|
|
+ oa->cand_heap[oa->cand_num] = en;
|
|
+ en->heap_pos = oa->cand_num;
|
|
+ HEAP_INSERT(oa->cand_heap, oa->cand_num,
|
|
+ struct top_hash_entry *, CAND_LESS, CAND_SWAP);
|
|
+}
|
|
+
|
|
+/* Extract and return the minimum-distance candidate — O(log n). */
|
|
+static inline struct top_hash_entry *
|
|
+cand_pop(struct ospf_area *oa)
|
|
+{
|
|
+ struct top_hash_entry *min = oa->cand_heap[1];
|
|
+ HEAP_DELMIN(oa->cand_heap, oa->cand_num,
|
|
+ struct top_hash_entry *, CAND_LESS, CAND_SWAP);
|
|
+ min->heap_pos = 0;
|
|
+ return min;
|
|
+}
|
|
+
|
|
/* RFC 2328 16.1. calculating shortest paths for an area */
|
|
static void
|
|
ospf_rt_spfa(struct ospf_area *oa)
|
|
{
|
|
struct ospf_proto *p = oa->po;
|
|
struct top_hash_entry *act;
|
|
- node *n;
|
|
|
|
if (oa->rt == NULL)
|
|
return;
|
|
@@ -644,21 +688,19 @@ ospf_rt_spfa(struct ospf_area *oa)
|
|
|
|
/* 16.1. (1) */
|
|
- init_list(&oa->cand); /* Empty list of candidates */
|
|
+ /* CWE-407 fix: stack-allocate a 1-based array sized for the full LSA table. */
|
|
+ oa->cand_heap = alloca((oa->gr->hash_size + 2) * sizeof(struct top_hash_entry *));
|
|
+ oa->cand_num = 0;
|
|
oa->trcap = 0;
|
|
|
|
DBG("LSA db prepared, adding me into candidate list.\n");
|
|
|
|
oa->rt->dist = 0;
|
|
oa->rt->color = CANDIDATE;
|
|
- add_head(&oa->cand, &oa->rt->cn);
|
|
+ cand_push(oa, oa->rt); /* CWE-407 fix: O(log n) */
|
|
DBG("RT LSA: rt: %R, id: %R, type: %u\n",
|
|
oa->rt->lsa.rt, oa->rt->lsa.id, oa->rt->lsa_type);
|
|
|
|
- while (!EMPTY_LIST(oa->cand))
|
|
+ while (oa->cand_num > 0)
|
|
{
|
|
- n = HEAD(oa->cand);
|
|
- act = SKIP_BACK(struct top_hash_entry, cn, n);
|
|
- rem_node(n);
|
|
+ act = cand_pop(oa); /* CWE-407 fix: O(log n) extract-min */
|
|
|
|
DBG("Working on LSA: rt: %R, id: %R, type: %u\n",
|
|
act->lsa.rt, act->lsa.id, act->lsa_type);
|
|
@@ -1882,8 +1924,6 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
|
|
{
|
|
struct ospf_proto *p = oa->po;
|
|
- node *prev, *n;
|
|
- int added = 0;
|
|
- struct top_hash_entry *act;
|
|
|
|
/* 16.1. (2b) */
|
|
if (en == NULL)
|
|
@@ -1960,7 +2000,7 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
|
|
if (en->color == CANDIDATE)
|
|
{ /* We found a shorter path — update key in heap */
|
|
- rem_node(&en->cn);
|
|
+ /* CWE-407 fix: decrease-key in O(log n); no list scan needed */
|
|
}
|
|
en->nhs = nhs;
|
|
en->dist = dist;
|
|
@@ -1968,30 +2008,12 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
|
|
en->nhs_reuse = (par->nhs != nhs);
|
|
|
|
- prev = NULL;
|
|
-
|
|
- if (EMPTY_LIST(oa->cand))
|
|
- {
|
|
- add_head(&oa->cand, &en->cn);
|
|
- }
|
|
- else
|
|
- {
|
|
- WALK_LIST(n, oa->cand) /* O(n) — CWE-407 defect */
|
|
- {
|
|
- act = SKIP_BACK(struct top_hash_entry, cn, n);
|
|
- if ((act->dist > dist) ||
|
|
- ((act->dist == dist) && (act->lsa_type == LSA_T_RT)))
|
|
- {
|
|
- if (prev == NULL)
|
|
- add_head(&oa->cand, &en->cn);
|
|
- else
|
|
- insert_node(&en->cn, prev);
|
|
- added = 1;
|
|
- break;
|
|
- }
|
|
- prev = n;
|
|
- }
|
|
-
|
|
- if (!added)
|
|
- {
|
|
- add_tail(&oa->cand, &en->cn);
|
|
- }
|
|
- }
|
|
+ if (en->color == CANDIDATE)
|
|
+ /* CWE-407 fix: decrease-key is O(log n) — dist already updated above */
|
|
+ HEAP_DECREASE(oa->cand_heap, oa->cand_num,
|
|
+ struct top_hash_entry *, CAND_LESS, CAND_SWAP, en->heap_pos);
|
|
+ else
|
|
+ cand_push(oa, en); /* CWE-407 fix: new node, O(log n) */
|
|
}
|