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
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue