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]

Reply via email to