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 263f3fdd069fbe867f768454d05478d5a8cea7c1
Author: opencode <[email protected]>
AuthorDate: Tue Oct 20 08:59:15 2026 +0200

    Clear handler prefix ThreadLocal when handler creation fails
    
    In ClassLoaderLogManager.readConfiguration(InputStream, ClassLoader)
    the prefix of the handler being configured is held in a ThreadLocal
    while the handler is instantiated, so the handler reads its own
    prefixed properties during construction. The reset of that ThreadLocal
    only ran on the success path, so a handler that could not be created,
    for example due to a typo in the handler class name in a web
    application's logging.properties, left its prefix on the configuring
    thread. Subsequent property lookups on that thread were then resolved
    against the prefixed properties of the failed handler before the
    unprefixed ones, which could corrupt the configuration of loggers and
    handlers created afterwards.
    
    The reset now runs in a finally block so the prefix is cleared on every
    path, including when an Error escapes the handler's static
    initialization. The side effect is that the prefix is cleared after the
    handler has been added to the handlers map and to the root logger.
    Neither operation resolves properties, so there is no change in
    behaviour on the success path. Added a regression test that resolves an
    unprefixed property after configuring a single broken handler.
---
 java/org/apache/juli/ClassLoaderLogManager.java    |  5 +++-
 .../org/apache/juli/TestClassLoaderLogManager.java | 33 +++++++++++++++++++++-
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/juli/ClassLoaderLogManager.java 
b/java/org/apache/juli/ClassLoaderLogManager.java
index 77db708011..c9da071977 100644
--- a/java/org/apache/juli/ClassLoaderLogManager.java
+++ b/java/org/apache/juli/ClassLoaderLogManager.java
@@ -695,7 +695,6 @@ public class ClassLoaderLogManager extends LogManager {
                     // The specification strongly implies all configuration 
should be done
                     // during the creation of the handler object.
                     // This includes setting level, filter, formatter and 
encoding.
-                    this.prefix.set(null);
                     info.handlers.put(handlerName, handler);
                     if (rootHandlers == null) {
                         localRootLogger.addHandler(handler);
@@ -704,6 +703,10 @@ public class ClassLoaderLogManager extends LogManager {
                     // Report error
                     System.err.println("Handler error");
                     e.printStackTrace();
+                } finally {
+                    // Clear the prefix even when the handler could not be 
created, so a
+                    // stale prefix does not affect property resolution on 
this thread
+                    this.prefix.set(null);
                 }
             }
 
diff --git a/test/org/apache/juli/TestClassLoaderLogManager.java 
b/test/org/apache/juli/TestClassLoaderLogManager.java
index 2e57009308..2f44fb74c3 100644
--- a/test/org/apache/juli/TestClassLoaderLogManager.java
+++ b/test/org/apache/juli/TestClassLoaderLogManager.java
@@ -115,6 +115,30 @@ public class TestClassLoaderLogManager {
         }
     }
 
+    /*
+     * The prefix of a handler must not remain set on the configuring thread 
after the instantiation of the handler
+     * fails, as subsequent property lookups on that thread would otherwise 
first be resolved against the prefixed
+     * properties of the failed handler.
+     */
+    @Test
+    public void testStaleHandlerPrefixAfterHandlerFailure() throws IOException 
{
+        final String config = "handlers = 10Foo.MissingHandler" + 
System.lineSeparator()
+                + "10Foo.marker = STALE_PREFIX_VALUE" + System.lineSeparator()
+                + "marker = CLEAN_VALUE" + System.lineSeparator();
+        final ClassLoader cl = new 
TestClassLoader(config.getBytes(StandardCharsets.UTF_8));
+        final Thread currentThread = Thread.currentThread();
+        final ClassLoader oldCL = currentThread.getContextClassLoader();
+        try {
+            currentThread.setContextClassLoader(cl);
+            final ClassLoaderLogManager logManager = new 
ClassLoaderLogManager();
+            logManager.readConfiguration();
+            // The failed handler must not leave its prefix behind for this 
thread
+            Assert.assertEquals("CLEAN_VALUE", 
logManager.getProperty("marker"));
+        } finally {
+            currentThread.setContextClassLoader(oldCL);
+        }
+    }
+
     /*
      * reconfigure() must replace the configuration and apply it to the 
loggers that already exist (which a container
      * class holds a static reference to), unlike readConfiguration() which 
only configures loggers created afterwards.
@@ -295,8 +319,15 @@ public class TestClassLoaderLogManager {
 
     private static class TestClassLoader extends URLClassLoader implements 
WebappProperties {
 
+        private final byte[] loggingProperties;
+
         TestClassLoader() {
+            this(EMPTY_BYTES);
+        }
+
+        TestClassLoader(byte[] loggingProperties) {
             super(new URL[0]);
+            this.loggingProperties = loggingProperties;
         }
 
 
@@ -332,7 +363,7 @@ public class TestClassLoaderLogManager {
         @Override
         public InputStream getResourceAsStream(final String resource) {
             if ("logging.properties".equals(resource)) {
-                return new ByteArrayInputStream(EMPTY_BYTES);
+                return new ByteArrayInputStream(loggingProperties);
             }
             return null;
         }


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

Reply via email to