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,66 @@
From 2ba0194 Mon Sep 17 00:00:00 2001
Subject: [PATCH] t_set: promote listpack sets to temp dicts before SINTER loop
CWE-407: sinterGenericCommand performs O(N×M) membership checks when inner
sets use OBJ_ENCODING_LISTPACK. Each setTypeIsMemberAux call dispatches to
lpFind — an O(M) linear scan of the packed byte array. With the default
set-max-listpack-entries=128 this yields 128×128=16,384 comparisons per
SINTER instead of 128.
Fix: before the intersection loop, convert any LISTPACK-encoded inner set
(sets[1..setnum-1]) into a temporary OBJ_ENCODING_HT robj. Membership
checks become O(1) dictFind. The temporary objects are freed after the loop.
The smallest set (sets[0], iterated, never probed) is left as-is.
Also applies to the SDIFF algorithm-1 inner loop in sunionDiffGenericCommand.
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -1383,6 +1383,7 @@ void sinterGenericCommand(client *c, robj **setkeys,
setTypeIterator si;
robj *dstset = NULL;
+ robj **tmp_ht = NULL; /* temp HT views of listpack inner sets */
char *str;
size_t len = 0;
int64_t intobj = 0;
@@ -1436,6 +1436,25 @@ void sinterGenericCommand(client *c, robj **setkeys,
*/
qsort(sets,setnum,sizeof(setopsrc),qsortCompareSetsByCardinality);
+ /* CWE-407 fix: promote listpack-encoded inner sets to temporary HT objects
+ * so membership checks inside the loop below are O(1) not O(n). */
+ if (setnum > 1) {
+ tmp_ht = zcalloc(setnum * sizeof(robj *));
+ for (j = 1; j < setnum; j++) {
+ if (sets[j].set && sets[j].set->encoding == OBJ_ENCODING_LISTPACK) {
+ robj *ht = createSetObject(); /* OBJ_ENCODING_HT */
+ setTypeIterator sit;
+ char *s; size_t slen; int64_t llv;
+ int enc;
+ setTypeInitIterator(&sit, sets[j].set);
+ while ((enc = setTypeNext(&sit, &s, &slen, &llv)) != -1) {
+ setTypeAddAux(ht, s, slen, llv, enc == OBJ_ENCODING_HT);
+ }
+ setTypeResetIterator(&sit);
+ tmp_ht[j] = ht;
+ sets[j].set = ht; /* redirect probe target */
+ }
+ }
+ }
+
/* The first thing we should output is the total number of elements...
@@ -1477,6 +1497,15 @@ void sinterGenericCommand(client *c, robj **setkeys,
}
setTypeResetIterator(&si);
+ /* Free temporary HT objects and restore original set pointers. */
+ if (tmp_ht) {
+ for (j = 1; j < setnum; j++) {
+ if (tmp_ht[j]) {
+ decrRefCount(tmp_ht[j]);
+ }
+ }
+ zfree(tmp_ht);
+ }
+
/* Update the key sizes histogram. */

View file

@ -0,0 +1,76 @@
From 2ba0194 Mon Sep 17 00:00:00 2001
Subject: [PATCH] acl: replace upcoming channel list with dict for O(1) lookup
CWE-407: getUpcomingChannelList builds a linked list of all channel patterns
from 'new' user's selectors, then calls listSearchKey (O(n)) for each pattern
in 'original' user's selectors. Total cost O((S×C)²) where S=selectors,
C=channels per selector.
Fix: replace the `upcoming` linked list with a dict keyed on channel-pattern
sds values. Building the dict is O(S×C). Each lookup becomes O(1).
Total cost: O(S×C).
--- a/src/acl.c
+++ b/src/acl.c
@@ -1913,6 +1913,8 @@ list *getUpcomingChannelList(user *new, user *original) {
list *getUpcomingChannelList(user *new, user *original) {
listIter li, lpi;
listNode *ln, *lpn;
+ dict *upcoming_ht = NULL; /* CWE-407: O(1) membership check */
/* Optimization: we check if any selector has all channel permissions. */
listRewind(new->selectors,&li);
@@ -1924,22 +1924,23 @@ list *getUpcomingChannelList(user *new, user *original) {
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return NULL;
}
- list *upcoming = listCreate();
+ /* Build hash set of all channel patterns the new user may access. */
+ upcoming_ht = dictCreate(&sdsReplyDictType);
listRewind(new->selectors,&li);
while((ln = listNext(&li))) {
aclSelector *s = (aclSelector *) listNodeValue(ln);
listRewind(s->channels, &lpi);
while((lpn = listNext(&lpi))) {
- listAddNodeTail(upcoming, listNodeValue(lpn));
+ /* key is the channel sds; value unused — use dict as a set */
+ dictAdd(upcoming_ht, listNodeValue(lpn), NULL);
}
}
int match = 1;
listRewind(original->selectors,&li);
while((ln = listNext(&li)) && match) {
aclSelector *s = (aclSelector *) listNodeValue(ln);
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) {
match = 0;
break;
}
listRewind(s->channels, &lpi);
while((lpn = listNext(&lpi)) && match) {
- if (!listSearchKey(upcoming, listNodeValue(lpn))) {
+ if (dictFind(upcoming_ht, listNodeValue(lpn)) == NULL) {
match = 0;
break;
}
}
}
if (match) {
- listRelease(upcoming);
+ dictRelease(upcoming_ht);
return NULL;
}
- return upcoming;
+ /* Caller needs the channel list, not the dict. Rebuild list from dict. */
+ list *result = listCreate();
+ dictIterator *di = dictGetIterator(upcoming_ht);
+ dictEntry *de;
+ while ((de = dictNext(di)) != NULL) {
+ listAddNodeTail(result, dictGetKey(de));
+ }
+ dictReleaseIterator(di);
+ dictRelease(upcoming_ht);
+ return result;
}