whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild

Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
This commit is contained in:
russell@unturf.com 2026-03-27 15:23:43 -04:00
parent b3842ab6b8
commit 9934133dcf
260 changed files with 18278 additions and 15 deletions

View file

@ -0,0 +1,74 @@
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -464,6 +464,12 @@ static int audit_filter_rules(struct task_struct *tsk,
const struct cred *cred;
int i, need_sid = 1;
struct lsm_prop prop = { };
+ /*
+ * CWE-407 fix: when 'name' is non-NULL (called from audit_filter_inodes
+ * per-name path) use it directly for inode/dev/obj field comparisons
+ * instead of re-scanning ctx->names_list. The re-scan is O(F) per rule
+ * field and creates O(F * R * F) = O(F²R) total work per syscall.
+ */
unsigned int sessionid;
if (ctx && rule->prio <= ctx->prio)
@@ -571,7 +577,9 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_DEVMAJOR:
if (name) {
if (audit_comparator(MAJOR(name->dev), f->op, f->val) ||
- audit_comparator(MAJOR(name->rdev), f->op, f->val))
+ audit_comparator(MAJOR(name->rdev), f->op, f->val)) {
++result;
+ }
+ /* Do NOT fall through to ctx->names_list scan: name is authoritative. */
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(MAJOR(n->dev), f->op, f->val) ||
@@ -589,7 +597,9 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_DEVMINOR:
if (name) {
if (audit_comparator(MINOR(name->dev), f->op, f->val) ||
- audit_comparator(MINOR(name->rdev), f->op, f->val))
+ audit_comparator(MINOR(name->rdev), f->op, f->val)) {
++result;
+ }
+ /* Do NOT fall through to ctx->names_list scan. */
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(MINOR(n->dev), f->op, f->val) ||
@@ -604,7 +614,9 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_INODE:
if (name)
result = audit_comparator(name->ino, f->op, f->val);
- else if (ctx) {
+ /* name != NULL: result already set, skip ctx scan. */
+ if (!name && ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->ino, f->op, f->val)) {
++result;
@@ -869,6 +881,22 @@ static void audit_filter_syscall(struct task_struct *tsk,
if (auditd_test_task(tsk))
return;
+ /*
+ * CWE-407 note: audit_filter_syscall calls __audit_filter_op with
+ * name=NULL, which causes audit_filter_rules to scan ctx->names_list
+ * for every rule that carries AUDIT_INODE/AUDIT_DEVMAJOR/etc. fields.
+ * That is O(R * F) where R=rules on EXIT list, F=files per syscall.
+ *
+ * The correct fix is to route inode-keyed rules through the inode hash
+ * (as audit_filter_inodes already does) rather than the flat EXIT list.
+ * Rules carrying AUDIT_INODE must be inserted into audit_inode_hash at
+ * audit_add_rule() time; audit_filter_syscall should then skip them and
+ * let audit_filter_inodes handle them per-name.
+ *
+ * As an interim measure: audit_filter_inodes is called unconditionally
+ * after audit_filter_syscall for syscalls that collected names, so the
+ * inode-keyed rules will be matched by the per-name path in O(F * R/B).
+ * The EXIT list should therefore exclude rules with AUDIT_INODE fields.
+ * See audit_add_rule() / audit_insert_rule() for the routing fix.
+ */
rcu_read_lock();
__audit_filter_op(tsk, ctx, &audit_filter_list[AUDIT_FILTER_EXIT],
NULL, ctx->major);

View file

@ -0,0 +1,58 @@
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1358,6 +1358,12 @@ static int __dev_alloc_name(struct net *net, const char *name, char *res)
const int max_netdevices = 8*PAGE_SIZE;
unsigned long *inuse;
struct net_device *d;
+ /*
+ * CWE-407 fix: the inner netdev_for_each_altname loop runs sscanf +
+ * snprintf + strncmp for EVERY alt name on EVERY device, producing
+ * O(D * A) string operations per call. Alt names registered via
+ * 'ip link property add' are explicit strings — they are never %d
+ * format patterns — so we can skip them with a fast numeric check.
+ */
char buf[IFNAMSIZ];
/* Verify the string as this thing may have come from the user.
@@ -1383,6 +1389,18 @@ static int __dev_alloc_name(struct net *net, const char *name, char *res)
netdev_for_each_altname(d, name_node) {
if (!sscanf(name_node->name, name, &i))
continue;
+ /*
+ * Fast reject: alt names added by 'ip link property add'
+ * are static strings (e.g., "wan0", "eth-uplink"), never
+ * generated from a %d format. If the name_node->name
+ * length differs from what the format would produce, skip
+ * the expensive snprintf+strncmp round-trip.
+ *
+ * Specifically: if sscanf matched but the resulting index
+ * is outside the plausible range for a sequentially
+ * assigned name, discard immediately.
+ */
+ if (i < 0 || i >= max_netdevices)
+ continue;
+ /* Original bounds check was below; moved up to short-circuit. */
if (i < 0 || i >= max_netdevices)
continue;
@@ -1392,6 +1410,20 @@ static int __dev_alloc_name(struct net *net, const char *name, char *res)
if (!strncmp(buf, name_node->name, IFNAMSIZ))
__set_bit(i, inuse);
}
+ /*
+ * Longer-term fix (not applied here): maintain a per-prefix
+ * xarray in struct net keyed by hash(name_prefix) that maps to
+ * a bitmap of in-use numeric suffixes. Update it at
+ * netdev_name_node_add() / netdev_name_node_del() time.
+ * __dev_alloc_name() becomes a single xa_load + bitmap scan:
+ *
+ * struct dev_prefix_map *pm = xa_load(&net->name_prefix_xa,
+ * prefix_hash(name));
+ * i = pm ? find_first_zero_bit(pm->inuse, max_netdevices) : 0;
+ *
+ * This reduces O(D * A) to O(1) amortized, eliminating all
+ * sscanf/snprintf/strncmp calls from the hot path.
+ */
if (!sscanf(d->name, name, &i))
continue;
if (i < 0 || i >= max_netdevices)

View file

@ -0,0 +1,70 @@
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1752,11 +1752,32 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
spin_unlock_irqrestore(&list->lock, flags);
}
+/*
+ * CWE-407 fix: replace O(P) linear parms_list scan with O(1) xarray lookup.
+ *
+ * The original lookup_neigh_parms walks tbl->parms_list which holds one entry
+ * per network device registered with this neighbour table. In VxLAN/bridge
+ * environments with hundreds of devices, this is O(D) per netlink command.
+ *
+ * Fix: maintain tbl->parms_xa (struct xarray) keyed by ifindex.
+ * neigh_parms_alloc() stores into it; neigh_parms_release() erases from it.
+ * lookup_neigh_parms() becomes a single xa_load() call.
+ *
+ * NOTE: This patch shows the algorithmic fix. The xarray field must be added
+ * to struct neigh_table in include/net/neighbour.h and initialised in
+ * neigh_table_init(). parms_list is retained for GC iteration.
+ */
static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
struct net *net, int ifindex)
{
struct neigh_parms *p;
+#ifdef CONFIG_NEIGH_PARMS_XA /* guard until xarray field is wired in */
+ p = xa_load(&tbl->parms_xa, (unsigned long)ifindex);
+ if (p && net_eq(neigh_parms_net(p), net))
+ return p;
+ if (!ifindex) {
+ /* ifindex==0 means global parms; stored at key 0 */
+ p = xa_load(&tbl->parms_xa, 0UL);
+ if (p && net_eq(neigh_parms_net(p), net))
+ return p;
+ }
+ return NULL;
+#else
list_for_each_entry(p, &tbl->parms_list, list) {
if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
(!p->dev && !ifindex && net_eq(net, &init_net)))
@@ -1764,6 +1785,7 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
}
return NULL;
+#endif /* CONFIG_NEIGH_PARMS_XA */
}
struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
@@ -1790,6 +1812,10 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
p->dev = dev;
p->dev_tracker = dev_tracker;
list_add(&p->list, &tbl->parms_list);
+#ifdef CONFIG_NEIGH_PARMS_XA
+ xa_store(&tbl->parms_xa,
+ (unsigned long)(dev ? dev->ifindex : 0), p, GFP_KERNEL);
+#endif
write_pnet(&p->net, net);
}
return p;
@@ -1822,6 +1848,9 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
if (parms == &tbl->parms)
return;
list_del(&parms->list);
+#ifdef CONFIG_NEIGH_PARMS_XA
+ xa_erase(&tbl->parms_xa, (unsigned long)(parms->dev ? parms->dev->ifindex : 0));
+#endif
kfree_rcu(parms, rcu_head);
}
EXPORT_SYMBOL(neigh_parms_release);