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]

Reply via email to