This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit dc25b0914e9db993ad8668b81afaf7abf9c01bbd Author: opencode <[email protected]> AuthorDate: Sun Oct 11 21:52:00 2026 +0200 Fix a data race in the lazy initialisation of the auto-generated rate limit policy name RateLimiterBase.getPolicyName() lazily initialised the plain, non-volatile field policyName without any synchronisation. When the associated RateLimitFilter was configured with exposeHeaders=true and no explicit policyName, every request thread called getPolicy() and getQuota() (both of which resolve the policy name) concurrently, so several early requests could each pass the null check and publish a different auto-generated name, consuming multiple values of the static counter in the process. The RateLimit-Policy and RateLimit header fields could then disagree with each other within a single response and across subsequent responses. Declare the field volatile and guard the generation with the object monitor using the usual double-checked pattern. A thread that loses the race now blocks and returns the winner's published name, so exactly one index value is consumed per limiter instance and all callers observe the same name. setPolicyName() is only invoked from the filter init path, which happens before the filter chain is published, so it does not need the monitor. --- java/org/apache/catalina/util/RateLimiterBase.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/java/org/apache/catalina/util/RateLimiterBase.java b/java/org/apache/catalina/util/RateLimiterBase.java index 5ac8884a94..7108d4ec5b 100644 --- a/java/org/apache/catalina/util/RateLimiterBase.java +++ b/java/org/apache/catalina/util/RateLimiterBase.java @@ -44,8 +44,9 @@ public abstract class RateLimiterBase implements RateLimiter { int duration; int actualDuration; - // Initial policy name can be rewritten by setPolicyName() - private String policyName = null; + // Initial policy name can be rewritten by setPolicyName(). Lazily initialized in getPolicyName() with the + // object monitor guarding the auto-generation, so the field itself is volatile for safe publication. + private volatile String policyName; /* * The self-owned utility executor, will be instantiated only when ScheduledThreadPoolExecutor is absent during @@ -64,10 +65,17 @@ public abstract class RateLimiterBase implements RateLimiter { @Override public String getPolicyName() { - if (policyName == null) { - policyName = getDefaultPolicyName() + "-" + index.incrementAndGet(); + String name = policyName; + if (name == null) { + synchronized (this) { + name = policyName; + if (name == null) { + name = getDefaultPolicyName() + "-" + index.incrementAndGet(); + policyName = name; + } + } } - return policyName; + return name; } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
