Bug#583896: deadlock in WebappClassLoader

Marcus Better marcus at better.se
Mon May 31 13:06:15 UTC 2010


Package: libtomcat6-java
Version: 6.0.26-1
Severity: important
Tags: patch, upstream, fixed-upstream

Tomcat up to 6.0.26 has a deadlock [1] that causes all sorts of
applications to hang [2, 3]. This has been fixed for 6.0.27. Suggest
to backport the fix to 6.0.26 if the next upstream release does not
make it to squeeze.

Attached patch fixes the problems for me. (I can apply it to svn
myself if needed.)

[1] https://issues.apache.org/bugzilla/show_bug.cgi?id=48903
[2] http://issues.hudson-ci.org/browse/HUDSON-6473
[3] http://issues.hudson-ci.org/browse/HUDSON-6404


Index: debian/patches/series
===================================================================
--- debian/patches/series	(revision 12531)
+++ debian/patches/series	(arbetskopia)
@@ -7,3 +7,4 @@
 servlet-api-OSGi.patch
 jsp-api-OSGi.patch
 allow-empty-pid-file.patch
+webapp-classloader-deadlock-fix.patch
Index: debian/patches/webapp-classloader-deadlock-fix.patch
===================================================================
--- debian/patches/webapp-classloader-deadlock-fix.patch	(revision 0)
+++ debian/patches/webapp-classloader-deadlock-fix.patch	(revision 0)
@@ -0,0 +1,275 @@
+Index: trunk/java/org/apache/jasper/servlet/JasperLoader.java
+===================================================================
+--- trunk/java/org/apache/jasper/servlet/JasperLoader.java	(revision 941867)
++++ trunk/java/org/apache/jasper/servlet/JasperLoader.java	(revision 941868)
+@@ -91,7 +91,7 @@
+      *                                     
+      * @exception ClassNotFoundException if the class was not found
+      */                                    
+-    public Class loadClass(final String name, boolean resolve)
++    public synchronized Class loadClass(final String name, boolean resolve)
+         throws ClassNotFoundException {
+ 
+         Class clazz = null;                
+@@ -169,4 +169,4 @@
+     public final PermissionCollection getPermissions(CodeSource codeSource) {
+         return permissionCollection;
+     }
+-}
+\ No newline at end of file
++}
+Index: trunk/java/org/apache/catalina/loader/ResourceEntry.java
+===================================================================
+--- trunk/java/org/apache/catalina/loader/ResourceEntry.java	(revision 941867)
++++ trunk/java/org/apache/catalina/loader/ResourceEntry.java	(revision 941868)
+@@ -47,7 +47,7 @@
+     /**
+      * Loaded class.
+      */
+-    public Class loadedClass = null;
++    public volatile Class loadedClass = null;
+ 
+ 
+     /**
+Index: trunk/java/org/apache/catalina/loader/WebappClassLoader.java
+===================================================================
+--- trunk/java/org/apache/catalina/loader/WebappClassLoader.java	(revision 941867)
++++ trunk/java/org/apache/catalina/loader/WebappClassLoader.java	(revision 941868)
+@@ -1432,102 +1432,121 @@
+      *
+      * @exception ClassNotFoundException if the class was not found
+      */
+-    public Class loadClass(String name, boolean resolve)
++    public synchronized Class loadClass(String name, boolean resolve)
+         throws ClassNotFoundException {
+ 
+-        synchronized (name.intern()) {
+-            if (log.isDebugEnabled())
+-                log.debug("loadClass(" + name + ", " + resolve + ")");
+-            Class clazz = null;
+-    
+-            // Log access to stopped classloader
+-            if (!started) {
+-                try {
+-                    throw new IllegalStateException();
+-                } catch (IllegalStateException e) {
+-                    log.info(sm.getString("webappClassLoader.stopped", name), e);
+-                }
++        if (log.isDebugEnabled())
++            log.debug("loadClass(" + name + ", " + resolve + ")");
++        Class clazz = null;
++
++        // Log access to stopped classloader
++        if (!started) {
++            try {
++                throw new IllegalStateException();
++            } catch (IllegalStateException e) {
++                log.info(sm.getString("webappClassLoader.stopped", name), e);
+             }
+-    
+-            // (0) Check our previously loaded local class cache
+-            clazz = findLoadedClass0(name);
++        }
++
++        // (0) Check our previously loaded local class cache
++        clazz = findLoadedClass0(name);
++        if (clazz != null) {
++            if (log.isDebugEnabled())
++                log.debug("  Returning class from cache");
++            if (resolve)
++                resolveClass(clazz);
++            return (clazz);
++        }
++
++        // (0.1) Check our previously loaded class cache
++        clazz = findLoadedClass(name);
++        if (clazz != null) {
++            if (log.isDebugEnabled())
++                log.debug("  Returning class from cache");
++            if (resolve)
++                resolveClass(clazz);
++            return (clazz);
++        }
++
++        // (0.2) Try loading the class with the system class loader, to prevent
++        //       the webapp from overriding J2SE classes
++        try {
++            clazz = system.loadClass(name);
+             if (clazz != null) {
+-                if (log.isDebugEnabled())
+-                    log.debug("  Returning class from cache");
+                 if (resolve)
+                     resolveClass(clazz);
+                 return (clazz);
+             }
+-    
+-            // (0.1) Check our previously loaded class cache
+-            clazz = findLoadedClass(name);
+-            if (clazz != null) {
+-                if (log.isDebugEnabled())
+-                    log.debug("  Returning class from cache");
+-                if (resolve)
+-                    resolveClass(clazz);
+-                return (clazz);
++        } catch (ClassNotFoundException e) {
++            // Ignore
++        }
++
++        // (0.5) Permission to access this class when using a SecurityManager
++        if (securityManager != null) {
++            int i = name.lastIndexOf('.');
++            if (i >= 0) {
++                try {
++                    securityManager.checkPackageAccess(name.substring(0,i));
++                } catch (SecurityException se) {
++                    String error = "Security Violation, attempt to use " +
++                        "Restricted Class: " + name;
++                    log.info(error, se);
++                    throw new ClassNotFoundException(error, se);
++                }
+             }
+-    
+-            // (0.2) Try loading the class with the system class loader, to prevent
+-            //       the webapp from overriding J2SE classes
++        }
++
++        boolean delegateLoad = delegate || filter(name);
++
++        // (1) Delegate to our parent if requested
++        if (delegateLoad) {
++            if (log.isDebugEnabled())
++                log.debug("  Delegating to parent classloader1 " + parent);
++            ClassLoader loader = parent;
++            if (loader == null)
++                loader = system;
+             try {
+-                clazz = system.loadClass(name);
++                clazz = loader.loadClass(name);
+                 if (clazz != null) {
++                    if (log.isDebugEnabled())
++                        log.debug("  Loading class from parent");
+                     if (resolve)
+                         resolveClass(clazz);
+                     return (clazz);
+                 }
+             } catch (ClassNotFoundException e) {
+-                // Ignore
++                ;
+             }
+-    
+-            // (0.5) Permission to access this class when using a SecurityManager
+-            if (securityManager != null) {
+-                int i = name.lastIndexOf('.');
+-                if (i >= 0) {
+-                    try {
+-                        securityManager.checkPackageAccess(name.substring(0,i));
+-                    } catch (SecurityException se) {
+-                        String error = "Security Violation, attempt to use " +
+-                            "Restricted Class: " + name;
+-                        log.info(error, se);
+-                        throw new ClassNotFoundException(error, se);
+-                    }
+-                }
+-            }
+-    
+-            boolean delegateLoad = delegate || filter(name);
+-    
+-            // (1) Delegate to our parent if requested
+-            if (delegateLoad) {
++        }
++
++        // (2) Search local repositories
++        if (log.isDebugEnabled())
++            log.debug("  Searching local repositories");
++        try {
++            clazz = findClass(name);
++            if (clazz != null) {
+                 if (log.isDebugEnabled())
+-                    log.debug("  Delegating to parent classloader1 " + parent);
+-                ClassLoader loader = parent;
+-                if (loader == null)
+-                    loader = system;
+-                try {
+-                    clazz = loader.loadClass(name);
+-                    if (clazz != null) {
+-                        if (log.isDebugEnabled())
+-                            log.debug("  Loading class from parent");
+-                        if (resolve)
+-                            resolveClass(clazz);
+-                        return (clazz);
+-                    }
+-                } catch (ClassNotFoundException e) {
+-                    ;
+-                }
++                    log.debug("  Loading class from local repository");
++                if (resolve)
++                    resolveClass(clazz);
++                return (clazz);
+             }
+-    
+-            // (2) Search local repositories
++        } catch (ClassNotFoundException e) {
++            ;
++        }
++
++        // (3) Delegate to parent unconditionally
++        if (!delegateLoad) {
+             if (log.isDebugEnabled())
+-                log.debug("  Searching local repositories");
++                log.debug("  Delegating to parent classloader at end: " + parent);
++            ClassLoader loader = parent;
++            if (loader == null)
++                loader = system;
+             try {
+-                clazz = findClass(name);
++                clazz = loader.loadClass(name);
+                 if (clazz != null) {
+                     if (log.isDebugEnabled())
+-                        log.debug("  Loading class from local repository");
++                        log.debug("  Loading class from parent");
+                     if (resolve)
+                         resolveClass(clazz);
+                     return (clazz);
+@@ -1535,30 +1554,10 @@
+             } catch (ClassNotFoundException e) {
+                 ;
+             }
+-    
+-            // (3) Delegate to parent unconditionally
+-            if (!delegateLoad) {
+-                if (log.isDebugEnabled())
+-                    log.debug("  Delegating to parent classloader at end: " + parent);
+-                ClassLoader loader = parent;
+-                if (loader == null)
+-                    loader = system;
+-                try {
+-                    clazz = loader.loadClass(name);
+-                    if (clazz != null) {
+-                        if (log.isDebugEnabled())
+-                            log.debug("  Loading class from parent");
+-                        if (resolve)
+-                            resolveClass(clazz);
+-                        return (clazz);
+-                    }
+-                } catch (ClassNotFoundException e) {
+-                    ;
+-                }
+-            }
+-    
+-            throw new ClassNotFoundException(name);
+         }
++
++        throw new ClassNotFoundException(name);
++
+     }
+ 
+ 
+@@ -2544,7 +2543,7 @@
+         if (clazz != null)
+             return clazz;
+ 
+-        synchronized (name.intern()) {
++        synchronized (this) {
+             clazz = entry.loadedClass;
+             if (clazz != null)
+                 return clazz;


-- System Information:
Debian Release: squeeze/sid
  APT prefers testing
  APT policy: (990, 'testing'), (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.33.3-melech (SMP w/2 CPU cores; PREEMPT)
Locale: LANG=sv_SE.UTF-8, LC_CTYPE=sv_SE.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages tomcat6 depends on:
ii  adduser                       3.112      add and remove users and groups
pn  jsvc                          <none>     (no description available)
ii  tomcat6-common                6.0.26-1   Servlet and JSP engine -- common f

tomcat6 recommends no packages.

Versions of packages tomcat6 suggests:
pn  tomcat6-admin                 <none>     (no description available)
pn  tomcat6-docs                  <none>     (no description available)
pn  tomcat6-examples              <none>     (no description available)

-- Configuration Files:
/etc/init.d/tomcat6 changed:
set -e
PATH=/bin:/usr/bin:/sbin:/usr/sbin
NAME=tomcat6
DESC="Tomcat servlet engine"
DAEMON=/usr/bin/jsvc
CATALINA_HOME=/usr/share/$NAME
DEFAULT=/etc/default/$NAME
JVM_TMP=/tmp/tomcat6-temp
if [ `id -u` -ne 0 ]; then
	echo "You need root privileges to run this script"
	exit 1
fi
 
if [ -r /etc/default/locale ]; then
	. /etc/default/locale
	export LANG
fi
. /lib/lsb/init-functions
. /etc/default/rcS
TOMCAT6_USER=tomcat6
JDK_DIRS="/usr/lib/jvm/java-6-openjdk /usr/lib/jvm/java-6-sun /usr/lib/jvm/java-1.5.0-sun /usr/lib/j2sdk1.5-sun /usr/lib/j2sdk1.5-ibm"
for jdir in $JDK_DIRS; do
    if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then
	JAVA_HOME="$jdir"
    fi
done
export JAVA_HOME
CATALINA_BASE=/var/lib/$NAME
TOMCAT6_SECURITY=yes
if [ -z "$JAVA_OPTS" ]; then
	JAVA_OPTS="-Djava.awt.headless=true -Xmx128M"
fi
if [ -f "$DEFAULT" ]; then
	. "$DEFAULT"
fi
if [ ! -f "$CATALINA_HOME/bin/bootstrap.jar" ]; then
	log_failure_msg "$NAME is not installed"
	exit 1
fi
[ -f "$DAEMON" ] || exit 0
POLICY_CACHE="$CATALINA_BASE/work/catalina.policy"
JAVA_OPTS="$JAVA_OPTS -Djava.endorsed.dirs=$CATALINA_HOME/endorsed -Dcatalina.base=$CATALINA_BASE -Dcatalina.home=$CATALINA_HOME -Djava.io.tmpdir=$JVM_TMP"
if [ -n "$JSP_COMPILER" ]; then
	JAVA_OPTS="$JAVA_OPTS -Dbuild.compiler=$JSP_COMPILER"
fi
if [ "$TOMCAT6_SECURITY" = "yes" ]; then
	JAVA_OPTS="$JAVA_OPTS -Djava.security.manager -Djava.security.policy=$POLICY_CACHE"
fi
if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then
  JAVA_OPTS="$JAVA_OPTS "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" "-Djava.util.logging.config.file="$CATALINA_BASE/conf/logging.properties"
fi
CATALINA_PID="/var/run/$NAME.pid"
BOOTSTRAP_CLASS=org.apache.catalina.startup.Bootstrap
JSVC_CLASSPATH="/usr/share/java/commons-daemon.jar:$CATALINA_HOME/bin/bootstrap.jar"
if [ -z "${JSSE_HOME}" -a -r "${JAVA_HOME}/jre/lib/jsse.jar" ]; then
    JSSE_HOME="${JAVA_HOME}/jre/"
fi
export JSSE_HOME
case "$1" in
  start)
	if [ -z "$JAVA_HOME" ]; then
		log_failure_msg "no JDK found - please set JAVA_HOME"
		exit 1
	fi
	if [ ! -d "$CATALINA_BASE/conf" ]; then
		log_failure_msg "invalid CATALINA_BASE: $CATALINA_BASE"
		exit 1
	fi
	log_daemon_msg "Starting $DESC" "$NAME"
	if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
		--user $TOMCAT6_USER --startas "$JAVA_HOME/bin/java" \
		>/dev/null; then
		# Regenerate POLICY_CACHE file
		umask 022
		echo "// AUTO-GENERATED FILE from /etc/tomcat6/policy.d/" \
			> "$POLICY_CACHE"
		echo ""  >> "$POLICY_CACHE"
		cat $CATALINA_BASE/conf/policy.d/*.policy \
			>> "$POLICY_CACHE"
		# Remove / recreate JVM_TMP directory
		rm -rf "$JVM_TMP"
		mkdir "$JVM_TMP" || {
			log_failure_msg "could not create JVM temporary directory"
			exit 1
		}
		chown $TOMCAT6_USER "$JVM_TMP"
		cd "$JVM_TMP"
		$DAEMON -user "$TOMCAT6_USER" -cp "$JSVC_CLASSPATH" \
		    -outfile SYSLOG -errfile SYSLOG \
		    -pidfile "$CATALINA_PID" $JAVA_OPTS "$BOOTSTRAP_CLASS"
		sleep 5
        	if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
			--user $TOMCAT6_USER --startas "$JAVA_HOME/bin/java" \
			>/dev/null; then
			log_end_msg 1
		else
			log_end_msg 0
		fi
	else
	        log_progress_msg "(already running)"
		log_end_msg 0
	fi
	;;
  stop)
	log_daemon_msg "Stopping $DESC" "$NAME"
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
		--user "$TOMCAT6_USER" --startas "$JAVA_HOME/bin/java" \
		>/dev/null; then
		log_progress_msg "(not running)"
	else
		$DAEMON -cp "$JSVC_CLASSPATH" -pidfile "$CATALINA_PID" \
		     -stop "$BOOTSTRAP_CLASS"
	fi
	rm -rf "$JVM_TMP"
	log_end_msg 0
	;;
   status)
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
		--user $TOMCAT6_USER --startas "$JAVA_HOME/bin/java" \
		>/dev/null; then
		if [ -f "$CATALINA_PID" ]; then
		    log_success_msg "$DESC is not running, but pid file exists."
			exit 1
		else
		    log_success_msg "$DESC is not running."
			exit 3
		fi
	else
		log_success_msg "$DESC is running with pid `cat $CATALINA_PID`"
	fi
        ;;
  restart|force-reload)
        if start-stop-daemon --test --stop --pidfile "$CATALINA_PID" \
		--user $TOMCAT6_USER --startas "$JAVA_HOME/bin/java" \
		>/dev/null; then
		$0 stop
		sleep 1
	fi
	$0 start
	;;
  try-restart)
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
		--user $TOMCAT6_USER --startas "$JAVA_HOME/bin/java" \
		>/dev/null; then
		$0 start
	fi
        ;;
  *)
	log_success_msg "Usage: $0 {start|stop|restart|try-restart|force-reload|status}"
	exit 1
	;;
esac
exit 0

/etc/tomcat6/tomcat-users.xml changed:
<?xml version='1.0' encoding='utf-8'?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<tomcat-users>
  <role rolename="manager"/>
  <user username="marcus" password="blahonga" roles="manager"/>
</tomcat-users>





More information about the pkg-java-maintainers mailing list