redis-0001: src/acl.c getUpcomingChannelList() listSearchKey O(C×U) → dictFind O(1) 1 test: 1/1 PASS
88 lines
3.3 KiB
Java
88 lines
3.3 KiB
Java
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");
|
||
}
|
||
}
|