67 lines
2.7 KiB
Diff
67 lines
2.7 KiB
Diff
# UNDF: UNDF-2026-000000260
|
||
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. */
|