Bug 61424

Summary: Obtaining a StackOverflowError when running Tomcat 8.5 or 9 with SecurityManager, a javax.management.remote.JMXPrincipal entry is present in catalina.policy file and Subject.doAs method is called.
Product: Tomcat 8 Reporter: Gherlan Robert <robert.gherlan>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P2    
Version: 8.5.20   
Target Milestone: ----   
Hardware: PC   
OS: All   
Attachments: Project used to reproduce the StackOverflowError

Description Gherlan Robert 2017-08-16 12:22:53 UTC
Created attachment 35231 [details]
Project used to reproduce the StackOverflowError

When run Tomcat 8.5.20 with SecurityManager and catalina.policy contains an javax.management.remote.JMXPrincipal entry and Subject.doAs method is called, then a StackOverflowError is thrown.
Same error is reproducible in Tomcat 9.0.0.M26, but is not present in Tomcat 8.0.45.

The test was made using JDK 1.8.0_144.

In order to reproduce this error, we build a short example(see JMXSubject.war in attachment) containing a simple servlet with the following source code:

package servlet;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.security.auth.Subject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/")
public class MyServlet extends HttpServlet {

  private static final long serialVersionUID = -1647039991464261998L;

  @Override
  protected void doGet(HttpServletRequest reqest, HttpServletResponse response) throws ServletException, IOException {
    Object doAsResult = null;
    Set<Principal> principals = new HashSet<>();
    principals.add(new Principal() {
      @Override
      public String getName() {
        return "myName";
      }
    });
    Subject subject = new Subject(false, principals, Collections.EMPTY_SET, Collections.EMPTY_SET);
    try {
      doAsResult = Subject.doAs(subject, new PrivilegedExceptionAction<Object>() {
        public Object run() throws IllegalAccessException, InvocationTargetException {
          return System.currentTimeMillis();
        }
      });
    } catch (Exception e) {
      e.printStackTrace();
    }
    
    response.getWriter().println("CurrentTimeMillis: " + doAsResult);
  }
}

Step to reproduce:
1) Deploy the provided JMXSubject.war web project (which also includes in the archive the source code) in ${catalina.home}/webapps folder.

2) Add in ${catalina.home}/conf/catalina.policy file the following lines:

grant codeBase "file:/-" {
  permission java.security.AllPermission;
};
	
grant principal javax.management.remote.JMXPrincipal "jmx" {
  permission java.security.AllPermission;
};

3) Start server with SecurityManager:
catalina.bat run -security

4) Access the following page: http://localhost:8080/JMXSubject

Now the following exception is thrown in tomcat 8.5.20:

javax.servlet.ServletException
	org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:337)
	org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:170)
	java.security.AccessController.doPrivileged(Native Method)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:498)
	org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:282)
	org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:279)
	java.security.AccessController.doPrivileged(Native Method)
	javax.security.auth.Subject.doAsPrivileged(Subject.java:549)
	org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:314)
	org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:253)
Root Cause

java.lang.StackOverflowError
	java.security.AccessController.doPrivileged(Native Method)
	java.io.FilePermission.init(FilePermission.java:203)
	java.io.FilePermission.<init>(FilePermission.java:277)
	java.lang.SecurityManager.checkRead(SecurityManager.java:888)
	java.io.File.isDirectory(File.java:844)
	sun.net.www.ParseUtil.fileToEncodedURL(ParseUtil.java:269)
	sun.security.provider.PolicyFile.canonicalizeCodebase(PolicyFile.java:1735)
	sun.security.provider.PolicyFile.access$700(PolicyFile.java:258)
	sun.security.provider.PolicyFile$5.run(PolicyFile.java:1188)
	sun.security.provider.PolicyFile$5.run(PolicyFile.java:1186)
	java.security.AccessController.doPrivileged(Native Method)
	sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1185)
	sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1132)
	sun.security.provider.PolicyFile.implies(PolicyFile.java:1086)
	java.security.ProtectionDomain.implies(ProtectionDomain.java:285)
	java.security.AccessControlContext.checkPermission(AccessControlContext.java:450)
	java.security.AccessController.checkPermission(AccessController.java:884)
	java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	sun.misc.URLClassPath.check(URLClassPath.java:642)
	sun.misc.URLClassPath$JarLoader.checkResource(URLClassPath.java:961)
	sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:1044)
	sun.misc.URLClassPath.getResource(URLClassPath.java:239)
	sun.misc.URLClassPath.getResource(URLClassPath.java:292)
	java.lang.ClassLoader.getBootstrapResource(ClassLoader.java:1264)
	java.lang.ClassLoader.getResource(ClassLoader.java:1093)
	org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1194)
	org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
	java.lang.Class.forName0(Native Method)
	java.lang.Class.forName(Class.java:348)
	sun.security.provider.PolicyFile.addPermissions(PolicyFile.java:1357)
	sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1228)
	sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1191)
	sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1132)
	sun.security.provider.PolicyFile.implies(PolicyFile.java:1086)
	java.security.ProtectionDomain.implies(ProtectionDomain.java:285)
	java.security.AccessControlContext.checkPermission(AccessControlContext.java:450)
	java.security.AccessController.checkPermission(AccessController.java:884)
	java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	sun.misc.URLClassPath.check(URLClassPath.java:642)
	sun.misc.URLClassPath$JarLoader.checkResource(URLClassPath.java:961)
	sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:1044)
	sun.misc.URLClassPath.getResource(URLClassPath.java:239)
	sun.misc.URLClassPath.getResource(URLClassPath.java:292)
	java.lang.ClassLoader.getBootstrapResource(ClassLoader.java:1264)
	java.lang.ClassLoader.getResource(ClassLoader.java:1093)
	org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1194)
	org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
	java.lang.Class.forName0(Native Method)
	java.lang.Class.forName(Class.java:348)
Comment 1 Mark Thomas 2017-08-21 15:28:52 UTC
Thanks for the report.

There was a targeted fix that handled a similar case that I have converted to a more general fix. I also back-ported the fix to 8.0.x since the more general may also be useful there.

Fixed in:
- trunk for 9.0.0.M27 onwards
- 8.5.x for 8.5.21 onwards
- 8.0.x for 8.0.47 onwards