java-topology/defects/redis/unit/RedisTest.java
russell@unturf.com 2ffc2001ab wave17a: redis-0001 acl.c getUpcomingChannelList listSearchKey → dict (6x, MEDIUM)
redis-0001: src/acl.c getUpcomingChannelList() listSearchKey O(C×U) → dictFind O(1)
1 test: 1/1 PASS
2026-03-30 07:46:16 -04:00

88 lines
3.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import java.util.*;
/**
* CWE-407 unit test for Redis acl.c defect.
*
* redis-0001: src/acl.c getUpcomingChannelList()
* list *upcoming built from new ACL channels, then checked with
* listSearchKey(upcoming, channel) — O(U) per original channel, O(C×U) total.
* Fix: build dict *upcoming_set alongside list for O(1) exact-match lookup.
*/
public class RedisTest {
// Simulate listSearchKey approach (defect)
static boolean isChannelInUpcomingList(List<String> upcoming, String channel) {
for (String c : upcoming) { // O(U) linear scan
if (c.equals(channel)) return true;
}
return false;
}
static boolean getUpcomingChannelList_list(List<String> newChannels, List<String> originalChannels) {
// Build upcoming list (O(U))
List<String> upcoming = new ArrayList<>(newChannels);
// Check each original channel against upcoming: O(C × U)
boolean match = true;
for (String ch : originalChannels) {
if (!isChannelInUpcomingList(upcoming, ch)) {
match = false;
break;
}
}
return match;
}
// Simulate dict/set approach (fix)
static boolean getUpcomingChannelList_dict(List<String> newChannels, List<String> originalChannels) {
// Build upcoming set for O(1) lookup
Set<String> upcoming_set = new HashSet<>(newChannels);
boolean match = true;
for (String ch : originalChannels) {
if (!upcoming_set.contains(ch)) { // O(1) lookup
match = false;
break;
}
}
return match;
}
static void testRedis0001() throws Exception {
int U = 1000; // upcoming channels (new ACL)
int C = 1000; // original channels to check
List<String> newChannels = new ArrayList<>();
for (int i = 0; i < U; i++) newChannels.add("chan:" + i);
// original channels: 500 overlap + 500 new (forces full scan in list version)
List<String> originalChannels = new ArrayList<>();
for (int i = 0; i < C / 2; i++) originalChannels.add("chan:" + i);
for (int i = 0; i < C / 2; i++) originalChannels.add("extra:" + i);
// correctness
boolean r1 = getUpcomingChannelList_list(newChannels, originalChannels);
boolean r2 = getUpcomingChannelList_dict(newChannels, originalChannels);
assert r1 == r2 : "list and dict must agree: " + r1 + " vs " + r2;
// performance
long t0 = System.nanoTime();
for (int r = 0; r < 500; r++) getUpcomingChannelList_list(newChannels, originalChannels);
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 500; r++) getUpcomingChannelList_dict(newChannels, originalChannels);
long tDict = System.nanoTime() - t0;
double ratio = (double) tList / tDict;
System.out.printf("redis-0001: list=%.3fs dict=%.3fs ratio=%.1f×%n",
tList / 1e9, tDict / 1e9, ratio);
assert ratio > 3 : "Expected >3× speedup, got " + ratio;
System.out.println("PASS redis-0001");
}
public static void main(String[] args) throws Exception {
testRedis0001();
System.out.println("ALL PASS");
}
}