diff --git a/defects/hurd/hurd-0004.md b/defects/hurd/hurd-0004.md index 3c368ebbf..6ef4782ea 100644 --- a/defects/hurd/hurd-0004.md +++ b/defects/hurd/hurd-0004.md @@ -1,32 +1,20 @@ -# hurd-0004 — CWE-407: check_owner → check_uid — O(nuids²) double UID scan +# hurd-0004 — CLEAN SCAN: check_owner / check_uid — no O(N²) pattern -**Severity:** LOW-MEDIUM -**Files:** `proc/info.c` lines ~47–67, `proc/mgt.c` lines ~84–92 +**Status:** CLEAN — no UNDF ID assigned +**Files inspected:** `proc/info.c`, `proc/mgt.c` **Functions:** `check_owner`, `check_uid` -**CWE:** CWE-407 Algorithmic Complexity -## Defect +## Finding -`check_owner(proc1, proc2)` verifies proc1 holds every UID proc2 holds. Outer loop over proc2's UIDs, inner `check_uid()` linearly scans proc1's UIDs. +Ticket was written speculatively. Actual `check_owner` (proc/info.c:41-47) does NOT loop over proc2's UIDs — it checks `proc2->p_owner`, a single UID, by calling `check_uid()` once. Total cost: O(nuids_proc1), which is O(N) for N ≤ 64. ```c -// check_owner — proc/info.c ~65 -for (size_t i = 0; i < proc2->p_id->i_nuids; i++) - if (!check_uid(proc1, proc2->p_id->i_uids[i])) // O(k) linear scan - return 0; - -// check_uid — proc/mgt.c ~88 -for (i = 0; i < p->p_id->i_nuids; i++) - if (p->p_id->i_uids[i] == uid || p->p_id->i_uids[i] == 0) - return 1; +// Actual check_owner — O(N), not O(N²) +int check_owner (struct proc *proc1, struct proc *proc2) { + return proc2->p_noowner + ? check_uid (proc1, 0) || proc1->p_login == proc2->p_login + : check_uid (proc1, proc2->p_owner); // single call, not a loop +} ``` -**Complexity:** O(nuids_proc2 × nuids_proc1). With N = total UIDs per process, O(N²). - -## Scale - -N is small in practice (≤64 UIDs), but called on every `proc_pid2task`, `proc_pid2proc`, `proc_proc2task` — every `kill()`, `waitpid()`, `ptrace()`, and task port request. - -## Fix - -Sort proc1's UID array once and use binary search for O(log N) lookup per check, giving O(N log N) total. Or maintain a small bitset for O(1) lookup when UID range is bounded. +No O(N×k) amplifier exists. Not a CWE-407 defect. No patch, no UNDF ID. diff --git a/defects/hurd/hurd-0005.md b/defects/hurd/hurd-0005.md index c8a41dbbe..dcf74d46a 100644 --- a/defects/hurd/hurd-0005.md +++ b/defects/hurd/hurd-0005.md @@ -1,4 +1,4 @@ -# hurd-0005 — CWE-407: _ports_bucket_class_iterate — O(total_ports) global scan +# hurd-0005 — CWE-407: _ports_bucket_class_iterate — O(total_ports) global scan [PATCHED] **Severity:** MEDIUM **File:** `libports/bucket-iterate.c` lines ~41–80 @@ -31,3 +31,14 @@ Total ports in a busy Hurd system: potentially thousands (one per open file desc ## Fix Maintain a per-class linked list or per-class hash table in `port_class`. `ports_class_iterate` iterates only its own members in O(class_size) rather than O(total_ports). This is what the source comment already asks for. + +## Patch + +`patch/0003-CWE-407-hurd-0005-per-class-port-list.patch` + +- `port_info`: added `class_next` / `class_prevp` intrusive list fields +- `port_class`: added `class_ports` head pointer +- `create-internal.c` + `import-port.c`: prepend to class list under `_ports_htable_lock` wrlock +- `complete-deallocate.c`: splice out of class list under same wrlock +- `bucket-iterate.c`: class-path walks `class->class_ports` in O(class_size); NULL-class (bucket iterate) still uses `HURD_IHASH_ITERATE` +- Upper-bound malloc uses `ht->nr_items` (always ≥ class size) to avoid race with `class->count` (updated under separate `_ports_lock` after htable_lock released) diff --git a/defects/hurd/patch/0001-CWE-407-hurd-0001-hurd-0006-fix-O-N-M-k-auth-verify-.patch b/defects/hurd/patch/0001-CWE-407-hurd-0001-hurd-0006-fix-O-N-M-k-auth-verify-.patch index 248ffc7f1..72e6b8434 100644 --- a/defects/hurd/patch/0001-CWE-407-hurd-0001-hurd-0006-fix-O-N-M-k-auth-verify-.patch +++ b/defects/hurd/patch/0001-CWE-407-hurd-0001-hurd-0006-fix-O-N-M-k-auth-verify-.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000001244 From c05586601073629e48ed886fbdaa12b9c4c80313 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sat, 4 Apr 2026 20:31:12 -0400 diff --git a/defects/hurd/patch/0002-CWE-407-hurd-0002-hurd-0003-replace-_ports_notificat.patch b/defects/hurd/patch/0002-CWE-407-hurd-0002-hurd-0003-replace-_ports_notificat.patch index 1191e0fff..8cee11fd5 100644 --- a/defects/hurd/patch/0002-CWE-407-hurd-0002-hurd-0003-replace-_ports_notificat.patch +++ b/defects/hurd/patch/0002-CWE-407-hurd-0002-hurd-0003-replace-_ports_notificat.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000001245 From 25aa731e690c799837c21c09db0d047b811e79c3 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sat, 4 Apr 2026 20:31:12 -0400 diff --git a/defects/hurd/patch/0003-CWE-407-hurd-0005-per-class-port-list.patch b/defects/hurd/patch/0003-CWE-407-hurd-0005-per-class-port-list.patch new file mode 100644 index 000000000..4f6c6cbdd --- /dev/null +++ b/defects/hurd/patch/0003-CWE-407-hurd-0005-per-class-port-list.patch @@ -0,0 +1,184 @@ +From f5f781fa2e85e407ac6742df85e001ba3e5e7342 Mon Sep 17 00:00:00 2001 +From: "russell@unturf.com" +Date: Sun, 5 Apr 2026 15:56:05 -0400 +Subject: [PATCH] CWE-407 (hurd-0005): per-class port list for O(class_size) + class iteration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ports_class_iterate previously called _ports_bucket_class_iterate which +scanned the entire _ports_htable filtering by class pointer — O(total_ports) +even when only a small class subset was needed. The source code itself +carried the comment: "This is obscenely inefficient." + +Fix: add a per-class intrusive linked list (class_ports head in port_class, +class_next/class_prevp in port_info) protected by _ports_htable_lock. +create-internal.c and import-port.c prepend to this list under the existing +_ports_htable_lock wrlock. complete-deallocate.c removes from it under the +same wrlock. _ports_bucket_class_iterate now walks class->class_ports +directly when class != NULL — O(class_size) — instead of scanning all ports. + +The malloc upper-bound uses ht->nr_items (total ports, always >= class size) +rather than class->count to avoid a race: class->count is incremented under +_ports_lock after _ports_htable_lock is released, so it may lag one insert +behind the list. The existing realloc trims unused slots. +--- + libports/bucket-iterate.c | 32 ++++++++++++++++++++++---------- + libports/complete-deallocate.c | 6 ++++++ + libports/create-internal.c | 9 +++++++++ + libports/import-port.c | 8 ++++++++ + libports/ports.h | 8 ++++++++ + 5 files changed, 53 insertions(+), 10 deletions(-) + +diff --git a/libports/bucket-iterate.c b/libports/bucket-iterate.c +index b021b99e..eea8d3c4 100644 +--- a/libports/bucket-iterate.c ++++ b/libports/bucket-iterate.c +@@ -29,21 +29,24 @@ _ports_bucket_class_iterate (struct hurd_ihash *ht, + struct port_class *class, + error_t (*fun)(void *)) + { +- /* This is obscenely ineffecient. ihash and ports need to cooperate +- more closely to do it efficiently. */ + void **p; + size_t i, n, nr_items; + error_t err; + + pthread_rwlock_rdlock (&_ports_htable_lock); + +- if (ht->nr_items == 0) ++ /* Upper bound for malloc: total ports in the htable is always >= any ++ class subset. class->count is updated under _ports_lock (separate ++ from _ports_htable_lock), so it may lag slightly; using ht->nr_items ++ avoids a potential overflow. The realloc below trims unused slots. */ ++ nr_items = ht->nr_items; ++ ++ if (nr_items == 0) + { + pthread_rwlock_unlock (&_ports_htable_lock); + return 0; + } + +- nr_items = ht->nr_items; + p = malloc (nr_items * sizeof *p); + if (p == NULL) + { +@@ -52,17 +55,26 @@ _ports_bucket_class_iterate (struct hurd_ihash *ht, + } + + n = 0; +- HURD_IHASH_ITERATE (ht, arg) ++ if (class != NULL) + { +- struct port_info *const pi = arg; +- +- if (class == 0 || pi->class == class) ++ /* CWE-407 fix (hurd-0005): O(class_size) iteration via per-class ++ member list instead of O(total_ports) global hash scan. */ ++ for (struct port_info *pi = class->class_ports; pi; pi = pi->class_next) ++ { ++ refcounts_ref (&pi->refcounts, NULL); ++ p[n++] = pi; ++ } ++ } ++ else ++ { ++ HURD_IHASH_ITERATE (ht, arg) + { ++ struct port_info *const pi = arg; + refcounts_ref (&pi->refcounts, NULL); +- p[n] = pi; +- n++; ++ p[n++] = pi; + } + } ++ + pthread_rwlock_unlock (&_ports_htable_lock); + + if (n != 0 && n != nr_items) +diff --git a/libports/complete-deallocate.c b/libports/complete-deallocate.c +index e3123b26..0e932cc8 100644 +--- a/libports/complete-deallocate.c ++++ b/libports/complete-deallocate.c +@@ -45,6 +45,12 @@ _ports_complete_deallocate (struct port_info *pi) + + hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry); + hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry); ++ ++ /* CWE-407 fix (hurd-0005): remove from per-class member list. */ ++ if (pi->class_next) ++ pi->class_next->class_prevp = pi->class_prevp; ++ *pi->class_prevp = pi->class_next; ++ + pthread_rwlock_unlock (&_ports_htable_lock); + + mach_port_mod_refs (mach_task_self (), pi->port_right, +diff --git a/libports/create-internal.c b/libports/create-internal.c +index d79dc78f..14cc636a 100644 +--- a/libports/create-internal.c ++++ b/libports/create-internal.c +@@ -94,6 +94,15 @@ _ports_create_port_internal (struct port_class *class, + pthread_rwlock_unlock (&_ports_htable_lock); + goto lose; + } ++ ++ /* CWE-407 fix (hurd-0005): prepend to per-class member list so ++ ports_class_iterate can walk O(class_size) instead of O(total_ports). */ ++ pi->class_next = class->class_ports; ++ pi->class_prevp = &class->class_ports; ++ if (class->class_ports) ++ class->class_ports->class_prevp = &pi->class_next; ++ class->class_ports = pi; ++ + pthread_rwlock_unlock (&_ports_htable_lock); + + bucket->count++; +diff --git a/libports/import-port.c b/libports/import-port.c +index 2c638e18..b391240c 100644 +--- a/libports/import-port.c ++++ b/libports/import-port.c +@@ -88,6 +88,14 @@ ports_import_port (struct port_class *class, struct port_bucket *bucket, + pthread_rwlock_unlock (&_ports_htable_lock); + goto lose; + } ++ ++ /* CWE-407 fix (hurd-0005): prepend to per-class member list. */ ++ pi->class_next = class->class_ports; ++ pi->class_prevp = &class->class_ports; ++ if (class->class_ports) ++ class->class_ports->class_prevp = &pi->class_next; ++ class->class_ports = pi; ++ + pthread_rwlock_unlock (&_ports_htable_lock); + + bucket->count++; +diff --git a/libports/ports.h b/libports/ports.h +index 1b6705e3..6942b4ab 100644 +--- a/libports/ports.h ++++ b/libports/ports.h +@@ -57,6 +57,11 @@ struct port_info + struct port_bucket *bucket; + hurd_ihash_locp_t hentry; + hurd_ihash_locp_t ports_htable_entry; ++ /* CWE-407 fix (hurd-0005): per-class member list for O(class_size) ++ class iteration instead of O(total_ports) global scan. ++ Protected by _ports_htable_lock. */ ++ struct port_info *class_next; ++ struct port_info **class_prevp; + }; + typedef struct port_info *port_info_t; + +@@ -90,6 +95,9 @@ struct port_class + int rpcs; + int count; + void (*clean_routine) (void *); ++ /* CWE-407 fix (hurd-0005): head of per-class member list. ++ Protected by _ports_htable_lock. */ ++ struct port_info *class_ports; + void (*dropweak_routine) (void *); + struct ports_msg_id_range *uninhibitable_rpcs; + }; +-- +2.43.0 + diff --git a/docs/tickets/hurd-0001.md b/docs/tickets/hurd-0001.md new file mode 100644 index 000000000..cdd1ef9e8 --- /dev/null +++ b/docs/tickets/hurd-0001.md @@ -0,0 +1,31 @@ +# hurd-0001 — CWE-407: S_auth_makeauth — O(N×M×k) UID verification + +**Severity:** HIGH +**File:** `auth/auth.c` lines ~162–216 +**Function:** `S_auth_makeauth` +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +Four nested loops over `nauths × n{e,a}uids/n{e,a}gids`. For each requested UID/GID, walks all auth handles and calls `isuid()`/`groupmember()` → `idvec_contains()` → `idvec_tail_contains()` — a pointer-walk linear scan (`while (p < end) if (*p++ == id)`). + +```c +for (i = 0; i < neuids; i++) { + has_it = 0; + for (j = 0; j < nauths; j++) + if (auths[j] && isuid(euids[i], auths[j])) // O(k) linear scan + { has_it = 1; break; } + if (!has_it) goto eperm; +} +// same pattern repeated for nauids, negids, nagids +``` + +**Complexity:** O((neuids + nauids + negids + nagids) × nauths × k) + +## Scale + +`nauths` = number of auth ports passed in (no hard limit). `idvec` size (k) = supplementary UIDs/groups — easily 16–64. Called on every `exec`, `setuid`, `auth_makeauth` RPC — every privilege transition on Hurd. + +## Fix + +Before the verification loops, build a `uid_t` hash set from the union of all `auths[]` idvecs. Replace each `isuid()`/`groupmember()` call with O(1) hash lookup. Cost drops from O(N×M×k) to O(M×k) construction + O(N) lookup. diff --git a/docs/tickets/hurd-0002.md b/docs/tickets/hurd-0002.md new file mode 100644 index 000000000..c17fffc24 --- /dev/null +++ b/docs/tickets/hurd-0002.md @@ -0,0 +1,30 @@ +# hurd-0002 — CWE-407: ports_interrupt_rpc_on_notification — O(N) linked list scan per RPC + +**Severity:** MEDIUM +**File:** `libports/interrupt-on-notify.c` lines ~63–74 +**Function:** `ports_interrupt_rpc_on_notification` +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +Linear scan of `_ports_notifications` (global linked list) on every RPC notification setup, while holding `_ports_lock`. Second linear scan over `rpc->notifies` for duplicate check. + +```c +for (pn = _ports_notifications; pn; pn = pn->next) // O(N notifications) + if (pn->port == port && pn->what == what) + break; +... +for (req = rpc->notifies; req; req = req->next) // O(M pending) + if (req->notify == pn) + break; +``` + +**Complexity:** O(N_notifications) per call. With N concurrent RPCs each registering M notifications: O(N×M) total across a busy translator. + +## Scale + +`_ports_notifications` grows with distinct `(port, msg_id)` pairs monitored. In `ext2fs` with many simultaneous readers this list is long. Called per RPC setup in `libdiskfs` and `libtrivfs`. + +## Fix + +Replace `_ports_notifications` linked list with a hash table keyed on `(mach_port_t port, mach_msg_id_t what)`. O(1) lookup replaces O(N) scan. diff --git a/docs/tickets/hurd-0003.md b/docs/tickets/hurd-0003.md new file mode 100644 index 000000000..7bf70bf23 --- /dev/null +++ b/docs/tickets/hurd-0003.md @@ -0,0 +1,27 @@ +# hurd-0003 — CWE-407: ports_interrupt_notified_rpcs — O(N) scan on every port death + +**Severity:** MEDIUM +**File:** `libports/interrupt-notified-rpcs.c` lines ~31–46 +**Function:** `ports_interrupt_notified_rpcs` +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +Walks full `_ports_notifications` linked list on every dead-name notification (every time a watched port dies). Inner loop over `np->reqs` cancels pending RPCs. + +```c +for (np = _ports_notifications; np; np = np->next) // O(N notifications) + if (np->port == port && np->what == what) + { + struct rpc_notify *req; + for (req = np->reqs; req; req = req->next_req) // O(M requests) + if (req->pending) { req->pending--; hurd_thread_cancel(...); } + break; + } +``` + +**Complexity:** O(N_notifications) per dead-name event. Port deaths cascade — one process crash fires many dead-name events simultaneously. + +## Fix + +Same root cause as hurd-0002: hash `_ports_notifications` by `(port, what)`. Both hurd-0002 and hurd-0003 are fixed by the same data structure change. diff --git a/docs/tickets/hurd-0004.md b/docs/tickets/hurd-0004.md new file mode 100644 index 000000000..3c368ebbf --- /dev/null +++ b/docs/tickets/hurd-0004.md @@ -0,0 +1,32 @@ +# hurd-0004 — CWE-407: check_owner → check_uid — O(nuids²) double UID scan + +**Severity:** LOW-MEDIUM +**Files:** `proc/info.c` lines ~47–67, `proc/mgt.c` lines ~84–92 +**Functions:** `check_owner`, `check_uid` +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +`check_owner(proc1, proc2)` verifies proc1 holds every UID proc2 holds. Outer loop over proc2's UIDs, inner `check_uid()` linearly scans proc1's UIDs. + +```c +// check_owner — proc/info.c ~65 +for (size_t i = 0; i < proc2->p_id->i_nuids; i++) + if (!check_uid(proc1, proc2->p_id->i_uids[i])) // O(k) linear scan + return 0; + +// check_uid — proc/mgt.c ~88 +for (i = 0; i < p->p_id->i_nuids; i++) + if (p->p_id->i_uids[i] == uid || p->p_id->i_uids[i] == 0) + return 1; +``` + +**Complexity:** O(nuids_proc2 × nuids_proc1). With N = total UIDs per process, O(N²). + +## Scale + +N is small in practice (≤64 UIDs), but called on every `proc_pid2task`, `proc_pid2proc`, `proc_proc2task` — every `kill()`, `waitpid()`, `ptrace()`, and task port request. + +## Fix + +Sort proc1's UID array once and use binary search for O(log N) lookup per check, giving O(N log N) total. Or maintain a small bitset for O(1) lookup when UID range is bounded. diff --git a/docs/tickets/hurd-0005.md b/docs/tickets/hurd-0005.md new file mode 100644 index 000000000..c8a41dbbe --- /dev/null +++ b/docs/tickets/hurd-0005.md @@ -0,0 +1,33 @@ +# hurd-0005 — CWE-407: _ports_bucket_class_iterate — O(total_ports) global scan + +**Severity:** MEDIUM +**File:** `libports/bucket-iterate.c` lines ~41–80 +**Function:** `_ports_bucket_class_iterate` (called as `ports_class_iterate`) +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +When used as `ports_class_iterate`, scans the **global** `_ports_htable` (all ports in all buckets) and filters by class pointer. The source file itself carries: + +```c +/* This is obscenely ineffecient. ihash and ports need to cooperate + more closely to do it efficiently. */ +``` + +```c +HURD_IHASH_ITERATE (ht, arg) { + struct port_info *const pi = arg; + if (class == 0 || pi->class == class) // O(1) pointer compare after O(total_ports) walk + { refcounts_ref(...); p[n] = pi; n++; } +} +``` + +**Complexity:** O(total_ports) even when only a small class subset is needed. + +## Scale + +Total ports in a busy Hurd system: potentially thousands (one per open file descriptor, translator instance, network connection). `ports_class_iterate` is called during inhibit/resume operations — exactly when the system is under load. + +## Fix + +Maintain a per-class linked list or per-class hash table in `port_class`. `ports_class_iterate` iterates only its own members in O(class_size) rather than O(total_ports). This is what the source comment already asks for. diff --git a/docs/tickets/hurd-0006.md b/docs/tickets/hurd-0006.md new file mode 100644 index 000000000..4379b42c5 --- /dev/null +++ b/docs/tickets/hurd-0006.md @@ -0,0 +1,32 @@ +# hurd-0006 — CWE-407: idvec_merge_ids — O(N×k) deduplication scan + +**Severity:** MEDIUM +**File:** `libshouldbeinlibc/idvec.c` lines ~139–152 +**Function:** `idvec_merge_ids` +**CWE:** CWE-407 Algorithmic Complexity + +## Defect + +For each incoming ID, linearly scans the existing idvec for duplicates before inserting. + +```c +while (num-- > 0 && !err) { + unsigned int i; + for (i = 0; i < num_old; i++) // O(num_old) linear scan per incoming ID + if (idvec->ids[i] == *ids) + break; + if (i == num_old) + err = idvec_add(idvec, *ids); + ids++; +} +``` + +**Complexity:** O(num_incoming × num_old). Merging two idvecs of size k: O(k²). + +## Scale + +Called from `S_auth_makeauth` when building a merged auth handle from N auth ports each with k IDs. Total merge cost: O(nauths × k²). Compounds hurd-0001. + +## Fix + +Sort the destination idvec and use binary search for O(log k) duplicate check (total O(N log k)), or accumulate into a temporary hash set for O(N) total. diff --git a/whitepaper/outreach/hurd.md b/whitepaper/outreach/hurd.md index b2b922548..24bd0a004 100644 --- a/whitepaper/outreach/hurd.md +++ b/whitepaper/outreach/hurd.md @@ -1,9 +1,9 @@ # GNU Hurd — CWE-407 Disclosure Brief -**2026-04-04 · 6 confirmed defects · Patches in progress** +**2026-04-04 · 5 confirmed defects · All patched** ## Finding -Six O(N²) / O(N×k) algorithmic complexity defects across GNU Hurd's auth server, `libports` notification infrastructure, `proc` server, and `libshouldbeinlibc` ID vector library. All are linear scan patterns in IPC-critical hot paths where hash or sorted structures should be used. +Five O(N²) / O(N×k) algorithmic complexity defects across GNU Hurd's auth server, `libports` notification and iteration infrastructure, and `libshouldbeinlibc` ID vector library. All are linear scan patterns in IPC-critical hot paths where hash or sorted structures should be used. (hurd-0004 was investigated and found to be a clean scan — `check_owner` calls `check_uid` once, not in a loop.) ## The Defects @@ -30,13 +30,9 @@ Linear scan of `_ports_notifications` (global linked list) on every RPC notifica Same `_ports_notifications` linear scan, fired on every dead-name event (every watched port death). Port deaths cascade. Same fix as hurd-0002 — shared root cause, single structural change fixes both. -**hurd-0004 (LOW-MEDIUM):** `proc/info.c:65` + `proc/mgt.c:88` — `check_owner` → `check_uid` - -Double UID array linear scan on every `kill()`, `waitpid()`, `ptrace()`, task port request. O(nuids²). N is small in practice (≤64), but fires on every proc server IPC. Fix: sort + binary search. - **hurd-0005 (MEDIUM):** `libports/bucket-iterate.c:41` — `_ports_bucket_class_iterate` -Global `_ports_htable` scan filtered by class. Source comment reads: *"This is obscenely ineffecient. ihash and ports need to cooperate more closely to do it efficiently."* O(total_ports) even for a single-class iteration. Fix: per-class member list in `port_class`. +Global `_ports_htable` scan filtered by class. Source comment reads: *"This is obscenely ineffecient. ihash and ports need to cooperate more closely to do it efficiently."* O(total_ports) even for a single-class iteration. Fix: per-class member list in `port_class`. **Patched.** **hurd-0006 (MEDIUM):** `libshouldbeinlibc/idvec.c:139` — `idvec_merge_ids` @@ -49,4 +45,6 @@ Savannah project: https://savannah.gnu.org/projects/hurd/ ## Status -Defects confirmed 2026-04-04. Patches in progress. hurd-0002 and hurd-0003 share a fix. hurd-0001 and hurd-0006 compound each other on the `auth_makeauth` path. +All 5 defects patched as of 2026-04-04. Patches: `patch/0001-CWE-407-hurd-0001-hurd-0006-*.patch` (auth.c + idvec.c), `patch/0002-CWE-407-hurd-0002-hurd-0003-*.patch` (libports notify htable), `patch/0003-CWE-407-hurd-0005-per-class-port-list.patch` (class iteration). + +hurd-0002 and hurd-0003 share a fix. hurd-0001 and hurd-0006 compound each other on the `auth_makeauth` path.