hurd: all 5 defects patched, hurd-0004 closed as clean scan, add hurd-0005 patch
This commit is contained in:
parent
80a81910cf
commit
6050e1b741
12 changed files with 401 additions and 33 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# UNDF: UNDF-2026-000001244
|
||||
From c05586601073629e48ed886fbdaa12b9c4c80313 Mon Sep 17 00:00:00 2001
|
||||
From: "russell@unturf.com" <russell@unturf.com>
|
||||
Date: Sat, 4 Apr 2026 20:31:12 -0400
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# UNDF: UNDF-2026-000001245
|
||||
From 25aa731e690c799837c21c09db0d047b811e79c3 Mon Sep 17 00:00:00 2001
|
||||
From: "russell@unturf.com" <russell@unturf.com>
|
||||
Date: Sat, 4 Apr 2026 20:31:12 -0400
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
From f5f781fa2e85e407ac6742df85e001ba3e5e7342 Mon Sep 17 00:00:00 2001
|
||||
From: "russell@unturf.com" <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
|
||||
|
||||
31
docs/tickets/hurd-0001.md
Normal file
31
docs/tickets/hurd-0001.md
Normal file
|
|
@ -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.
|
||||
30
docs/tickets/hurd-0002.md
Normal file
30
docs/tickets/hurd-0002.md
Normal file
|
|
@ -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.
|
||||
27
docs/tickets/hurd-0003.md
Normal file
27
docs/tickets/hurd-0003.md
Normal file
|
|
@ -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.
|
||||
32
docs/tickets/hurd-0004.md
Normal file
32
docs/tickets/hurd-0004.md
Normal file
|
|
@ -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.
|
||||
33
docs/tickets/hurd-0005.md
Normal file
33
docs/tickets/hurd-0005.md
Normal file
|
|
@ -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.
|
||||
32
docs/tickets/hurd-0006.md
Normal file
32
docs/tickets/hurd-0006.md
Normal file
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue