Bug 64582 - SecurityClassLoad - CoyoteOutputStream
Summary: SecurityClassLoad - CoyoteOutputStream
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 9.0.36
Hardware: Macintosh All
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-07-06 20:34 UTC by Johnathan Gilday
Modified: 2020-07-10 15:53 UTC (History)
0 users



Attachments
patch (898 bytes, patch)
2020-07-06 20:34 UTC, Johnathan Gilday
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Johnathan Gilday 2020-07-06 20:34:15 UTC
Created attachment 37351 [details]
patch

A basic servlet application that prints a message to the ServletOutputStream may fail unexpectedly with an AccessControlException when Tomcat is configured to use a Java security policy and circumstances in the JVM defer loading the CoyoteOutputStream class until org.apache.catalina.Response.getOutputStream() is called.

I was able to reproduce this behavior on Tomcat 9.0.26, but not with 7.0.94 nor 8.5.43. The environment in testing is

openjdk version "1.8.0_222"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.222-b10, mixed mode)

Under normal circumstances, verbose:class logs indicate that HotSpot loads the CoyoteOutputStream class during application initialization before handling any requests; however, this class is not resolved until the application handles a request and the servlet calls `Response.getOutputStream()` wherein the CoyoteOutputStream is lazily initialized. Based on the JVM specification for class loading and resolution, I don't believe there is anything compelling HotSpot to load the `CoyoteOutputStream` class when it does; rather, this appears to be an eager class load at HotSpot's discretion. This timing is fortunate, because the application needs `CoyoteOutputStream` to be pre-loaded prior to resolving it in the call to `Response.getOutputStream()`, since the application does not have permissions to load classes in the org.apache.catalina.connector package. In order to guarantee that HotSpot loads CoyoteOutputStream before the user's application needs it, Tomcat should include this class in its `SecurityClassLoad` utility (patch attached) which I understand is built to handle these cases.

Without the proposed change to `SecurityClassLoad`, circumstances which affect HotSpot's class loading could defer loading the `CoyoteOutputStream` until the application calls `Response.getOutputStream()` thus causing an AccessControlException. The only example I have of such a circumstance is the inclusion of the Contrast Security Java agent. When the Contrast Security Java agent (see https://docs.contrastsecurity.com/installation-java.html#java-overview) is added to the JVM via `CATALINA_OPTS`, the CoyoteOutputStream class is not loaded during application initialization; rather, the class is loaded just prior to resolving it when the CoyoteOutputStream field in Response is lazily instantiated in `Response.getOutputStream()`.

```
> The server encountered an unexpected condition that prevented it from fulfilling the request.</p><p><b>Exception</b></p><pre>java.security.AccessControlException: access denied (&quot;java.lang.RuntimePermission&quot; &quot;accessClassInPackage.org.apache.catalina.connector&quot;)
	java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
	java.security.AccessController.checkPermission(AccessController.java:886)
	java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1564)
	sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:329)
	java.lang.ClassLoader.loadClass(ClassLoader.java:411)
	java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	org.apache.catalina.connector.Response.getOutputStream(Response.java:552)
	org.apache.catalina.connector.ResponseFacade.getOutputStream(ResponseFacade.java:210)
	com.contrastsecurity.testapp.servlet25.servletoutputstream.PrintServlet.doGet(PrintServlet.java:30)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
```
Comment 1 Mark Thomas 2020-07-09 10:55:44 UTC
Thanks for the report and for the patch.

Fixed in:
- master for 10.0.0-M8 onwards
- 9.0.x for 9.0.38 onwards
- 8.5.x for 8.5.58 onwards
- 7.0.x for 7.0.106 onwards