diff --git a/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java b/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java index 2a44b5f297b..e57113ef189 100644 --- a/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java +++ b/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java @@ -22,6 +22,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.common.net.InetAddresses; +import com.google.common.primitives.UnsignedInteger; import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; @@ -115,6 +116,7 @@ private static boolean isEnabledXdsDualStack() { private static EdsUpdate processClusterLoadAssignment(ClusterLoadAssignment assignment) throws ResourceInvalidException { Map> priorities = new HashMap<>(); + Map localityWeightSums = new HashMap<>(); Map localityLbEndpointsMap = new LinkedHashMap<>(); Set endpointAddresses = new HashSet<>(); List dropOverloads = new ArrayList<>(); @@ -133,6 +135,14 @@ private static EdsUpdate processClusterLoadAssignment(ClusterLoadAssignment assi LocalityLbEndpoints localityLbEndpoints = structOrError.getStruct(); int priority = localityLbEndpoints.priority(); maxPriority = Math.max(maxPriority, priority); + long localityWeightSum = localityWeightSums.getOrDefault(priority, 0L) + + localityLbEndpoints.localityWeight(); + if (localityWeightSum > UnsignedInteger.MAX_VALUE.longValue()) { + throw new ResourceInvalidException( + "Sum of weights of localities at the same priority " + priority + + " exceeded maximal value"); + } + localityWeightSums.put(priority, localityWeightSum); // Note endpoints with health status other than HEALTHY and UNKNOWN are still // handed over to watching parties. It is watching parties' responsibility to // filter out unhealthy endpoints. See EnvoyProtoData.LbEndpoint#isHealthy(). diff --git a/xds/src/test/java/io/grpc/xds/GrpcXdsClientImplDataTest.java b/xds/src/test/java/io/grpc/xds/GrpcXdsClientImplDataTest.java index dc9590aed51..418a7457564 100644 --- a/xds/src/test/java/io/grpc/xds/GrpcXdsClientImplDataTest.java +++ b/xds/src/test/java/io/grpc/xds/GrpcXdsClientImplDataTest.java @@ -1080,6 +1080,39 @@ public void parseLocalityLbEndpoints_withHealthyEndpoints() throws ResourceInval 100, 1, ImmutableMap.of())); } + @Test + public void processClusterLoadAssignment_localityWeightSumExceedsUint32Max() { + // Sum of locality weights at priority 1 is 2147483647 + 2147483647 + 2 = 4294967296. + io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment assignment = + io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment.newBuilder() + .setClusterName("cluster-foo") + .addEndpoints(buildLocalityLbEndpoints("region1", "172.14.14.1", 1, 0)) + .addEndpoints(buildLocalityLbEndpoints("region2", "172.14.14.2", Integer.MAX_VALUE, 1)) + .addEndpoints(buildLocalityLbEndpoints("region3", "172.14.14.3", Integer.MAX_VALUE, 1)) + .addEndpoints(buildLocalityLbEndpoints("region4", "172.14.14.4", 2, 1)) + .build(); + ResourceInvalidException ex = assertThrows( + ResourceInvalidException.class, + () -> XdsEndpointResource.getInstance().doParse(getXdsResourceTypeArgs(true), assignment)); + assertThat(ex).hasMessageThat().isEqualTo( + "Sum of weights of localities at the same priority 1 exceeded maximal value"); + } + + private static io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints + buildLocalityLbEndpoints(String region, String address, int localityWeight, int priority) { + return io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints.newBuilder() + .setLocality(Locality.newBuilder().setRegion(region)) + .setLoadBalancingWeight(UInt32Value.of(localityWeight)) + .setPriority(priority) + .addLbEndpoints(io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint.newBuilder() + .setEndpoint(Endpoint.newBuilder() + .setAddress(Address.newBuilder() + .setSocketAddress( + SocketAddress.newBuilder().setAddress(address).setPortValue(8888)))) + .setHealthStatus(io.envoyproxy.envoy.config.core.v3.HealthStatus.HEALTHY)) + .build(); + } + @Test public void parseLocalityLbEndpoints_onlyPermitIp() { io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints proto =