From 924849f55b7641ebd8eaacdb3d8c9f18e919d020 Mon Sep 17 00:00:00 2001 From: junhyeong9812 Date: Tue, 16 Jun 2026 09:43:39 +0900 Subject: [PATCH] Avoid divide-by-zero in ExponentialBackOff jitter When an ExponentialBackOff is configured with an initialInterval of 0 and a positive jitter, the first nextBackOff() evaluated (jitter * (interval / initialInterval)) performs integer division by zero and throws an ArithmeticException. Both initialInterval = 0 and jitter > 0 are individually accepted configurations -- with jitter = 0, an initialInterval of 0 already yields a delay of 0 -- so the combination should not throw. This commit addresses that by guarding the division so that no jitter scaling is applied when initialInterval is 0, leaving the behavior for positive intervals unchanged. Closes gh-36932 Signed-off-by: junhyeong9812 --- .../util/backoff/ExponentialBackOff.java | 2 +- .../util/ExponentialBackOffTests.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java b/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java index fd48e023e15..2747703bd6e 100644 --- a/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java +++ b/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java @@ -311,7 +311,7 @@ public class ExponentialBackOff implements BackOff { long jitter = getJitter(); if (jitter > 0) { long initialInterval = getInitialInterval(); - long applicableJitter = jitter * (interval / initialInterval); + long applicableJitter = jitter * (initialInterval > 0 ? (interval / initialInterval) : 1); long min = Math.max(interval - applicableJitter, initialInterval); long max = Math.min(interval + applicableJitter, getMaxInterval()); return min + (long) (Math.random() * (max - min)); diff --git a/spring-core/src/test/java/org/springframework/util/ExponentialBackOffTests.java b/spring-core/src/test/java/org/springframework/util/ExponentialBackOffTests.java index 40271f5c744..095ff93d9ed 100644 --- a/spring-core/src/test/java/org/springframework/util/ExponentialBackOffTests.java +++ b/spring-core/src/test/java/org/springframework/util/ExponentialBackOffTests.java @@ -27,6 +27,7 @@ import org.springframework.util.backoff.ExponentialBackOff; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNoException; /** * Tests for {@link ExponentialBackOff}. @@ -118,6 +119,20 @@ class ExponentialBackOffTests { assertThatIllegalArgumentException().isThrownBy(() -> backOff.setMultiplier(0.9)); } + @Test + void jitterWithZeroInitialInterval() { + // 'initialInterval = 0' and 'jitter > 0' are both individually accepted + // configurations, so their combination must not throw. With initialInterval + // of 0, the first nextBackOff() previously evaluated 'jitter * (0 / 0)', + // resulting in an integer division by zero. + ExponentialBackOff backOff = new ExponentialBackOff(); + backOff.setInitialInterval(0); + backOff.setJitter(100); + + BackOffExecution execution = backOff.start(); + assertThatNoException().isThrownBy(execution::nextBackOff); + } + @Test void maxIntervalReachedImmediately() { ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);