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 53911b4e7eab7d4391bca8f94861729a38d770db
Author: opencode <[email protected]>
AuthorDate: Wed Sep 30 23:04:04 2026 +0200

    Capture and send the final session delta in DeltaSession.expire() before 
acquiring the session monitor to avoid a lock order inversion against the delta 
lock
---
 .../apache/catalina/ha/session/DeltaSession.java   | 29 ++++++++++++++--------
 webapps/docs/changelog.xml                         |  8 ++++++
 2 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/java/org/apache/catalina/ha/session/DeltaSession.java 
b/java/org/apache/catalina/ha/session/DeltaSession.java
index 7508d5ce26..490648d078 100644
--- a/java/org/apache/catalina/ha/session/DeltaSession.java
+++ b/java/org/apache/catalina/ha/session/DeltaSession.java
@@ -456,6 +456,25 @@ public class DeltaSession extends StandardSession 
implements Externalizable, Clu
             return;
         }
 
+        /*
+         * Obtaining and sending the final delta below takes the delta lock. 
That must not happen while the session
+         * monitor is held: code that runs while the delta lock is held, in 
particular the listener notifications
+         * triggered by StandardSession's attribute methods, may enter methods 
that take the session monitor, and the
+         * two orders would deadlock the two threads. The delta is therefore 
captured and sent before synchronizing.
+         * Concurrent expirations of the same session may then duplicate this 
message, which receivers handle
+         * harmlessly (a delta for an unknown session is ignored). The expired 
notification below remains under the
+         * monitor so it is sent only once.
+         */
+        String expiredId = getIdInternal();
+
+        if (notifyCluster && expiredId != null && manager instanceof 
DeltaManager dmanager) {
+            CatalinaCluster cluster = dmanager.getCluster();
+            ClusterMessage msg = dmanager.requestCompleted(expiredId, true);
+            if (msg != null) {
+                cluster.send(msg);
+            }
+        }
+
         synchronized (this) {
             // Check again, now we are inside the sync so this code only runs 
once
             // Double check locking - isValid needs to be volatile
@@ -467,16 +486,6 @@ public class DeltaSession extends StandardSession 
implements Externalizable, Clu
                 return;
             }
 
-            String expiredId = getIdInternal();
-
-            if (notifyCluster && expiredId != null && manager instanceof 
DeltaManager dmanager) {
-                CatalinaCluster cluster = dmanager.getCluster();
-                ClusterMessage msg = dmanager.requestCompleted(expiredId, 
true);
-                if (msg != null) {
-                    cluster.send(msg);
-                }
-            }
-
             super.expire(notify);
 
             if (notifyCluster) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d59d471101..e7d453683a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -401,6 +401,14 @@
   <subsection name="Cluster">
     <changelog>
       <!-- Entries for backport and removal before 12.0.0-M1 below this line 
-->
+      <fix>
+        Fix a possible deadlock in <code>DeltaSession.expire()</code>: the
+        final session delta is now captured and sent to the cluster before the
+        session monitor is acquired, because obtaining the delta takes the
+        delta lock and code that runs while the delta lock is held, such as
+        session attribute listener notifications, may in turn wait for the
+        session monitor. (remm)
+      </fix>
       <fix>
         Move context attributes that were set while the web application was
         still starting into the replicated attribute map when it is installed


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

Reply via email to