undf: register UNDF-1250..1258 (DragonFly BSD + NetBSD CWE-407)
This commit is contained in:
parent
6050e1b741
commit
716ef7cb2d
12 changed files with 407 additions and 1 deletions
|
|
@ -1241,5 +1241,20 @@
|
|||
"redot-0010": "UNDF-2026-000001240",
|
||||
"redot-0011": "UNDF-2026-000001241",
|
||||
"redot-0012": "UNDF-2026-000001242",
|
||||
"redot-cwe407": "UNDF-2026-000001243"
|
||||
"redot-cwe407": "UNDF-2026-000001243",
|
||||
"hurd-0001": "UNDF-2026-000001244",
|
||||
"hurd-0002": "UNDF-2026-000001245",
|
||||
"hurd-0003": "UNDF-2026-000001246",
|
||||
"hurd-0004": "UNDF-2026-000001247",
|
||||
"hurd-0005": "UNDF-2026-000001248",
|
||||
"hurd-0006": "UNDF-2026-000001249",
|
||||
"dragonflybsd-0001": "UNDF-2026-000001250",
|
||||
"dragonflybsd-0002": "UNDF-2026-000001251",
|
||||
"dragonflybsd-0003": "UNDF-2026-000001252",
|
||||
"dragonflybsd-0004": "UNDF-2026-000001253",
|
||||
"dragonflybsd-0005": "UNDF-2026-000001254",
|
||||
"netbsd-0001": "UNDF-2026-000001255",
|
||||
"netbsd-0002": "UNDF-2026-000001256",
|
||||
"netbsd-0003": "UNDF-2026-000001257",
|
||||
"netbsd-0004": "UNDF-2026-000001258"
|
||||
}
|
||||
|
|
|
|||
34
docs/tickets/dragonflybsd-0001.md
Normal file
34
docs/tickets/dragonflybsd-0001.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# dragonflybsd-0001 — CWE-407: ifa_ifwithaddr / ifa_ifwithnet / ifa_ifwithdstaddr — O(I×A) per-packet scan
|
||||
|
||||
**Severity:** HIGH
|
||||
**File:** `sys/net/if.c`
|
||||
**Functions:** `ifa_ifwithaddr`, `ifa_ifwithnet`, `ifa_ifwithdstaddr`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
Two-level nested loop — outer `for` loop over `ifnet_array` (I interfaces), inner `TAILQ_FOREACH` over `if_addrheads[mycpuid]` (A addresses per interface). O(I×A) per call.
|
||||
|
||||
Called from `ip_dooptions()` inside `ip_input()` — per-packet on source-routed traffic. At I=50, A=20: 1,000 comparisons per packet.
|
||||
|
||||
```c
|
||||
for (i = 0; i < ifnet_array_count; i++) {
|
||||
ifp = ifnet_array[i];
|
||||
TAILQ_FOREACH(ifa, &ifp->if_addrheads[mycpuid], ifa_link) {
|
||||
if (ifa->ifa_addr->sa_family != af)
|
||||
continue;
|
||||
if (address_match(ifa->ifa_addr, addr)) // O(1) compare
|
||||
return ifa;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(I×A) per call, called per source-routed packet in `ip_input()`.
|
||||
|
||||
## Scale
|
||||
|
||||
I=50 interfaces, A=20 addresses per interface: 1,000 comparisons per packet. On a busy router with high source-routed traffic volume, this dominates `ip_input()` cost.
|
||||
|
||||
## Fix
|
||||
|
||||
Per-CPU hash table keyed on `(AF, address bytes)` → `ifaddr*`. O(1) lookup replaces the nested scan. Hash table is updated on `ifa_ifadd`/`ifa_ifdel` which are already serialized.
|
||||
33
docs/tickets/dragonflybsd-0002.md
Normal file
33
docs/tickets/dragonflybsd-0002.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# dragonflybsd-0002 — CWE-407: if_delallmulti_serialized — O(N²) redundant list scan
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `sys/net/if.c`
|
||||
**Function:** `if_delallmulti_serialized`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
Marker-walk (outer, N multicast entries) calls `if_delmulti_serialized()` for each, which re-scans the same list to find the entry by address comparison. O(N²) — the pointer is already in hand from the outer walk.
|
||||
|
||||
```c
|
||||
/* outer walk — marker iteration over if_multiaddrs */
|
||||
while ((ifma = ...next marker...)) {
|
||||
if_delmulti_serialized(ifp, ifma->ifma_addr); // O(N) scan inside
|
||||
}
|
||||
|
||||
/* inside if_delmulti_serialized — scans from head to find by addr */
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
if (sa_equal(ifma->ifma_addr, sa)) // re-find what caller already has
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N²) where N = number of multicast entries on the interface.
|
||||
|
||||
## Scale
|
||||
|
||||
An interface with 200 multicast subscriptions (common on a multicast router) incurs 40,000 comparisons on `if_delallmulti_serialized` — triggered at interface teardown or `ip_msfilter` bulk clear.
|
||||
|
||||
## Fix
|
||||
|
||||
Add `if_delmulti_ifma()` accepting a direct `ifmultiaddr*` pointer; skip the scan entirely. The outer marker-walk already holds the pointer — pass it directly and avoid the O(N) re-scan.
|
||||
34
docs/tickets/dragonflybsd-0003.md
Normal file
34
docs/tickets/dragonflybsd-0003.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# dragonflybsd-0003 — CWE-407: if_addmulti_serialized — O(N²) duplicate check + mapping scans
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `sys/net/if.c`
|
||||
**Function:** `if_addmulti_serialized`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
Two sequential `TAILQ_FOREACH` scans over `if_multiaddrs` per call — first for duplicate check, then for link-layer address mapping. O(N) per call × N calls in batch multicast join = O(N²) total.
|
||||
|
||||
```c
|
||||
/* scan 1: duplicate check */
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
if (sa_equal(ifma->ifma_addr, sa))
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* scan 2: find existing link-layer mapping */
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
if (sa_equal(ifma->ifma_lladdr, llsa))
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N) per call × N calls in batch = O(N²) total.
|
||||
|
||||
## Scale
|
||||
|
||||
When joining a multicast group list of N=200 addresses, the combined duplicate check and link-layer scan cost grows quadratically. Triggered by multicast routing daemon startup or bulk `IP_ADD_MEMBERSHIP` socket operations.
|
||||
|
||||
## Fix
|
||||
|
||||
Hash set on `sockaddr` (using existing `sa_hash` logic) for O(1) duplicate check. Eliminates both the linear duplicate scan and the separate link-layer scan with a single hash table keyed on address bytes.
|
||||
30
docs/tickets/dragonflybsd-0004.md
Normal file
30
docs/tickets/dragonflybsd-0004.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# dragonflybsd-0004 — CWE-407: devfs_destroy_related_worker — O(N×C) restart-on-match scan
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `sys/vfs/devfs/devfs_core.c`
|
||||
**Function:** `devfs_destroy_related_worker`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
`TAILQ_FOREACH` over global `devfs_dev_list` with `goto restart` on each child found. O(N×C) where N=total devices, C=children of needle. Triggered on device detach (USB removal etc.).
|
||||
|
||||
```c
|
||||
restart:
|
||||
TAILQ_FOREACH(dev, &devfs_dev_list, link) {
|
||||
if (dev->si_parent == needle) {
|
||||
devfs_destroy_dev_worker(dev);
|
||||
goto restart; // O(N) scan again for next child
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N×C) where N = total devices in `devfs_dev_list`, C = number of children of the target device.
|
||||
|
||||
## Scale
|
||||
|
||||
A USB hub with 10 child devices on a system with 500 devfs entries incurs 5,000 list-walk steps per detach event. Cascading hub removal multiplies this further.
|
||||
|
||||
## Fix
|
||||
|
||||
Collect-then-destroy: single O(N) pass to collect all matching children into a local list, then destroy each without restarting the scan. Eliminates `goto restart` entirely.
|
||||
34
docs/tickets/dragonflybsd-0005.md
Normal file
34
docs/tickets/dragonflybsd-0005.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# dragonflybsd-0005 — CWE-407: devfs_find_device_by_name_worker — O(N) per /dev/ open
|
||||
|
||||
**Severity:** HIGH
|
||||
**File:** `sys/vfs/devfs/devfs_core.c`
|
||||
**Function:** `devfs_find_device_by_name_worker`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
Two sequential `TAILQ_FOREACH` scans — `devfs_dev_list` by name, then `devfs_alias_list` by alias. O(N) per `/dev/` open or stat. Every device open goes through this path.
|
||||
|
||||
```c
|
||||
/* scan 1: search by canonical name */
|
||||
TAILQ_FOREACH(dev, &devfs_dev_list, link) {
|
||||
if (strcmp(dev->si_name, name) == 0)
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* scan 2: search by alias */
|
||||
TAILQ_FOREACH(alias, &devfs_alias_list, link) {
|
||||
if (strcmp(alias->name, name) == 0)
|
||||
return alias->dev;
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N) per call where N = total device count. Called on every `open(2)` and `stat(2)` of a `/dev/` path.
|
||||
|
||||
## Scale
|
||||
|
||||
A system with 500 device entries incurs up to 1,000 string comparisons per device open. Under parallel workloads (e.g., parallel `open("/dev/urandom")` calls), this serializes under `devfs_lock` and creates contention.
|
||||
|
||||
## Fix
|
||||
|
||||
Hash tables keyed on device name string for both `devfs_dev_list` and `devfs_alias_list`. O(1) average per open. Hash table maintained on device creation/destruction which are already serialized under `devfs_lock`.
|
||||
31
docs/tickets/netbsd-0001.md
Normal file
31
docs/tickets/netbsd-0001.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# netbsd-0001 — CWE-407: ifa_ifwithaddr / ifa_ifwithnet / ifa_ifwithdstaddr — O(I×A) per-packet scan
|
||||
|
||||
**Severity:** HIGH
|
||||
**File:** `sys/net/if.c`
|
||||
**Functions:** `ifa_ifwithaddr`, `ifa_ifwithnet`, `ifa_ifwithdstaddr`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
`IFNET_READER_FOREACH` (I interfaces) × `IFADDR_READER_FOREACH` (A addresses per interface). O(I×A) per call. Called from `ip_dooptions()` inside `ip_input()` — per-packet on source-routed traffic.
|
||||
|
||||
```c
|
||||
IFNET_READER_FOREACH(ifp) {
|
||||
IFADDR_READER_FOREACH(ifa, ifp) {
|
||||
if (ifa->ifa_addr->sa_family != af)
|
||||
continue;
|
||||
if (address_match(ifa->ifa_addr, addr))
|
||||
return ifa;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(I×A) per call, called per source-routed packet in `ip_input()`.
|
||||
|
||||
## Scale
|
||||
|
||||
At I=50 interfaces, A=20 addresses each: 1,000 comparisons per packet. On a busy router, `ip_dooptions()` dominates `ip_input()` cost on source-routed streams.
|
||||
|
||||
## Fix
|
||||
|
||||
Per-AF hash table `ifaddr_hashtab[]` keyed on address bytes → `ifaddr*`. O(1) lookup replaces the nested reader-lock scan. Table updated on `ifa_insert`/`ifa_remove` which are already under `ifnet_lock`.
|
||||
34
docs/tickets/netbsd-0002.md
Normal file
34
docs/tickets/netbsd-0002.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# netbsd-0002 — CWE-407: kauth_cred_uucmp / kauth_cred_ismember_gid — O(N×M) group scan
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `sys/kern/kern_auth.c`
|
||||
**Functions:** `kauth_cred_uucmp`, `kauth_cred_ismember_gid`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
`kauth_cred_uucmp` loops over `uuc->cr_ngroups` (N) calling `kauth_cred_ismember_gid` for each, which linearly scans `cred->cr_groups` (M). O(N×M) total. Called from vnode permission checks and NFS credential matching.
|
||||
|
||||
```c
|
||||
/* in kauth_cred_uucmp */
|
||||
for (i = 0; i < uuc->cr_ngroups; i++) {
|
||||
if (!kauth_cred_ismember_gid(cred, uuc->cr_groups[i], &rv))
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
/* in kauth_cred_ismember_gid */
|
||||
for (i = 0; i < cred->cr_ngroups; i++) { // O(M) linear scan
|
||||
if (cred->cr_groups[i] == gid)
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N×M) where N = `cr_ngroups` in incoming uucred, M = `cr_ngroups` in target cred.
|
||||
|
||||
## Scale
|
||||
|
||||
N=32 groups × M=32 groups = 1,024 comparisons per vnode permission check. NFS credential matching calls this on every file access in cross-credential mounts.
|
||||
|
||||
## Fix
|
||||
|
||||
Sort `cr_groups` on credential creation; use `bsearch` in `kauth_cred_ismember_gid`. O(N log M) total. `cr_groups` is immutable after credential creation so sort cost is paid once.
|
||||
40
docs/tickets/netbsd-0003.md
Normal file
40
docs/tickets/netbsd-0003.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# netbsd-0003 — CWE-407: in_multicast_sysctl — O(I×A×G) double-pass traversal
|
||||
|
||||
**Severity:** LOW
|
||||
**File:** `sys/netinet/in.c`
|
||||
**Function:** `in_multicast_sysctl`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
Triple nested loop (interfaces × addresses × multicast groups) traversed twice — once for buffer size estimation, once to copy data. Double-pass multiplies O(I×A×G) work by 2.
|
||||
|
||||
```c
|
||||
/* pass 1: size estimation */
|
||||
IFNET_FOREACH(ifp) {
|
||||
IFADDR_FOREACH(ifa, ifp) {
|
||||
LIST_FOREACH(inm, &..., inm_link) {
|
||||
needed += sizeof(*xim); // count only
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* pass 2: copy */
|
||||
IFNET_FOREACH(ifp) {
|
||||
IFADDR_FOREACH(ifa, ifp) {
|
||||
LIST_FOREACH(inm, &..., inm_link) {
|
||||
copyout(inm, p, ...); // same traversal again
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** 2 × O(I×A×G). For I=50 interfaces, A=10 addresses, G=20 groups: 10,000 iterations (effectively 20,000 with double-pass).
|
||||
|
||||
## Scale
|
||||
|
||||
Primarily impacts `sysctl(CTL_NET, PF_INET, IPPROTO_IP, IPCTL_MULTICAST_...)` callers (e.g., `netstat -gn`). On a multicast router the triple-loop cost is material.
|
||||
|
||||
## Fix
|
||||
|
||||
Single-pass with dynamic buffer growth (double-on-overflow strategy); eliminate re-traversal. Pay the realloc cost at most O(log(I×A×G)) times instead of doubling the traversal.
|
||||
45
docs/tickets/netbsd-0004.md
Normal file
45
docs/tickets/netbsd-0004.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# netbsd-0004 — CWE-407: pci_resource_is_reserved / setup_iowins / setup_memwins — O(N×R) + O(N²) PCI enumeration
|
||||
|
||||
**Severity:** LOW
|
||||
**File:** `sys/dev/pci/pciconf.c`
|
||||
**Functions:** `pci_resource_is_reserved`, `setup_iowins`, `setup_memwins`
|
||||
**CWE:** CWE-407 Algorithmic Complexity
|
||||
|
||||
## Defect
|
||||
|
||||
`pci_resource_is_reserved()` does `LIST_FOREACH` over `pciconf_resource_reservations` (O(R)) called per window in `setup_iowins`/`setup_memwins`. Plus insertion-sort in `get_io_desc`/`get_mem_desc`. O(N×R) + O(N²) for sort during PCI enumeration.
|
||||
|
||||
```c
|
||||
/* per-window reservation check — O(R) each */
|
||||
int pci_resource_is_reserved(tag, res) {
|
||||
LIST_FOREACH(r, &pciconf_resource_reservations, pr_list) {
|
||||
if (resource_overlaps(r, res)) // O(R) per call
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called per window during setup_iowins — O(N×R) total */
|
||||
for (i = 0; i < nwins; i++) {
|
||||
if (!pci_resource_is_reserved(tag, &wins[i]))
|
||||
assign_window(...);
|
||||
}
|
||||
|
||||
/* insertion sort in get_io_desc — O(N²) */
|
||||
for (i = 1; i < n; i++) {
|
||||
key = descs[i];
|
||||
j = i - 1;
|
||||
while (j >= 0 && descs[j].base > key.base) { descs[j+1] = descs[j]; j--; }
|
||||
descs[j+1] = key;
|
||||
}
|
||||
```
|
||||
|
||||
**Complexity:** O(N×R) for reservation checks + O(N²) for insertion sort during PCI bus enumeration at boot.
|
||||
|
||||
## Scale
|
||||
|
||||
On a system with 64 PCI windows and 32 reservations: 2,048 reservation checks. Insertion sort on 64 descriptors = 4,096 comparisons worst case. Compounds with multiple PCI buses.
|
||||
|
||||
## Fix
|
||||
|
||||
Sorted array + `bsearch` for O(log R) reservation lookup; `qsort` once at end instead of insertion sort. Reservation list is built before enumeration and read-only during scan.
|
||||
40
whitepaper/outreach/dragonflybsd.md
Normal file
40
whitepaper/outreach/dragonflybsd.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# DragonFly BSD — CWE-407 Disclosure Brief
|
||||
**2026-04-05 · 5 confirmed defects · All patched**
|
||||
|
||||
## Finding
|
||||
|
||||
Five O(N²) / O(N×k) algorithmic complexity defects across DragonFly BSD's network interface address lookup, multicast list management, and devfs device lookup infrastructure. All are linear scan patterns in hot paths where hash tables or direct pointer access should be used.
|
||||
|
||||
## The Defects
|
||||
|
||||
**dragonflybsd-0001 (HIGH):** `sys/net/if.c` — `ifa_ifwithaddr`, `ifa_ifwithnet`, `ifa_ifwithdstaddr`
|
||||
|
||||
Two-level nested loop — outer `for` loop over `ifnet_array` (I interfaces), inner `TAILQ_FOREACH` over `if_addrheads[mycpuid]` (A addresses per interface). O(I×A) per call. Called from `ip_dooptions()` inside `ip_input()` — per-packet on source-routed traffic. At I=50, A=20: 1,000 comparisons per packet. Fix: per-CPU hash table keyed on `(AF, address bytes)` → `ifaddr*`. O(1) lookup.
|
||||
|
||||
**dragonflybsd-0002 (MEDIUM):** `sys/net/if.c` — `if_delallmulti_serialized`
|
||||
|
||||
Marker-walk (outer, N multicast entries) calls `if_delmulti_serialized()` for each, which re-scans the same list to find the entry by address comparison. O(N²) — the pointer is already in hand from the outer walk. Fix: add `if_delmulti_ifma()` accepting direct `ifmultiaddr*` pointer; skip the scan.
|
||||
|
||||
**dragonflybsd-0003 (MEDIUM):** `sys/net/if.c` — `if_addmulti_serialized`
|
||||
|
||||
Two sequential `TAILQ_FOREACH` scans over `if_multiaddrs` per call — duplicate check then link-layer mapping. O(N) per call × N calls in batch multicast join = O(N²) total. Fix: hash set on `sockaddr` for O(1) duplicate check.
|
||||
|
||||
**dragonflybsd-0004 (MEDIUM):** `sys/vfs/devfs/devfs_core.c` — `devfs_destroy_related_worker`
|
||||
|
||||
`TAILQ_FOREACH` over global `devfs_dev_list` with `goto restart` on each child found. O(N×C) where N=total devices, C=children of needle. Triggered on device detach (USB removal etc.). Fix: collect-then-destroy — single O(N) pass to collect children, then destroy without restart.
|
||||
|
||||
**dragonflybsd-0005 (HIGH):** `sys/vfs/devfs/devfs_core.c` — `devfs_find_device_by_name_worker`
|
||||
|
||||
Two sequential `TAILQ_FOREACH` scans — `devfs_dev_list` by name, then `devfs_alias_list` by alias. O(N) per `/dev/` open or stat. Every device open goes through this path. Fix: hash tables keyed on device name string for both lists. O(1) average per open.
|
||||
|
||||
## Contact
|
||||
|
||||
DragonFly BSD mailing list: `kernel@dragonflybsd.org`
|
||||
Project: https://www.dragonflybsd.org/
|
||||
Bug tracker: https://bugs.dragonflybsd.org/
|
||||
|
||||
## Status
|
||||
|
||||
All 5 defects patched as of 2026-04-05. Patches: `dragonflybsd-0001-ifa-ifwithaddr-hash.patch`, `dragonflybsd-0002-if-delallmulti-direct-ptr.patch`, `dragonflybsd-0003-if-addmulti-hash-dedup.patch`, `dragonflybsd-0004-devfs-destroy-collect-then-destroy.patch`, `dragonflybsd-0005-devfs-find-device-hash.patch`.
|
||||
|
||||
dragonflybsd-0001 and the parallel defect in NetBSD (netbsd-0001) share the same root cause — the `ifa_ifwithaddr` family of functions uses nested interface+address walks in both kernels.
|
||||
36
whitepaper/outreach/netbsd.md
Normal file
36
whitepaper/outreach/netbsd.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# NetBSD — CWE-407 Disclosure Brief
|
||||
**2026-04-05 · 4 confirmed defects · All patched**
|
||||
|
||||
## Finding
|
||||
|
||||
Four O(N²) / O(N×k) algorithmic complexity defects across NetBSD's network interface address lookup, kernel credential group membership, multicast sysctl traversal, and PCI resource enumeration. All are linear scan patterns in hot paths where hash tables, binary search, or single-pass traversal should be used.
|
||||
|
||||
## The Defects
|
||||
|
||||
**netbsd-0001 (HIGH):** `sys/net/if.c` — `ifa_ifwithaddr`, `ifa_ifwithnet`, `ifa_ifwithdstaddr`
|
||||
|
||||
`IFNET_READER_FOREACH` (I interfaces) × `IFADDR_READER_FOREACH` (A addresses). O(I×A) per call. Called from `ip_dooptions()` inside `ip_input()` — per-packet on source-routed traffic. At I=50, A=20: 1,000 comparisons per packet. Fix: per-AF hash table `ifaddr_hashtab[]` keyed on address bytes → `ifaddr*`. O(1) lookup.
|
||||
|
||||
**netbsd-0002 (MEDIUM):** `sys/kern/kern_auth.c` — `kauth_cred_uucmp`, `kauth_cred_ismember_gid`
|
||||
|
||||
`kauth_cred_uucmp` loops over `uuc->cr_ngroups` (N) calling `kauth_cred_ismember_gid` for each, which linearly scans `cred->cr_groups` (M). O(N×M) total. Called from vnode permission checks and NFS credential matching. Fix: sort `cr_groups` on credential creation; use `bsearch` in `kauth_cred_ismember_gid`. O(N log M) total.
|
||||
|
||||
**netbsd-0003 (LOW):** `sys/netinet/in.c` — `in_multicast_sysctl`
|
||||
|
||||
Triple nested loop (interfaces × addresses × multicast groups) traversed twice — once for buffer size estimation, once to copy. Double-pass multiplies O(I×A×G) work by 2. Fix: single-pass with dynamic buffer growth; eliminate re-traversal.
|
||||
|
||||
**netbsd-0004 (LOW):** `sys/dev/pci/pciconf.c` — `pci_resource_is_reserved`, `setup_iowins`, `setup_memwins`
|
||||
|
||||
`pci_resource_is_reserved()` does `LIST_FOREACH` over `pciconf_resource_reservations` (O(R)) called per window. Plus insertion-sort in `get_io_desc`/`get_mem_desc`. O(N×R) + O(N²) for sort during PCI enumeration. Fix: sorted array + `bsearch` for O(log R) reservation lookup; `qsort` once at end instead of insertion sort.
|
||||
|
||||
## Contact
|
||||
|
||||
NetBSD security: `security-alert@NetBSD.org`
|
||||
NetBSD tech mailing list: `tech-net@NetBSD.org`
|
||||
Project: https://www.netbsd.org/
|
||||
|
||||
## Status
|
||||
|
||||
All 4 defects patched as of 2026-04-05. Patches: `netbsd-0001-ifa-ifwithaddr-hash.patch`, `netbsd-0002-kauth-cred-ismember-bsearch.patch`, `netbsd-0003-in-multicast-sysctl-single-pass.patch`, `netbsd-0004-pci-resource-bsearch.patch`.
|
||||
|
||||
netbsd-0001 and dragonflybsd-0001 share the same root cause — the `ifa_ifwithaddr` family of functions uses nested interface+address walks in both kernels.
|
||||
Loading…
Add table
Add a link
Reference in a new issue