java-topology/defects/redis/unit/RedisAclChannelCheckAlgorithmTest.java

119 lines
4.1 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.

package unit;
import java.util.*;
/**
* Models Redis ACLCheckChannelAgainstList per-command channel pattern check.
*
* SLOW: O(C×P) — for each channel arg, scan all P patterns linearly via linked list.
* FAST: O(C+P) — pre-build HashSet<exact> + List<glob> from selector; O(1) exact lookup.
*
* CWE-407: src/acl.c:1652,1694 (ACLCheckChannelAgainstList, ACLSelectorCheckCmd)
*/
public class RedisAclChannelCheckAlgorithmTest {
// Simplified match: exact match only (no glob) for benchmark purposes
// In production, glob patterns need fnmatch — we model exact-pattern dominance
static class SlowAclSelector {
final List<String> patterns; // linked-list analog
long cmpOps = 0;
SlowAclSelector(List<String> patterns) { this.patterns = patterns; }
boolean channelAllowed(String channel) {
for (String pat : patterns) {
cmpOps++;
if (pat.equals(channel)) return true; // exact match (simplification)
}
return false;
}
long checkCommand(List<String> channels) {
cmpOps = 0;
for (String ch : channels) channelAllowed(ch);
return cmpOps;
}
}
static class FastAclSelector {
final Set<String> exactPatterns; // hash set for O(1) exact match
long cmpOps = 0;
FastAclSelector(List<String> patterns) {
exactPatterns = new HashSet<>(patterns);
cmpOps = patterns.size(); // build cost
}
boolean channelAllowed(String channel) {
cmpOps++;
return exactPatterns.contains(channel);
}
long checkCommand(List<String> channels) {
cmpOps = 0;
for (String ch : channels) channelAllowed(ch);
return cmpOps;
}
}
static List<String> makePatterns(int n) {
List<String> p = new ArrayList<>();
for (int i = 0; i < n; i++) p.add("channel:" + i);
return p;
}
static List<String> makeChannels(int c, int offset) {
List<String> ch = new ArrayList<>();
for (int i = 0; i < c; i++) ch.add("channel:" + (offset + i));
return ch;
}
public static void main(String[] args) {
int passed = 0, total = 0;
System.out.println("=== redis-0004: ACLCheckChannelAgainstList O(C×P) ===");
// Test various (P, C) combinations
int[][] cases = {{100, 10}, {500, 10}, {1000, 10}, {500, 50}};
for (int[] cs : cases) {
int p = cs[0], c = cs[1];
List<String> patterns = makePatterns(p);
// Channels not in patterns (worst case — full scan each time)
List<String> channels = makeChannels(c, p + 1000);
SlowAclSelector slow = new SlowAclSelector(patterns);
FastAclSelector fast = new FastAclSelector(patterns);
long s = slow.checkCommand(channels);
long f = fast.checkCommand(channels);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 5.0;
System.out.printf("P=%4d C=%3d slow=%8d fast=%5d ratio=%7.1fx %s%n",
p, c, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both allow/deny same channels
List<String> pats = makePatterns(100);
SlowAclSelector slow = new SlowAclSelector(pats);
FastAclSelector fast = new FastAclSelector(pats);
// Allowed channel
total++;
boolean correct1 = slow.channelAllowed("channel:42") == fast.channelAllowed("channel:42");
System.out.printf("allow correctness (channel:42): %s%n", correct1 ? "PASS" : "FAIL");
if (correct1) passed++;
// Denied channel
total++;
boolean correct2 = slow.channelAllowed("channel:9999") == fast.channelAllowed("channel:9999");
System.out.printf("deny correctness (channel:9999): %s%n", correct2 ? "PASS" : "FAIL");
if (correct2) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}