77 lines
2.8 KiB
Diff
77 lines
2.8 KiB
Diff
# UNDF: UNDF-2026-000000261
|
||
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;
|
||
}
|