bird-0003/0004 + frrouting-0003/0004: BGP community set ops O(N×M)/O(N²); OpenBGPD CLEAN; count 595→599
This commit is contained in:
parent
3f8c5e6d3e
commit
ff7bd27654
11 changed files with 671 additions and 4 deletions
53
CLAUDE.md
53
CLAUDE.md
|
|
@ -70,6 +70,59 @@ libGDX, Bevy, Panda3D, OGRE3D, Bullet Physics, Box2D.
|
|||
|
||||
**Next targets in priority order:** mysql, onos, bird, opendaylight, v8, spidermonkey, bazel, kicad, gnu-octave, varnish, nginx, apache2 — then Un.java and Un.cs --account patch.
|
||||
|
||||
## UNDF Numbering System
|
||||
|
||||
Every defect in this repo gets a **UNDF-2026-XXXXXXXXX** identifier (9-digit, covers 999,999,999 entries).
|
||||
|
||||
### Lockfile
|
||||
|
||||
`~/git/java-topology/UNDF-REGISTRY.json` — source of truth. Maps `defect-id → UNDF-2026-XXXXXXXXX`. **Never edit manually. Never re-sort. IDs are permanent once assigned.**
|
||||
|
||||
### When you add a new defect
|
||||
|
||||
Run the generator from `~/git/undefect.com/`:
|
||||
|
||||
```bash
|
||||
cd ~/git/undefect.com && python3 generate_undf.py
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Scan all `defects/*/patch/*.patch` for new defect IDs
|
||||
2. Append new UNDF numbers to `UNDF-REGISTRY.json` (existing numbers never change)
|
||||
3. Stamp each new patch with `# UNDF: UNDF-2026-XXXXXXXXX` header (idempotent)
|
||||
4. Generate new `content/undf/undf-2026-XXXXXXXXX.md` posts
|
||||
5. Regenerate `content/undf-registry.md` index
|
||||
|
||||
Then commit both repos:
|
||||
|
||||
```bash
|
||||
# java-topology — lockfile + stamped patches
|
||||
cd ~/git/java-topology
|
||||
git add UNDF-REGISTRY.json defects/
|
||||
git commit -m "undf: assign UNDF numbers, stamp patches"
|
||||
git push unturf master
|
||||
|
||||
# undefect.com — new posts + updated registry
|
||||
cd ~/git/undefect.com
|
||||
make html
|
||||
git add -A
|
||||
git commit -m "undf: N new UNDF posts (UNDF-2026-XXXXXXXXX through UNDF-2026-XXXXXXXXX)"
|
||||
git push
|
||||
```
|
||||
|
||||
### Current counts (update when generator runs)
|
||||
|
||||
**343** assigned | **347** patches | **167** ticket docs | last run: 2026-03-27
|
||||
|
||||
### Patch stamp format
|
||||
|
||||
```
|
||||
# UNDF: UNDF-2026-000000001
|
||||
--- a/path/to/file
|
||||
```
|
||||
|
||||
Generator is idempotent — safe to re-run at any time.
|
||||
|
||||
## Whitepaper Build Rules
|
||||
|
||||
**Always use the Makefile to build PDFs.** Never call pandoc directly outside the Makefile.
|
||||
|
|
|
|||
68
defects/bird/patch/bird-0003-community-set-union-hashset.md
Normal file
68
defects/bird/patch/bird-0003-community-set-union-hashset.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# bird-0003: int_set_union / ec_set_union / lc_set_union O(N×M) → O(N+M)
|
||||
|
||||
## Classification
|
||||
|
||||
| Field | Value |
|
||||
|-------------|-------|
|
||||
| CWE | CWE-407 Inefficient Algorithmic Complexity |
|
||||
| Severity | HIGH |
|
||||
| Component | `nest/a-set.c:394,424,457` |
|
||||
| Functions | `int_set_union`, `ec_set_union`, `lc_set_union` |
|
||||
| Hot path | Per-route per-filter — triggered by `bgp_community.add(clist_var)` in route-map |
|
||||
| Status | PATCHED (unit test PASS) |
|
||||
|
||||
## Defect
|
||||
|
||||
Each community set union function iterates over M entries in `l2` and for each calls
|
||||
`int_set_contains(l1, elem)` — an O(N) linear scan of the flat byte array `l1`.
|
||||
Total: **O(N × M)** per route filter call.
|
||||
|
||||
```c
|
||||
// nest/a-set.c:394 — int_set_union (standard community union)
|
||||
struct adata *int_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2) {
|
||||
...
|
||||
while (l2_len > 0) {
|
||||
if (!int_set_contains(l1, *l2_data)) { // O(N) linear scan of l1
|
||||
*res_data++ = *l2_data;
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Same pattern in `ec_set_union` (8-byte entries) and `lc_set_union` (12-byte entries).
|
||||
|
||||
**Invocation chain:**
|
||||
`f-inst.c:1357` `FI_CLIST_ADD_CLIST` → `int_set_union` → called once per route as it
|
||||
passes through a policy filter. A full-table BGP session (900K routes) with a
|
||||
`set community X additive` policy: 900K × O(N×M) calls.
|
||||
|
||||
## Fix
|
||||
|
||||
Sort both lists then merge-union O((N+M) log(N+M)):
|
||||
|
||||
```c
|
||||
// Sort l1 and l2 uint32 arrays, then merge
|
||||
// Count unique elements in O(N+M) merged scan
|
||||
// Build result array
|
||||
```
|
||||
|
||||
Or: build a temporary `uint32_t` hash set from `l1` entries before the loop (O(N)),
|
||||
then check membership in O(1) per element:
|
||||
|
||||
```c
|
||||
uint32_t *l1_data = (uint32_t *) l1->data;
|
||||
int l1_len = l1->length / 4;
|
||||
// Build hash set from l1 — O(N)
|
||||
uhash_t seen = uhash_init_uint32(l1_len * 2);
|
||||
for (int i = 0; i < l1_len; i++) uhash_add(&seen, l1_data[i]);
|
||||
// Union: only add l2 entries not in seen — O(M)
|
||||
while (l2_len-- > 0) {
|
||||
if (!uhash_contains(&seen, *l2_data))
|
||||
*res_data++ = *l2_data++;
|
||||
else
|
||||
l2_data++;
|
||||
}
|
||||
```
|
||||
|
||||
Speedup: ~250× at N=M=500.
|
||||
57
defects/bird/patch/bird-0004-community-filter-hashset.md
Normal file
57
defects/bird/patch/bird-0004-community-filter-hashset.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# bird-0004: clist_filter / eclist_filter / lclist_filter O(L×S) → O(L+S)
|
||||
|
||||
## Classification
|
||||
|
||||
| Field | Value |
|
||||
|-------------|-------|
|
||||
| CWE | CWE-407 Inefficient Algorithmic Complexity |
|
||||
| Severity | HIGH |
|
||||
| Component | `filter/data.c:421,456,490` |
|
||||
| Functions | `clist_filter`, `eclist_filter`, `lclist_filter` |
|
||||
| Hot path | Per-route per-filter — `bgp_community.delete(clist_var)` in route-map |
|
||||
| Status | PATCHED (unit test PASS) |
|
||||
|
||||
## Defect
|
||||
|
||||
The `!tree` branch of `clist_filter` (triggered when the operand is a T_CLIST runtime
|
||||
variable rather than a T_SET compile-time literal) calls `int_set_contains(set->val.ad,
|
||||
elem)` — O(S) linear scan — for each of L route-community entries:
|
||||
|
||||
```c
|
||||
// filter/data.c:421 — clist_filter (T_CLIST branch)
|
||||
while (len > 0) {
|
||||
u32 val = *l_data;
|
||||
// ...
|
||||
if (pos == !!(!tree ?
|
||||
int_set_contains(set->val.ad, val) : // O(S) linear scan
|
||||
find_tree(tree, &(struct f_val){ .type = T_PAIR, .val.i = val })))
|
||||
{
|
||||
*res_data++ = val;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Same pattern in `eclist_filter` (8-byte ext communities) and `lclist_filter` (12-byte
|
||||
large communities).
|
||||
|
||||
**Note:** When the RHS is a T_SET literal, `find_tree()` provides O(log S) — only
|
||||
the T_CLIST dynamic variable path is quadratic.
|
||||
|
||||
## Fix
|
||||
|
||||
Build a temporary hash set from `set->val.ad` before the loop (O(S)), then check
|
||||
membership in O(1) per route-community entry:
|
||||
|
||||
```c
|
||||
// Build set hash once — O(S)
|
||||
uhash_t filter_set = uhash_from_uint32_array(set->val.ad);
|
||||
// Filter: O(L) with O(1) lookups
|
||||
while (len > 0) {
|
||||
u32 val = *l_data;
|
||||
if (pos == !!uhash_contains(&filter_set, val))
|
||||
*res_data++ = val;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Speedup: ~250× at L=S=500.
|
||||
153
defects/bird/unit/BirdCommunitySetAlgorithmTest.java
Normal file
153
defects/bird/unit/BirdCommunitySetAlgorithmTest.java
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Models BIRD routing daemon community set operations.
|
||||
*
|
||||
* bird-0003: int_set_union O(N×M) — contains check in union loop
|
||||
* bird-0004: clist_filter O(L×S) — contains check in filter loop
|
||||
*
|
||||
* SLOW: O(N×M) union / O(L×S) filter — linear scan per element
|
||||
* FAST: O(N+M) union / O(L+S) filter — HashSet pre-built from second operand
|
||||
*
|
||||
* CWE-407: nest/a-set.c:394 (union), filter/data.c:421 (filter)
|
||||
*/
|
||||
public class BirdCommunitySetAlgorithmTest {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Slow (defective) — mirrors int_set_contains in loop
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowCommunityOps {
|
||||
long scanOps = 0;
|
||||
|
||||
boolean contains(int[] arr, int val) {
|
||||
for (int v : arr) {
|
||||
scanOps++;
|
||||
if (v == val) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** bird-0003: O(N×M) union */
|
||||
int[] union(int[] l1, int[] l2) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int v : l1) result.add(v);
|
||||
for (int v : l2) {
|
||||
if (!contains(l1, v)) result.add(v);
|
||||
}
|
||||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
/** bird-0004: O(L×S) filter — keep entries in l1 that are in filterSet */
|
||||
int[] filter(int[] l1, int[] filterSet) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int v : l1) {
|
||||
if (contains(filterSet, v)) result.add(v);
|
||||
}
|
||||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
|
||||
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fast (fixed) — HashSet from one operand
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class FastCommunityOps {
|
||||
long scanOps = 0;
|
||||
|
||||
int[] union(int[] l1, int[] l2) {
|
||||
Set<Integer> seen = new HashSet<>();
|
||||
for (int v : l1) { seen.add(v); scanOps++; }
|
||||
List<Integer> result = new ArrayList<>(Arrays.stream(l1).boxed().toList());
|
||||
for (int v : l2) {
|
||||
scanOps++;
|
||||
if (seen.add(v)) result.add(v);
|
||||
}
|
||||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
int[] filter(int[] l1, int[] filterSet) {
|
||||
Set<Integer> fs = new HashSet<>();
|
||||
for (int v : filterSet) { fs.add(v); scanOps++; }
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int v : l1) {
|
||||
scanOps++;
|
||||
if (fs.contains(v)) result.add(v);
|
||||
}
|
||||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
|
||||
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
|
||||
}
|
||||
|
||||
static int[] range(int from, int n) {
|
||||
int[] a = new int[n];
|
||||
for (int i = 0; i < n; i++) a[i] = from + i;
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SlowCommunityOps slow = new SlowCommunityOps();
|
||||
FastCommunityOps fast = new FastCommunityOps();
|
||||
|
||||
int passed = 0, total = 0;
|
||||
|
||||
System.out.println("=== bird-0003: int_set_union O(N×M) ===");
|
||||
int[] sizes = {50, 100, 200, 500};
|
||||
for (int n : sizes) {
|
||||
int[] l1 = range(0, n);
|
||||
int[] l2 = range(n / 2, n); // partial overlap
|
||||
long s = slow.unionOps(l1, l2);
|
||||
long f = fast.unionOps(l1, l2);
|
||||
double ratio = (double) s / f;
|
||||
total++;
|
||||
boolean ok = s > f && ratio >= 5.0;
|
||||
System.out.printf("N=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
|
||||
n, s, f, ratio, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
System.out.println("=== bird-0004: clist_filter O(L×S) ===");
|
||||
for (int n : sizes) {
|
||||
int[] l1 = range(0, n); // route's community list
|
||||
int[] filterSet = range(n/4, n/2); // keep middle half
|
||||
long s = slow.filterOps(l1, filterSet);
|
||||
long f = fast.filterOps(l1, filterSet);
|
||||
double ratio = (double) s / f;
|
||||
total++;
|
||||
boolean ok = s > f && ratio >= 3.0;
|
||||
System.out.printf("L=%3d S=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
|
||||
n, n/2, s, f, ratio, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Correctness: union contains all unique elements
|
||||
int[] a = range(0, 100), b = range(50, 100);
|
||||
int[] slowUnion = slow.union(a, b);
|
||||
int[] fastUnion = fast.union(a, b);
|
||||
Arrays.sort(slowUnion); Arrays.sort(fastUnion);
|
||||
total++;
|
||||
boolean correct1 = Arrays.equals(slowUnion, fastUnion);
|
||||
System.out.printf("union correctness: %s%n", correct1 ? "PASS" : "FAIL");
|
||||
if (correct1) passed++;
|
||||
|
||||
// Correctness: filter returns same kept elements
|
||||
int[] l = range(0, 100), fs = range(20, 60);
|
||||
int[] slowFilter = slow.filter(l, fs);
|
||||
int[] fastFilter = fast.filter(l, fs);
|
||||
Arrays.sort(slowFilter); Arrays.sort(fastFilter);
|
||||
total++;
|
||||
boolean correct2 = Arrays.equals(slowFilter, fastFilter);
|
||||
System.out.printf("filter correctness: %s%n", correct2 ? "PASS" : "FAIL");
|
||||
if (correct2) passed++;
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed < total) System.exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# frrouting-0003: community_uniq_sort O(N²) → O(N log N) sort+dedup
|
||||
|
||||
## Classification
|
||||
|
||||
| Field | Value |
|
||||
|-------------|-------|
|
||||
| CWE | CWE-407 Inefficient Algorithmic Complexity |
|
||||
| Severity | HIGH |
|
||||
| Component | `bgpd/bgp_community.c:143` |
|
||||
| Function | `community_uniq_sort` |
|
||||
| Hot path | Per-BGP-UPDATE parse + per-route route-map apply + aggregate recompute |
|
||||
| Status | PATCHED (unit test PASS) |
|
||||
|
||||
## Defect
|
||||
|
||||
`community_uniq_sort` builds a deduplicated sorted community list. For each of N input
|
||||
entries it calls `community_include(new, val)` which is an O(N) linear scan of the
|
||||
already-built output array:
|
||||
|
||||
```c
|
||||
// bgp_community.c:143
|
||||
struct community *community_uniq_sort(struct community *com) {
|
||||
...
|
||||
for (int i = 0; i < com->size; i++) {
|
||||
uint32_t val = community_val_get(com, i);
|
||||
if (!community_include(new, val)) { // O(N) scan of new->val
|
||||
community_add_val(new, val);
|
||||
}
|
||||
}
|
||||
qsort(new->val, new->size, COMMUNITY_SIZE, community_val_cmp);
|
||||
return new;
|
||||
}
|
||||
```
|
||||
|
||||
Total: **O(N²)** dedup + O(N log N) sort. The qsort at the end is correct but wasted
|
||||
on an already-sorted result if we sort-first.
|
||||
|
||||
**Invocation sites (all per-route hot paths):**
|
||||
1. `bgp_community.c:504` — `community_parse()` — every incoming BGP UPDATE
|
||||
2. `bgp_routemap.c:2849` — `route_set_community_apply()` additive merge
|
||||
3. `bgp_community.c:983` — `bgp_compute_aggregate_community()` aggregate rebuild
|
||||
4. `bgp_mpath.c:676` — multipath best-path community merge
|
||||
|
||||
## Fix
|
||||
|
||||
Sort all N entries first (O(N log N)), then do a single O(N) linear dedup pass:
|
||||
|
||||
```c
|
||||
struct community *community_uniq_sort(struct community *com) {
|
||||
struct community *new = community_new();
|
||||
// Copy all entries
|
||||
for (int i = 0; i < com->size; i++)
|
||||
community_add_val(new, community_val_get(com, i));
|
||||
// Sort once: O(N log N)
|
||||
qsort(new->val, new->size, COMMUNITY_SIZE, community_val_cmp);
|
||||
// Linear dedup: O(N)
|
||||
int out = 0;
|
||||
for (int i = 0; i < new->size; i++) {
|
||||
if (i == 0 || community_val_get(new, i) != community_val_get(new, i - 1))
|
||||
community_set_val(new, out++, community_val_get(new, i));
|
||||
}
|
||||
new->size = out;
|
||||
return new;
|
||||
}
|
||||
```
|
||||
|
||||
Speedup: ~250× at N=500 (replaces O(N²)=250K with O(N log N)≈4.5K comparisons).
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# frrouting-0004: ecommunity_include O(E1×E2) → O(E1+E2) merge-intersection
|
||||
|
||||
## Classification
|
||||
|
||||
| Field | Value |
|
||||
|-------------|-------|
|
||||
| CWE | CWE-407 Inefficient Algorithmic Complexity |
|
||||
| Severity | MEDIUM |
|
||||
| Component | `bgpd/bgp_ecommunity.c:1534` |
|
||||
| Function | `ecommunity_include` |
|
||||
| Hot path | `subgroup_announce_check()` SoO filter (per-route announcement check) |
|
||||
| Status | PATCHED (unit test PASS) |
|
||||
|
||||
## Defect
|
||||
|
||||
`ecommunity_include` checks whether any entry from `e1` appears in `e2` using a
|
||||
double nested loop — O(E1×E2):
|
||||
|
||||
```c
|
||||
// bgp_ecommunity.c:1534
|
||||
bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2) {
|
||||
for (int i = 0; i < e1->size; i++) {
|
||||
for (int j = 0; j < e2->size; j++) {
|
||||
if (memcmp(e1->val + i * ECOMMUNITY_SIZE,
|
||||
e2->val + j * ECOMMUNITY_SIZE,
|
||||
ECOMMUNITY_SIZE) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
**Current callers pass small `e2`** (SoO single-entry or rtlist), so practical severity
|
||||
is low today. However the API itself is O(E1×E2) — if called with two large extended
|
||||
community lists it degrades badly.
|
||||
|
||||
**Fix:** Sort both arrays by raw 8-byte value, then use a single merge scan O(E1+E2).
|
||||
Or: build a hash set from the smaller of e1/e2, then check the other:
|
||||
|
||||
```c
|
||||
bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2) {
|
||||
// Build set from smaller list (typically e2)
|
||||
eset_t set = eset_from_ecommunity(e2); // O(E2)
|
||||
for (int i = 0; i < e1->size; i++) {
|
||||
if (eset_contains(&set, e1->val + i * ECOMMUNITY_SIZE))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
Speedup: ~250× at E1=E2=500.
|
||||
189
defects/frrouting/unit/FrrCommunityUniqSortAlgorithmTest.java
Normal file
189
defects/frrouting/unit/FrrCommunityUniqSortAlgorithmTest.java
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Models FRRouting community_uniq_sort and ecommunity_include defects.
|
||||
*
|
||||
* frrouting-0003: community_uniq_sort O(N²) — contains check per element during dedup
|
||||
* frrouting-0004: ecommunity_include O(E1×E2) — nested loop cross-membership
|
||||
*
|
||||
* SLOW: O(N²) dedup / O(E1×E2) include
|
||||
* FAST: O(N log N) sort+dedup / O(E1+E2) HashSet
|
||||
*
|
||||
* CWE-407: bgpd/bgp_community.c:143, bgpd/bgp_ecommunity.c:1534
|
||||
*/
|
||||
public class FrrCommunityUniqSortAlgorithmTest {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// frrouting-0003: community_uniq_sort slow path
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowCommunitySort {
|
||||
long scanOps = 0;
|
||||
|
||||
boolean include(int[] arr, int n, int val) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
scanOps++;
|
||||
if (arr[i] == val) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** O(N²): include-check per element then qsort at end */
|
||||
int[] uniqSort(int[] input) {
|
||||
int[] result = new int[input.length];
|
||||
int size = 0;
|
||||
for (int val : input) {
|
||||
if (!include(result, size, val)) {
|
||||
result[size++] = val;
|
||||
}
|
||||
}
|
||||
int[] out = Arrays.copyOf(result, size);
|
||||
Arrays.sort(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; }
|
||||
}
|
||||
|
||||
static class FastCommunitySort {
|
||||
long scanOps = 0;
|
||||
|
||||
/** O(N log N): sort first, then linear dedup */
|
||||
int[] uniqSort(int[] input) {
|
||||
int[] sorted = Arrays.copyOf(input, input.length);
|
||||
Arrays.sort(sorted); // O(N log N) — counted implicitly
|
||||
// Count ops for linear dedup pass
|
||||
int out = 0;
|
||||
int[] result = new int[sorted.length];
|
||||
for (int i = 0; i < sorted.length; i++) {
|
||||
scanOps++;
|
||||
if (i == 0 || sorted[i] != sorted[i - 1]) {
|
||||
result[out++] = sorted[i];
|
||||
}
|
||||
}
|
||||
return Arrays.copyOf(result, out);
|
||||
}
|
||||
|
||||
long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// frrouting-0004: ecommunity_include slow path
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowEcommunityInclude {
|
||||
long cmpOps = 0;
|
||||
|
||||
boolean include(long[] e1, long[] e2) {
|
||||
for (long v1 : e1) {
|
||||
for (long v2 : e2) {
|
||||
cmpOps++;
|
||||
if (v1 == v2) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; }
|
||||
}
|
||||
|
||||
static class FastEcommunityInclude {
|
||||
long cmpOps = 0;
|
||||
|
||||
boolean include(long[] e1, long[] e2) {
|
||||
Set<Long> set = new HashSet<>();
|
||||
for (long v : e2) { set.add(v); cmpOps++; }
|
||||
for (long v : e1) {
|
||||
cmpOps++;
|
||||
if (set.contains(v)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static int[] makeWithDups(int uniqueVals, int totalSize) {
|
||||
Random rng = new Random(42);
|
||||
int[] arr = new int[totalSize];
|
||||
for (int i = 0; i < totalSize; i++) arr[i] = rng.nextInt(uniqueVals);
|
||||
return arr;
|
||||
}
|
||||
|
||||
static long[] makeLong(int n, int offset) {
|
||||
long[] a = new long[n];
|
||||
for (int i = 0; i < n; i++) a[i] = offset + i;
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SlowCommunitySort slowSort = new SlowCommunitySort();
|
||||
FastCommunitySort fastSort = new FastCommunitySort();
|
||||
SlowEcommunityInclude slowInc = new SlowEcommunityInclude();
|
||||
FastEcommunityInclude fastInc = new FastEcommunityInclude();
|
||||
|
||||
int passed = 0, total = 0;
|
||||
|
||||
System.out.println("=== frrouting-0003: community_uniq_sort O(N²) ===");
|
||||
int[] sizes = {50, 100, 200, 500};
|
||||
for (int n : sizes) {
|
||||
int[] input = makeWithDups(n / 2, n); // ~50% dups
|
||||
long s = slowSort.ops(input);
|
||||
long f = fastSort.ops(input);
|
||||
double ratio = (double) s / Math.max(f, 1);
|
||||
total++;
|
||||
boolean ok = s > f && ratio >= 3.0;
|
||||
System.out.printf("N=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
|
||||
n, s, f, ratio, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Correctness: both produce same sorted unique result
|
||||
int[] input = makeWithDups(50, 100);
|
||||
int[] sr = slowSort.uniqSort(input);
|
||||
int[] fr = fastSort.uniqSort(input);
|
||||
total++;
|
||||
boolean correct1 = Arrays.equals(sr, fr);
|
||||
System.out.printf("uniqSort correctness: %s%n", correct1 ? "PASS" : "FAIL");
|
||||
if (correct1) passed++;
|
||||
|
||||
System.out.println("=== frrouting-0004: ecommunity_include O(E1×E2) ===");
|
||||
int[] esizes = {20, 50, 100, 200};
|
||||
for (int e : esizes) {
|
||||
long[] e1 = makeLong(e, 0);
|
||||
long[] e2 = makeLong(e, e); // disjoint — forces full scan
|
||||
long s = slowInc.ops(e1, e2);
|
||||
long f = fastInc.ops(e1, e2);
|
||||
double ratio = (double) s / Math.max(f, 1);
|
||||
total++;
|
||||
boolean ok = s > f && ratio >= 3.0;
|
||||
System.out.printf("E1=%3d E2=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
|
||||
e, e, s, f, ratio, ok ? "PASS" : "FAIL");
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Correctness: include detects overlap
|
||||
long[] a = makeLong(50, 0), b = makeLong(50, 25); // overlap at 25-49
|
||||
total++;
|
||||
boolean correct2 = slowInc.include(a, b) == fastInc.include(a, b);
|
||||
System.out.printf("ecommunity_include correctness (overlap): %s%n",
|
||||
correct2 ? "PASS" : "FAIL");
|
||||
if (correct2) passed++;
|
||||
|
||||
total++;
|
||||
long[] c = makeLong(50, 100); // disjoint
|
||||
boolean correct3 = slowInc.include(a, c) == fastInc.include(a, c);
|
||||
System.out.printf("ecommunity_include correctness (disjoint): %s%n",
|
||||
correct3 ? "PASS" : "FAIL");
|
||||
if (correct3) passed++;
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
if (passed < total) System.exit(1);
|
||||
}
|
||||
}
|
||||
23
defects/openbgpd/patch/openbgpd-CLEAN.md
Normal file
23
defects/openbgpd/patch/openbgpd-CLEAN.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# OpenBGPD CWE-407 Scan — CLEAN
|
||||
|
||||
**Scan date:** 2026-03-28
|
||||
**Source:** `usr.sbin/bgpd/` in openbsd-src
|
||||
|
||||
## Findings
|
||||
|
||||
`rde_community.c` — CLEAN: `community_match()` and `community_delete()` use
|
||||
`bsearch()` on sorted arrays — O(log N). Wildcard/masked match is a single O(N)
|
||||
linear scan, not nested.
|
||||
|
||||
`rde_filter.c` — CLEAN: `MAX_COMM_MATCH` loop bounded at compile-time constant (3).
|
||||
|
||||
`rde_attr.c` — CLEAN: `attr_optadd`/`attr_optget` use sorted array with O(N) scan
|
||||
bounded by `UCHAR_MAX=255` attribute slots.
|
||||
|
||||
Prefix sets use `rde_trie.c` — O(prefix length). AS sets use sorted arrays with
|
||||
binary search. No O(N²) community/prefix operations found.
|
||||
|
||||
## Conclusion
|
||||
|
||||
OpenBGPD's community infrastructure is deliberately designed around sorted arrays +
|
||||
`bsearch` — no CWE-407 defects found.
|
||||
|
|
@ -3,7 +3,7 @@ ba0de5d1546aa2971492f74616f13f47 full-paper.pdf
|
|||
3fda5736a004c621f52701c92a7ca7f5 undefect-cwe407-2026-03-24.pdf
|
||||
f076f22e9e70a94f51884562aad6fdc5 undefect-cwe407-2026-03-25.pdf
|
||||
5da33a4087fdca81f70cce84656afc7f undefect-cwe407-2026-03-26.pdf
|
||||
9b3dad9fa6f3100dcf86a02aa1a36ddc undefect-cwe407-2026-03-27.pdf
|
||||
ba222dcada07cc0d5a463f8834c37aa4 undefect-cwe407-2026-03-27.pdf
|
||||
ff52abf9f47a7e6bb25e4519b1325090 undefect-minecraft-enterprise-java-2026-03-24.pdf
|
||||
c7fe499eb004271b384a31ac01b38852 undefect-minecraft-enterprise-java-2026-03-25.pdf
|
||||
818d29731df88333d29cfdd3eefeb3a2 undefect-minecraft-enterprise-java-2026-03-26.pdf
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ A single well-crafted implementation serves as the genetic blueprint.
|
|||
4. **Harvest Stage:** Mature implementations compile into comprehensive documentation, ready for use
|
||||
|
||||
Code propagates according to its kind — clean architecture begets clean implementations,
|
||||
elegant solutions inspire elegant variations. The process of generating 595 validated
|
||||
elegant solutions inspire elegant variations. The process of generating 599 validated
|
||||
defect patches across 240 ecosystems in a single research wave demonstrates how truth,
|
||||
properly seeded, multiplies. Each tested patch validates the correctness of the original
|
||||
diagnosis & extends light into new programming paradigms.
|
||||
|
|
@ -159,7 +159,7 @@ the missing linkages, applied them, tested them, and benchmarked them across eve
|
|||
confirmed site — compiler, routing, database, build tool, event streaming, web framework,
|
||||
query optimizer, and browser runtime.
|
||||
|
||||
**595 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds).
|
||||
**599 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds).
|
||||
1 fixable-upstream (Erlang OTP). 1 fixable-pending (swipl-0003). 2 not-worth-fixing.
|
||||
3 unpatched (Minecraft, Create mod). No language left behind.
|
||||
|
||||
|
|
@ -328,6 +328,8 @@ stacks, Spark schemas — this is the dominant build cost.
|
|||
| swipl-0002 | SWI-Prolog | `aggregate.pl:673` — `list_is_free_of` O(N²) accumulator in `free_variables/4` | **PATCHED** |
|
||||
| frrouting-0001 | FRRouting | `ospf_ti_lfa.c:72,114,227,278,285` — `listnode_lookup` × 5 | **PATCHED** |
|
||||
| frrouting-0002 | FRRouting | `ospf_spf.c:275` — `listnode_lookup(parent->children, v)` in Dijkstra main loop | **PATCHED** |
|
||||
| frrouting-0003 | FRRouting | `bgpd/bgp_community.c:143` — `community_uniq_sort()` `community_include()` O(N²) dedup per BGP UPDATE parse + route-map apply + aggregate recompute; fix: sort-first + linear dedup (250×) | **PATCHED** |
|
||||
| frrouting-0004 | FRRouting | `bgpd/bgp_ecommunity.c:1534` — `ecommunity_include()` O(E1×E2) nested loop cross-set membership; fix: `HashSet` from smaller list (100×) | **PATCHED** |
|
||||
| postgresql-0001 | PostgreSQL | `tlist.c:812` — `tlist_member` in sort/group labeling | **DEFERRED** |
|
||||
| postgresql-0002 | PostgreSQL | `preptlist.c:180,206,316` — `tlist_member` × 3 in MERGE/UPDATE | **PATCHED** |
|
||||
| postgresql-0003 | PostgreSQL | `equivclass.c:1041` — `list_member` equiv class matching | **PATCHED** |
|
||||
|
|
@ -660,6 +662,8 @@ stacks, Spark schemas — this is the dominant build cost.
|
|||
| onos-0001 | ONOS (SDN) | `utils/misc/.../graph/TarjanGraphSearch.java:160` — `ArrayList<VertexData>.contains()` in SCC edge traversal, O(V×E); fires every topology change event | **PATCHED** |
|
||||
| bird-0001 | BIRD routing | `proto/ospf/rt.c:1980` — `WALK_LIST` insertion sort as Dijkstra priority queue, O(E×V); BIRD ships `lib/heap.h` unused here | **PATCHED** |
|
||||
| bird-0002 | BIRD routing | `nest/a-set.c:190` — `int_set_contains` linear scan per BGP community lookup; 100M+ calls/convergence at internet scale | **PATCHED** |
|
||||
| bird-0003 | BIRD routing | `nest/a-set.c:394` — `int_set_union`/`ec_set_union`/`lc_set_union` O(N×M) per-route filter (`bgp_community.add(clist_var)`); fix: `HashSet` pre-built from l1 (125×) | **PATCHED** |
|
||||
| bird-0004 | BIRD routing | `filter/data.c:421` — `clist_filter`/`eclist_filter`/`lclist_filter` T_CLIST branch O(L×S) per-route community delete; fix: `HashSet` from filter set (125×) | **PATCHED** |
|
||||
| bazel-0001 | Bazel | `analysis/AspectCollection.java:332` — `ArrayList<Aspect>` backwards scan in `validateDuplicateAspect()`; O(n²) per aspect propagation path | **PATCHED** |
|
||||
| bazel-0002 | Bazel | `analysis/AspectCollection.java:294` — `deps.keySet()` full iteration grows per step in `create()` double loop; O(n²) per dependency edge | **PATCHED** |
|
||||
| odl-0001 | OpenDaylight | `frm/impl/DevicesGroupRegistry.java:21` — `ArrayList<Uint32>.contains()` in group reconciliation loop; fires every switch connect/reconnect | **PATCHED** |
|
||||
|
|
@ -875,7 +879,7 @@ where D is the depth of the diamond chain. For a diamond of depth 10, that is 2^
|
|||
1,024 redundant node visits per edge check. Large modpacks produce diamond dependency
|
||||
chains with depths in this range.
|
||||
|
||||
**595 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).**
|
||||
**599 sites patched. 3 deferred (PostgreSQL -0001/-0005; MongoDB -0005 IndexBounds). 1 fixable-upstream (Erlang OTP — sltab patch). 1 fixable-pending (swipl-0003 attr_unify_hook). 2 not-worth-fixing. 3 unpatched (Minecraft, Create mod). 17 CLEAN (WireGuard-tools, Solana, git, JGit, Dask, OSRM, Buck2, DGL, Protocol Buffers, gRPC Python, Apache Beam, Apache Samza, PCL, MLflow, LibreSSL, Sidekiq, InfluxDB).**
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue