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 caa8ba60082f18c3258492b865e18c1b01935b60
Author: opencode <[email protected]>
AuthorDate: Mon Oct 12 12:54:25 2026 +0200

    Reject a SemaphoreValve concurrency lower than one
    
    The setter previously rejected only negative values, so a concurrency of
    zero was accepted. The semaphore then started with no permits and, with
    the default blocking mode, every request waited forever to acquire one,
    exhausting the container threads and making the container unresponsive
    without any configuration error being reported.
    
    Reject a concurrency lower than one, and document the minimum in the
    attribute documentation.
---
 .../org/apache/catalina/valves/SemaphoreValve.java |  8 ++++---
 .../apache/catalina/valves/TestSemaphoreValve.java | 25 ++++++++++++++++++++++
 webapps/docs/config/valve.xml                      |  3 ++-
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/java/org/apache/catalina/valves/SemaphoreValve.java 
b/java/org/apache/catalina/valves/SemaphoreValve.java
index 274138926f..200fc76efc 100644
--- a/java/org/apache/catalina/valves/SemaphoreValve.java
+++ b/java/org/apache/catalina/valves/SemaphoreValve.java
@@ -65,7 +65,9 @@ public class SemaphoreValve extends ValveBase {
 
 
     /**
-     * Concurrency level of the semaphore.
+     * Concurrency level of the semaphore. The value must be at least one,
+     * since a semaphore without permits combined with blocking would make
+     * every request wait forever.
      */
     protected int concurrency = 10;
 
@@ -81,10 +83,10 @@ public class SemaphoreValve extends ValveBase {
     /**
      * Set the concurrency level of the semaphore.
      *
-     * @param concurrency the concurrency level
+     * @param concurrency the concurrency level, at least one
      */
     public void setConcurrency(int concurrency) {
-        if (concurrency < 0) {
+        if (concurrency < 1) {
             throw new IllegalArgumentException(
                     sm.getString("semaphoreValve.invalidConcurrency", 
Integer.valueOf(concurrency)));
         }
diff --git a/test/org/apache/catalina/valves/TestSemaphoreValve.java 
b/test/org/apache/catalina/valves/TestSemaphoreValve.java
index 3497fed65f..11b6b41af8 100644
--- a/test/org/apache/catalina/valves/TestSemaphoreValve.java
+++ b/test/org/apache/catalina/valves/TestSemaphoreValve.java
@@ -298,6 +298,31 @@ public class TestSemaphoreValve extends TomcatBaseTest {
         Assert.assertEquals(HttpServletResponse.SC_TOO_MANY_REQUESTS, 
valve.getHighConcurrencyStatus());
     }
 
+    @Test
+    public void testInvalidConcurrency() {
+        SemaphoreValve valve = new SemaphoreValve();
+
+        // A semaphore with no permits, combined with the default blocking
+        // mode, would make every request wait forever
+        try {
+            valve.setConcurrency(0);
+            Assert.fail("Expected the concurrency of zero to be rejected");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+
+        try {
+            valve.setConcurrency(-1);
+            Assert.fail("Expected a negative concurrency to be rejected");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+
+        // The smallest accepted concurrency level remains usable
+        valve.setConcurrency(1);
+        Assert.assertEquals(1, valve.getConcurrency());
+    }
+
 
     @Test
     public void testFairSemaphore() throws Exception {
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 5301cf9fcf..604f84019c 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -2603,7 +2603,8 @@ RequestHeader unset SSL_CLIENT_ESCAPED_CERT</source>
       </attribute>
 
       <attribute name="concurrency" required="false">
-        <p>Concurrency level of the semaphore. The default value is
+        <p>Concurrency level of the semaphore. The value must be at least
+        one. The default value is
         <strong>10</strong>.</p>
       </attribute>
 


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

Reply via email to