https://bz.apache.org/bugzilla/show_bug.cgi?id=70135

            Bug ID: 70135
           Summary: SecurityClassLoad fails to preload JspRuntimeLibrary
                    (NoSuchMethodException) since its constructor was made
                    private in 9.0.119
           Product: Tomcat 9
           Version: 9.0.119
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Jasper
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: -----

When Tomcat is started with a SecurityManager installed, an ERROR with a full
stack trace is logged during context startup, from
`org.apache.jasper.security.SecurityClassLoad`:

```
ERROR [main] org.apache.jasper.security.SecurityClassLoad.securityClassLoad
Error preloading classes
 java.lang.NoSuchMethodException:
org.apache.jasper.runtime.JspRuntimeLibrary.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3761)
    at java.base/java.lang.Class.getConstructor(Class.java:2930)
    at
org.apache.jasper.security.SecurityClassLoad.securityClassLoad(SecurityClassLoad.java:46)
    at
org.apache.jasper.servlet.JasperInitializer.<clinit>(JasperInitializer.java:58)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:578)
    at java.base/java.lang.Class.forName(Class.java:557)
    at
org.apache.catalina.startup.WebappServiceLoader.loadServices(WebappServiceLoader.java:231)
    at
org.apache.catalina.startup.WebappServiceLoader.load(WebappServiceLoader.java:202)
    at
org.apache.catalina.startup.ContextConfig.processServletContainerInitializers(ContextConfig.java:1860)
    at
org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1341)
    at
org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:1042)
    at
org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:298)
    at
org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:115)
    at
org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4552)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:170)
    at
org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:616)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:586)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:622)
    at
org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:716)
    at
org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1860)
    at
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    at
org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:93)
    at
java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
    at
org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:604)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:495)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1594)
    at
org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:333)
    at
org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:115)
    at
org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:395)
    at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:342)
    at
org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:792)
    at
org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:740)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:170)
    at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1243)
    at
org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1233)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    at
org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:93)
    at
java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
    at
org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:765)
    at
org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:209)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:170)
    at
org.apache.catalina.core.StandardService.startInternal(StandardService.java:433)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:170)
    at
org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:879)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:170)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:860)
    at
java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:348)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
```

# Root cause

`SecurityClassLoad.securityClassLoad(ClassLoader)` preloads the JSP runtime
classes. For `JspRuntimeLibrary` it not only loads the class but instantiates
it (SecurityClassLoad.java:46):

```java
loader.loadClass(basePackage +
"runtime.JspRuntimeLibrary").getConstructor().newInstance();
```

`Class.getConstructor()` returns only *public* constructors. In 9.0.119 an
explicit private constructor was added to `JspRuntimeLibrary`:

```java
private JspRuntimeLibrary() {
}
```

In 9.0.118 and earlier the class declared no constructor. Because the class
itself is `public`, the compiler-generated default constructor was also
`public`, so `getConstructor()` succeeded. Introducing the explicit `private`
constructor in 9.0.119 makes `getConstructor()` throw `NoSuchMethodException`,
which is caught and logged at SecurityClassLoad.java:46.

# Affected versions

Verified against the published sources:

- 9.0.107 – 9.0.118: no explicit constructor (implicit public) — NOT affected
- 9.0.119: explicit `private JspRuntimeLibrary()` — first affected version
- 9.0.120-dev (current 9.0.x branch): still present; no related changelog entry

The same `private JspRuntimeLibrary()` constructor is also present on the
current 10.1.x and 11.0.x branches, which use the same
`SecurityClassLoad.getConstructor()` preload path, so those lines are affected
as well.

# Why it only appears with a SecurityManager

`securityClassLoad()` returns early when `System.getSecurityManager() == null`,
so the preload — and therefore the error — only happens when a SecurityManager
is installed.

# Impact

Functionally harmless. The preload is only an optimization: it pre-defines the
JSP runtime classes in their package up front so a later
`defineClassInPackage` RuntimePermission check under the SecurityManager does
not trip; `JspRuntimeLibrary` is otherwise loaded lazily on first JSP use, and
the application starts and runs normally. However, it logs a misleading ERROR
with a full stack trace on every startup whenever a SecurityManager is active,
which causes unnecessary support noise and false alarms.

# Suggested fix

Either:

1. Make `JspRuntimeLibrary`'s no-arg constructor `public` again, or
2. Change `SecurityClassLoad` to not instantiate it — `JspRuntimeLibrary` is a
   utility class with only static members, so loading/defining the class is
   sufficient; `getConstructor().newInstance()` could be dropped (or replaced
   with `getDeclaredConstructor()`).

# Steps to reproduce

1. Use Tomcat 9.0.119.
2. Start Tomcat with a SecurityManager installed, e.g.
   `bin/catalina.sh start -security` (or `-Djava.security.manager`).
3. Deploy any web application that triggers Jasper initialization (any app with
   JSP support).
4. Observe the `Error preloading classes` ERROR with the
   `NoSuchMethodException:
org.apache.jasper.runtime.JspRuntimeLibrary.<init>()`
   stack trace during context startup.

---

Note: this issue was analysed and the report drafted with the help of Claude
(Anthropic).

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to