Bug 57281

Summary: Tomcat fails to call method of non-public filter class configured via Servlet 3.0 API when running with SecurityManager
Product: Tomcat 7 Reporter: Konstantin Kolinko <knst.kolinko>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal CC: andy.wilkinson
Priority: P2    
Version: 7.0.52   
Target Milestone: ---   
Hardware: PC   
OS: All   

Description Konstantin Kolinko 2014-11-28 23:55:12 UTC
There is a report on StackOverflow that Spring Boot (1.1.6) application fails on Tomcat 7.0.52 when running with SecurityManager enabled.

https://stackoverflow.com/questions/27189047/springboot-webapp-under-java-securitymanager-throws-exceptions-when-granted-allp

The error is
[[[
java.lang.IllegalAccessException: Class org.apache.catalina.security.SecurityUtil$1 can not access a member of class org.springframework.boot.context.web.ErrorPageFilter with modifiers "public"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109) ~[na:1.7.0_65]
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261) ~[na:1.7.0_65]
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253) ~[na:1.7.0_65]
    at java.lang.reflect.Method.invoke(Method.java:599) ~[na:1.7.0_65]
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:277) ~[catalina.jar:7.0.52]
    at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:274) ~[catalina.jar:7.0.52]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_65]
    at javax.security.auth.Subject.doAsPrivileged(Subject.java:536) ~[na:1.7.0_65]
    at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:309) ~[catalina.jar:7.0.52]
    at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:249) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:55) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:191) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:187) ~[catalina.jar:7.0.52]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_65]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:186) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) ~[catalina.jar:7.0.52]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) [catalina.jar:7.0.52]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) [catalina.jar:7.0.52]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) [catalina.jar:7.0.52]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) [catalina.jar:7.0.52]
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683) [catalina.jar:7.0.52]
    at ch.qos.logback.access.tomcat.LogbackValve.invoke(LogbackValve.java:178) [logback-access-1.0.13.jar:na]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [catalina.jar:7.0.52]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) [catalina.jar:7.0.52]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) [tomcat-coyote.jar:7.0.52]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) [tomcat-coyote.jar:7.0.52]
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313) [tomcat-coyote.jar:7.0.52]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_65]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_65]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_65]
]]]

Looking at the current sources of Spring Boot, the org.springframework.boot.context.web.ErrorPageFilter class is not public, but package-visible. Reflection call fails, as Java Language rules prohibit access to that class.

Such filter cannot be configured via web.xml (as its constructor is not accessible), but can be configured programmatically via ServletContext.addFilter(String, Filter). It is a rare situation, but I think that it is a valid one.

My idea of the fix is that method lookup in catalina SecurityUtil shall not use targetObject.getClass().getMethod(..), but Filter.class.getMethod(). My expectation is that it will be allowed to invoke java.lang.reflect.Method declared by javax.servlet.Filter class on the targetObject object regardless of accessibility of targetObject class, but testing is needed.

A bonus point is that the method cache shall become smaller.
Comment 1 Mark Thomas 2014-12-03 21:34:35 UTC
Trivial test case to reproduce:
https://github.com/markt-asf/tomcat-bug57281

I'm moving on to testing the proposed fix.
Comment 2 Mark Thomas 2014-12-03 22:16:12 UTC
I can confirm that the proposed fix works. I'll commit it once the svn server comes back to life.
Comment 4 Mark Thomas 2014-12-05 14:07:04 UTC
Fixed in trunk, 8.0.x (for 8.0.16 onwards) and 7.0.x (for 7.0.38 onwards).
Comment 5 Johno Crawford 2015-07-09 07:56:36 UTC
Unfortunately the fix for this issue caused a regression with Comet servlets, I opened a new bug with all the details and proposed patch https://bz.apache.org/bugzilla/show_bug.cgi?id=58116