This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 14077dd53fdb550ff1ed1a8063cd321b7e7fdb69 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 758a653145..383def62b2 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]
