distlib (3, Python), redmine (3, Ruby), grape (3, Ruby), solc (3, Solidity/C++), grpc-java (3, Java).
5.9 KiB
gRPC-Java — CWE-407 Disclosure Brief
2026-04-13 · Patch available — awaiting upstream merge
Finding
Three O(n²) defects in gRPC-Java across the xDS priority load balancer, xDS client authority resolution, and OkHttp TLS cipher suite negotiation. All patched. Two defects fire during xDS control plane operations; one fires during TLS handshake setup.
The Defects
grpc-java-0001 (PATCHED — HIGH): xds/src/main/java/io/grpc/xds/PriorityLoadBalancer.java:65
// In PriorityLoadBalancer.handleNameResolutionError() — fires on every name resolution error:
for (ChildLbState child : childValues) {
if (priorityNames.contains(child.priority)) { // List.contains() — O(n) per child
child.lb.handleNameResolutionError(error);
gotoTransientFailure = false;
}
}
priorityNames is a List<String>. List.contains() performs a linear scan for every child load balancer state on every name resolution error. With C children and P priorities: O(C x P) per error event.
grpc-java-0002 (PATCHED — MEDIUM): xds/src/main/java/io/grpc/xds/client/XdsClientImpl.java:1091
// In XdsClientImpl.getActiveAuthorities() — called from cleanUpResourceTimers and onControlPlaneClientError:
List<String> asList = activatedCpClients.entrySet().stream()
.filter(...)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return (asList.size() < 100) ? asList : new HashSet<>(asList);
Returns a List when authority count drops below 100, causing O(n) contains() calls in the double-loop callers (cleanUpResourceTimers, onControlPlaneClientError). With A authorities, S subscriptions, T resource types: O(A x S x T) when list path fires.
grpc-java-0003 (PATCHED — MEDIUM): okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Util.java:59
// In Util.intersect() — fires during TLS cipher suite negotiation:
for (T a : first) {
for (T b : second) {
if (a.equals(b)) {
result.add(b);
break;
}
}
}
Classic nested-loop intersection. first and second are cipher suite arrays. With |F| client suites and |S| server suites: O(|F| x |S|) comparisons on every TLS handshake.
Complexity Proof
grpc-java-0001: At C=50 children, P=20 priorities:
- Defective: 50 × 20 = 1,000 comparisons per error event
- Fixed: 50 × O(1) HashSet lookups = 50 operations
- 20x op reduction per error event.
grpc-java-0002: At A=200 authorities, used in double-loop with S=50 subscriptions, T=5 types:
- Defective: 50 × 5 × 200 = 50,000 comparisons (when list path fires at <100 authorities)
- Fixed: always HashSet, 50 × 5 = 250 lookups at O(1)
- 200x op reduction.
grpc-java-0003: At |F|=30 client suites, |S|=40 server suites:
- Defective: 30 × 40 = 1,200 comparisons
- Fixed: 40 (build set) + 30 (lookups) = 70 operations
- 17x op reduction per TLS handshake.
Impact
gRPC-Java powers service-to-service communication at Google, Netflix, Uber, Square, and thousands of microservice architectures worldwide. The xDS load balancing subsystem (grpc-java-0001, grpc-java-0002) handles service mesh control plane interactions for every gRPC client using xDS-based service discovery (Envoy, Istio, Traffic Director).
grpc-java-0001 fires on every name resolution error, which can cascade during service outages. In degraded network conditions, quadratic cost compounds with error frequency. grpc-java-0002 fires during resource timer cleanup and control plane error handling, both of which run frequently in large xDS deployments.
grpc-java-0003 fires during every TLS handshake when using the OkHttp transport. High-connection-rate services (short-lived connections, frequent reconnects) pay this cost repeatedly.
The Fix
grpc-java-0001: Add priorityNamesSet HashSet alongside the list:
// Before
if (priorityNames.contains(child.priority)) {
// After
// CWE-407 fix: HashSet for O(1) contains() instead of O(n) List scan.
private Set<String> priorityNamesSet = new HashSet<>();
priorityNamesSet = new HashSet<>(config.priorities);
if (priorityNamesSet.contains(child.priority)) {
grpc-java-0002: Always return HashSet from getActiveAuthorities():
// Before
return (asList.size() < 100) ? asList : new HashSet<>(asList);
// After
// CWE-407 fix: always return HashSet for O(1) contains().
return activatedCpClients.entrySet().stream()
.filter(...)
.map(Map.Entry::getKey)
.collect(Collectors.toCollection(HashSet::new));
grpc-java-0003: Build HashSet of second array for O(1) membership:
// Before
for (T a : first) {
for (T b : second) {
if (a.equals(b)) { result.add(b); break; }
}
}
// After
// CWE-407 fix: HashSet for O(1) membership test.
LinkedHashSet<T> secondSet = new LinkedHashSet<>(Arrays.asList(second));
for (T a : first) {
if (secondSet.contains(a)) {
result.add(a);
}
}
Patch
Fix available: defects/grpc-java/patch/grpc-java-0001-priority-lb-priority-names-list-contains.patch, defects/grpc-java/patch/grpc-java-0002-xds-client-get-active-authorities-list-contains.patch, defects/grpc-java/patch/grpc-java-0003-okhttp-util-intersect-nested-loop.patch
Three patches across PriorityLoadBalancer.java, XdsClientImpl.java, and Util.java.
grpc-java-0001: 20x speedup at C=50, P=20. grpc-java-0002: 200x speedup at A=200. grpc-java-0003: 17x speedup at |F|=30, |S|=40.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a tracker reference (grpc/grpc-java).
- Assess severity — grpc-java-0001 fires on every name resolution error; grpc-java-0002 fires during xDS control plane error handling.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the gRPC-Java team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.