Bug 8694 - Exception when trying to acces the Map.entry components while using c:forEach fails to iterate over a java.util.HashMap
Summary: Exception when trying to acces the Map.entry components while using c:forEach...
Status: CLOSED FIXED
Alias: None
Product: Taglibs
Classification: Unclassified
Component: Standard Taglib (show other bugs)
Version: 1.0
Hardware: PC Linux
: P3 major (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2002-05-01 12:16 UTC by Stefan Kuehnel
Modified: 2010-01-03 00:24 UTC (History)
0 users



Attachments
Patch to make BeanInfoManager try Class.getMethod() before Class.getDeclaredMethod() (535 bytes, patch)
2003-01-03 22:07 UTC, Christopher Lenz
Details | Diff
Improved/fixed patch (703 bytes, patch)
2003-01-04 12:34 UTC, Christopher Lenz
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Stefan Kuehnel 2002-05-01 12:16:40 UTC
The c:forEach fails with an "java.security.AccessControlException: access denied
(java.lang.RuntimePermission accessDeclaredMembers)".  This is with the nightly
build from 30-Apr-2002.

Environment: J2SDK 1.4, J2EE 1.3.1_02

=========

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ page import="java.util.HashMap" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
  <title>ForEach Map-Test</title>
</head>
<body>
<jsp:useBean id="myMap" scope="page" class="java.util.HashMap"/>
<c:set target="${myMap}" property="key1" value="value1" />
<c:set target="${myMap}" property="key2" value="value2" />
<c:set target="${myMap}" property="key3" value="value3" />
<c:forEach var="entry" items="${myMap}">
  <c:set var="key" value="${entry.key}" />
  <c:set var="value" value="${entry.value}" />

  <c:out value="${key}" /> = <c:out value="${value}" />
</c:forEach>
</body>
</html>
=========

java.security.AccessControlException: access denied (java.lang.RuntimePermission
accessDeclaredMembers)
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:270)
	at java.security.AccessController.checkPermission(AccessController.java:401)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:542)
	at java.lang.SecurityManager.checkMemberAccess(SecurityManager.java:1662)
	at java.lang.Class.checkMemberAccess(Class.java:1401)
	at java.lang.Class.getDeclaredMethod(Class.java:1218)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.getPublicMethod(BeanInfoManager.java:376)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.getPublicMethod(BeanInfoManager.java:390)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.getPublicMethod(BeanInfoManager.java:353)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.initialize(BeanInfoManager.java:245)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.checkInitialized(BeanInfoManager.java:209)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.getProperty(BeanInfoManager.java:294)
	at
org.apache.taglibs.standard.lang.jstl.BeanInfoManager.getBeanInfoProperty(BeanInfoManager.java:178)
	at org.apache.taglibs.standard.lang.jstl.ArraySuffix.evaluate(ArraySuffix.java:304)
	at org.apache.taglibs.standard.lang.jstl.ComplexValue.evaluate(ComplexValue.java:141)
	at org.apache.taglibs.standard.lang.jstl.ELEvaluator.evaluate(ELEvaluator.java:235)
	at org.apache.taglibs.standard.lang.jstl.ELEvaluator.evaluate(ELEvaluator.java:198)
	at org.apache.taglibs.standard.lang.jstl.Evaluator.evaluate(Evaluator.java:134)
	at
org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager.evaluate(ExpressionEvaluatorManager.java:112)
	at
org.apache.taglibs.standard.tag.el.core.ExpressionUtil.evalNotNull(ExpressionUtil.java:85)
	at
org.apache.taglibs.standard.tag.el.core.SetTag.evaluateExpressions(SetTag.java:147)
	at org.apache.taglibs.standard.tag.el.core.SetTag.doStartTag(SetTag.java:95)
	at org.apache.jsp.test$jsp._jspService(test$jsp.java:230)
	at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:107)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at
org.apache.jasper.servlet.JspServlet$JspServletWrapper.service(JspServlet.java:202)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:382)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:474)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
	at
org.apache.catalina.core.ApplicationFilterChain.access$0(ApplicationFilterChain.java:197)
	at
org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:176)
	at java.security.AccessController.doPrivileged(Native Method)
	at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:172)
	at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:243)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:566)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:472)
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:943)
	at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:201)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:566)
	at org.apache.catalina.valves.CertificatesValve.invoke(CertificatesValve.java:246)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:564)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:472)
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:943)
	at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2343)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:566)
	at
org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:170)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:564)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:170)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:564)
	at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:368)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:564)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:472)
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:943)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
	at org.apache.catalina.core.StandardPipeline.invokeNext(StandardPipeline.java:566)
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:472)
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:943)
	at org.apache.catalina.connector.http.HttpProcessor.process(HttpProcessor.java:1012)
	at org.apache.catalina.connector.http.HttpProcessor.run(HttpProcessor.java:1107)
	at java.lang.Thread.run(Thread.java:536)
Comment 1 Shawn Bayern 2002-05-01 16:49:58 UTC
I believe this is a container bug; Nathan Abramson, the implementor of the EL
agrees.  This works for me on Tomcat 4.0.4B2, the most recent Tomcat milestone.
Comment 2 Shawn Bayern 2002-05-01 17:17:58 UTC
By the way, it could also just be a configuration issue:  can your security
manager be made less restrictive?
Comment 3 Stefan Kuehnel 2002-05-02 09:06:12 UTC
The security settings are the standard ones of a J2EE 1.3.1 RI installation.  In
1.3.1 those were made a lot tighter than before, but not unreasonable except for
the fact that they don't allow writing to the servlet temp directory. BTW, I put
all JSTL libraries into the lib directory of the web application, could have
added them as extensions but I'd assume this scenario is not unreasonable.

=====

// Standard extensions get all permissions by default

grant codeBase "file:${java.home}/lib/ext/-" {
    permission java.security.AllPermission;
};

grant codeBase "file:${java.home}/../lib/tools.jar" {
    permission java.security.AllPermission;
};

grant codeBase "file:${com.sun.enterprise.home}/lib/-" {
    permission java.security.AllPermission;
};

grant codeBase "file:${jms.home}/classes/" {
    permission java.security.AllPermission;
};

// Drivers and other system classes should be stored in this 
// code base.
grant codeBase "file:${com.sun.enterprise.home}/lib/system/-" {
    permission java.security.AllPermission;
};

// additional permissions for EJBs
grant codeBase "file:${com.sun.enterprise.home}/ejb_impls/-" {
    permission java.lang.RuntimePermission "queuePrintJob";
    permission java.io.FilePermission
"${com.sun.enterprise.home}${/}repository${/}-", "read";
};

// additional permissions for servlets
grant codeBase "file:${com.sun.enterprise.home}/public_html/-" {
    permission java.lang.RuntimePermission "loadLibrary.*";
    permission java.lang.RuntimePermission "accessClassInPackage.*";
    permission java.lang.RuntimePermission "queuePrintJob";
    permission java.lang.RuntimePermission "modifyThreadGroup";
    permission java.io.FilePermission "<<ALL FILES>>", "read,write";
    permission java.io.FilePermission
"${com.sun.enterprise.home}${/}repository${/}-", "read,write,delete";
};

// additional permissions for standalone resource adapters
grant codeBase "file:${com.sun.enterprise.home}/connector/adapters/-" {
    permission javax.security.auth.PrivateCredentialPermission "* * \"*\"", "read";
    permission java.io.FilePermission "${com.sun.enterprise.home}${/}logs${/}-",
"read,write";
};

// permissions for other classes
grant codeBase "file:${com.sun.enterprise.home}/repository/-" {
    permission java.lang.RuntimePermission "loadLibrary.*";
    permission java.lang.RuntimePermission "accessClassInPackage.*";
    permission java.lang.RuntimePermission "queuePrintJob";
    permission java.lang.RuntimePermission "modifyThreadGroup";
    permission java.io.FilePermission "<<ALL FILES>>", "read,write";
    permission javax.security.auth.PrivateCredentialPermission "* * \"*\"", "read";
};


// permissions for default domain
grant {
    permission java.net.SocketPermission "*", "connect";
    permission java.util.PropertyPermission "*", "read";

    // workaround missing doPrivileged blocks in javamail
    permission java.io.FilePermission
"${com.sun.enterprise.home}${/}lib${/}j2ee.jar", "read";

    permission javax.security.auth.PrivateCredentialPermission
"javax.resource.spi.security.PasswordCredential * \"*\"", "read";
    permission javax.security.auth.PrivateCredentialPermission
"javax.resource.spi.security.GenericCredential * \"*\"", "read";

};
Comment 4 Christopher Lenz 2003-01-03 22:03:29 UTC
I'm having the same problem here. I very much hope this will be fixed: the JSTL
RI should pretty please work out of the box with the J2EE RI, which it currently
doesn't when trying to get the 'key' or 'value' properties of Map.Entry objects
from an EL expression (as sufficiently demonstrated in this bug report).

The core of the problem is that the BeanInfoManager in the EL is inappropriately
assuming it has full reflection permissions, and uses Class.getDeclaredMethod()
without even trying Class.getMethod() first.

I'll attach a patch containing a possible remedy to this problem.
Comment 5 Christopher Lenz 2003-01-03 22:07:09 UTC
Created attachment 4331 [details]
Patch to make BeanInfoManager try Class.getMethod() before Class.getDeclaredMethod()
Comment 6 Christopher Lenz 2003-01-03 22:37:58 UTC
BTW, I'm not at all an expert about reflection, and the patch I've attached is
quite certainly incorrect. I just wanted to make the point that with the patch
applied my web-app works on J2EE-RI 1.3.1.

So somehow calling getMethod() before of getDeclaredMethod(), or resorting to
getMethod() when getDeclaredMethod() fails with a SecurityException, would be
the right direction to go.
Comment 7 Christopher Lenz 2003-01-04 12:34:40 UTC
Created attachment 4339 [details]
Improved/fixed patch
Comment 8 Christopher Lenz 2003-01-04 12:38:03 UTC
I've attached a fix that preserves the current behaviour, and only resorts to
calling getMethod() when getDeclaredMethod() throws an AccessControlException.
Essentially, the current functionality should continue to work in all cases, but
errors like the one described in this report should be fixed.
Comment 9 Shawn Bayern 2003-01-06 17:11:49 UTC
I believe the patch you've submitted is too general; note that if the declared 
methods fail, we check methods declared through the interface and superclass 
(recursively).  The solution, as far as my analysis suggests, is simply to 
ignore security-related exceptions received while focusing on the declared 
methods of the present class (as distinct from its interfaces and parent 
classes).

Let me know if that more restricted change would work for you (simply a

  try {
    ...
  } catch (AccessControlException ex) {
    // no methods here; pass through to next check
  }

).  If it does, and once I think about it some more, I'll change the EL code.
Comment 10 Christopher Lenz 2003-01-06 20:41:13 UTC
This does not work. The problem as I understand it is that however far recursion
goes, or whether it's searching interfaces and super-classes, the code *always*
calls getDeclaredMembers(), and that is simply not allowed under J2EE-RI 1.3.1
(thanks to a denied 'java.lang.RuntimePermission accessDeclaredMembers').

At some place, the routine would need to resort to a simple getMethod(), which
will work without problems under the described restrictions.

I guess I don't quite understand why the BeanInfoManger.getPublicMethod needs to
call getDeclaredMethod() in all cases... however I'll do some more experimenting.
Comment 11 Christopher Lenz 2003-01-06 22:43:21 UTC
Okay, some more research...

I actually believe the patch I proposed is pretty okay. I've inserted print
statements all over the place in the GetPublicMethod() methods of
BeanInfoManager, and this is what I get when trying to access the 'key' property
of a HashMap entry:

> getPublicMethod(getKey)
  Declaring class is java.util.HashMap$Entry
  Class modifiers are 'private static'
  > getPublicMethod(class java.util.HashMap$Entry, public java.lang.Object
java.util.HashMap$Entry.getKey())
    See if this is a public class declaring the method
    [no]
    Searching interfaces
    Searching interface java.util.Map$Entry
    > getPublicMethod(interface java.util.Map$Entry, public java.lang.Object
java.util.HashMap$Entry.getKey())
      See if this is a public class declaring the method
      [yes]
      Trying with getDeclaredMethod()
      [my patch:] AccessControlException -> trying a simple getMethod()
      [success]
      Found method 'getKey'
    Found method 'getKey'

[indentation and comments in array notation added manually for better clarity]

What I've learned from this: BeanInfoManager delegates tothe recursive
getPublicMethod(Class,Method) because the class HashMap$Entry is private.
Correctly, it looks for the interfaces/superclasses, and finds
java.util.Map$Entry. The interface is public, but now the lookup of the 'getKey'
method fails because we don't have permissions to 'accessDeclaredMembers'...

IMHO this is a good place to try a simple Class.getMethod(). I have been playing
with several alternatives, but none of them seem as clean as what I proposed in
the last patch (sorry it's marked as text/plain BTW, should have been tar-gzip).

If you have any concerns, I'd really be interested in a scenario where the
proposed change would alter behaviour for the worse. BTW, I'm testing this
change with a fairly large web-app, and everything is working as expected
(though I might not have any real weirdo-beans flying around ;-) ).
Comment 12 Shawn Bayern 2003-01-14 20:52:39 UTC
Okay, you've convinced me.  The JSTL spec doesn't specifically ask for 
getDeclaredMethod(); I'm actually now thinking of simply replacing the call to 
getDeclaredMethod() with one to getMethod().  But for now, I've adopted a 
slightly modified version of your patch that implements the logic you're 
suggesting:  drop back to getMethod() on an AccessControlException.  Thanks 
for the report, and let me know if you continue to experience problems.
Comment 13 Christopher Lenz 2003-01-16 19:29:16 UTC
This change works for me. Agree it's not really elegant :-P