This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit be0c3c6e008903142fdc8c1cae36ce56c1c1fe37
Author: opencode <[email protected]>
AuthorDate: Wed Oct 14 10:04:37 2026 +0200

    Fix potential NPE in Diagnostics deadlock thread dump
    
    findDeadlock() called ThreadMXBean.findDeadlockedThreads() twice,
    using the first result only for a null check and passing the second,
    independent snapshot to getThreadInfo(). If the deadlock was resolved
    between the two calls the second call returned null and getThreadInfo()
    threw a NullPointerException. Reuse the ids from the first snapshot so
    there is no time-of-check/time-of-use window between the calls.
    
    In addition, ThreadMXBean.getThreadInfo(long[], boolean, boolean) is
    documented to return null array elements for thread ids that are no
    longer alive, for example threads that died after the deadlock
    snapshot. getThreadDump(ThreadInfo[]) passed those elements straight
    to getThreadDump(ThreadInfo) which dereferences getThreadName(), also
    throwing a NullPointerException. Skip null entries. Since the deadlock
    genuinely existed at snapshot time, the deadlock message is still
    reported; in the extreme case where all threads died the message is
    followed by an empty dump rather than crashing.
    
    findDeadlock() runs on every Manager /threaddump request via
    Diagnostics.getThreadDump(). The dumpAllThreads() path used by the
    same method never returns null elements, so the new guard is a no-op
    there. Verified with TestManagerWebapp which requests
    /manager/text/threaddump.
---
 java/org/apache/tomcat/util/Diagnostics.java | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/tomcat/util/Diagnostics.java 
b/java/org/apache/tomcat/util/Diagnostics.java
index 05755e80f1..f3383bf746 100644
--- a/java/org/apache/tomcat/util/Diagnostics.java
+++ b/java/org/apache/tomcat/util/Diagnostics.java
@@ -350,6 +350,11 @@ public class Diagnostics {
     private static String getThreadDump(ThreadInfo[] tinfos) {
         StringBuilder sb = new StringBuilder();
         for (ThreadInfo tinfo : tinfos) {
+            // A null entry means the thread died after the thread id
+            // snapshot was taken. Skip it.
+            if (tinfo == null) {
+                continue;
+            }
             sb.append(getThreadDump(tinfo));
             sb.append(CRLF);
         }
@@ -364,7 +369,15 @@ public class Diagnostics {
     public static String findDeadlock() {
         long[] ids = threadMXBean.findDeadlockedThreads();
         if (ids != null) {
-            ThreadInfo[] tinfos = 
threadMXBean.getThreadInfo(threadMXBean.findDeadlockedThreads(), true, true);
+            // Use the ids from the first call rather than calling
+            // findDeadlockedThreads() a second time. A second call may return
+            // null if the deadlock is resolved in between, in which case
+            // getThreadInfo() throws a null pointer exception; or it may
+            // return ids for threads that have since died, in which case
+            // getThreadInfo() returns null entries. Note getThreadInfo() can
+            // also return null entries for threads that die after this first
+            // snapshot; those are skipped by getThreadDump(ThreadInfo[]).
+            ThreadInfo[] tinfos = threadMXBean.getThreadInfo(ids, true, true);
             if (tinfos != null) {
                 return sm.getString("diagnostics.deadlockFound") + CRLF + 
getThreadDump(tinfos);
             }


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

Reply via email to