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 c68697c350c80e255248a0210bac2b109db72940
Author: opencode <[email protected]>
AuthorDate: Sun Oct 11 22:02:23 2026 +0200

    Harden RateLimiterBase.destroy() against a limiter whose configuration 
failed
    
    destroy() dereferenced bucketCounter unconditionally. That field is
    only assigned once newCounterInstance() returns, so a limiter whose
    setFilterConfig() failed part-way - for example a custom RateLimiter
    whose counter constructor rejected the configured duration, or an
    embedded caller driving the public RateLimiter API directly with an
    invalid duration - would throw a NullPointerException from destroy()
    instead of simply doing nothing, masking the original configuration
    failure.
    
    Skip the counter teardown when it was never created. The shutdown of
    internalExecutorService is a separate, already-guarded block and it
    still runs, which matters because the executor is assigned before
    the counter, so a failed setFilterConfig() that had created the
    internal executor still gets it shut down. Note that the standard
    filter lifecycle does not reach this case: an ApplicationFilterConfig
    whose init() threw is never registered, so the container never
    destroys that filter. This change hardens the public API for direct
    and embedded use.
---
 java/org/apache/catalina/util/RateLimiterBase.java | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/catalina/util/RateLimiterBase.java 
b/java/org/apache/catalina/util/RateLimiterBase.java
index 383def62b2..27930045d9 100644
--- a/java/org/apache/catalina/util/RateLimiterBase.java
+++ b/java/org/apache/catalina/util/RateLimiterBase.java
@@ -118,7 +118,11 @@ public abstract class RateLimiterBase implements 
RateLimiter {
 
     @Override
     public void destroy() {
-        bucketCounter.destroy();
+        // bucketCounter remains null if setFilterConfig() failed before it 
was assigned, e.g. a counter
+        // constructor rejected the configured duration. Do not mask that 
failure with an NPE here.
+        if (bucketCounter != null) {
+            bucketCounter.destroy();
+        }
         if (internalExecutorService != null) {
             try {
                 internalExecutorService.shutdown();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to