From 3a7a9cc5577bd4ad27ae072ccd9b3bf65c637c02 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Thu, 4 Sep 2008 09:43:32 -0700 Subject: [PATCH] 6733959: Insufficient checks for "Main-Class" manifest entry in JAR files Fixes a buffer overrun problem with a very long Main-Class attribute. Reviewed-by: darcy --- jdk/src/share/bin/emessages.h | 1 + jdk/src/share/bin/java.c | 10 ++- jdk/test/tools/launcher/MultipleJRE.sh | 82 ++++++++++++++++++----- jdk/test/tools/launcher/ZipMeUp.java | 91 ++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 jdk/test/tools/launcher/ZipMeUp.java diff --git a/jdk/src/share/bin/emessages.h b/jdk/src/share/bin/emessages.h index 03824bba5d1..6bfb8e16614 100644 --- a/jdk/src/share/bin/emessages.h +++ b/jdk/src/share/bin/emessages.h @@ -54,6 +54,7 @@ #define CLS_ERROR2 "Error: Failed to load Main Class: %s\n%s" #define CLS_ERROR3 "Error: No main method found in specified class.\n" GEN_ERROR #define CLS_ERROR4 "Error: Main method not public\n" GEN_ERROR +#define CLS_ERROR5 "Error: main-class: attribute exceeds system limits of %d bytes\n" GEN_ERROR #define CFG_WARN1 "Warning: %s VM not supported; %s VM will be used" #define CFG_WARN2 "Warning: No leading - on line %d of `%s'" diff --git a/jdk/src/share/bin/java.c b/jdk/src/share/bin/java.c index f7cbcdc95bc..b351fe3aa83 100644 --- a/jdk/src/share/bin/java.c +++ b/jdk/src/share/bin/java.c @@ -987,8 +987,14 @@ SelectVersion(int argc, char **argv, char **main_class) * to avoid locating, expanding and parsing the manifest extra * times. */ - if (info.main_class != NULL) - (void)JLI_StrCat(env_entry, info.main_class); + if (info.main_class != NULL) { + if (JLI_StrLen(info.main_class) <= MAXNAMELEN) { + (void)JLI_StrCat(env_entry, info.main_class); + } else { + ReportErrorMessage(CLS_ERROR5, MAXNAMELEN); + exit(1); + } + } (void)putenv(env_entry); ExecJRE(jre, new_argv); JLI_FreeManifest(); diff --git a/jdk/test/tools/launcher/MultipleJRE.sh b/jdk/test/tools/launcher/MultipleJRE.sh index b3e1cf764ed..a6dfaaa5253 100644 --- a/jdk/test/tools/launcher/MultipleJRE.sh +++ b/jdk/test/tools/launcher/MultipleJRE.sh @@ -1,14 +1,14 @@ # @test MultipleJRE.sh -# @bug 4811102 4953711 4955505 4956301 4991229 4998210 5018605 6387069 +# @bug 4811102 4953711 4955505 4956301 4991229 4998210 5018605 6387069 6733959 # @build PrintVersion # @build UglyPrintVersion +# @build ZipMeUp # @run shell MultipleJRE.sh # @summary Verify Multiple JRE version support # @author Joseph E. Kowalski - # -# Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -49,10 +49,25 @@ then exit 1 fi +JAVAEXE="$TESTJAVA/bin/java" JAVA="$TESTJAVA/bin/java -classpath $TESTCLASSES" JAR="$TESTJAVA/bin/jar" OS=`uname -s`; +# +# Tests whether we are on windows (true) or not. +# +IsWindows() { + case "$OS" in + Windows* | CYGWIN* ) + printf "true" + ;; + * ) + printf "false" + ;; + esac +} + # # Shell routine to test for the proper rejection of syntactically incorrect # version specifications. @@ -139,7 +154,6 @@ CreateUglyJar() { $PACKAGE/UglyPrintVersion.class } - # # Constructs a jar file with a fair number of "zip directory" entries and # the MANIFEST.MF entry at or near the end of that directory to validate @@ -262,6 +276,29 @@ LaunchVM() { fi } +# Tests very long Main-Class attribute in the jar +TestLongMainClass() { + JVER=$1 + if [ "$JVER" = "mklink" ]; then + JVER=XX + JDKXX=jdk/j2re$JVER + rm -rf jdk + mkdir jdk + ln -s $TESTJAVA $JDKXX + JAVA_VERSION_PATH="`pwd`/jdk" + export JAVA_VERSION_PATH + fi + $JAVAEXE -cp $TESTCLASSES ZipMeUp UglyBetty.jar 4097 + message="`$JAVAEXE -version:$JVER -jar UglyBetty.jar 2>&1`" + echo $message | grep "Error: main-class: attribute exceeds system limits" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + printf "Long manifest test did not get expected error" + exit 1 + fi + unset JAVA_VERSION_PATH + rm -rf jdk +} + # # Main test sequence starts here # @@ -280,14 +317,11 @@ CreateJar "" "" LaunchVM "" "${RELEASE}" CreateJar "" "0" LaunchVM "" "${RELEASE}" -case "$OS" in - Windows* | CYGWIN* ) - MAXIMUM_PATH=255; - ;; - *) - MAXIMUM_PATH=1024; - ;; -esac +if [ `IsWindows` = "true" ]; then + MAXIMUM_PATH=255; +else + MAXIMUM_PATH=1024; +fi PATH_LENGTH=`printf "%s" "$UGLYCLASS" | wc -c` if [ ${PATH_LENGTH} -lt ${MAXIMUM_PATH} ]; then @@ -346,7 +380,6 @@ if [ -x /usr/bin/zipnote ]; then LaunchVM "" "${RELEASE}" fi - # # Throw some syntactically challenged (illegal) version specifiers at # the interface. Failure (of the launcher) is success for the test. @@ -357,15 +390,28 @@ TestSyntax "1.2.3-" # Ends with a separator TestSyntax "1.2+.3" # Embedded modifier TestSyntax "1.2.4+&1.2*&1++" # Long and invalid +# On windows we see if there is another jre installed, usually +# there is, then we test using that, otherwise links are created +# to get through to SelectVersion. +if [ `IsWindows` = "false" ]; then + TestLongMainClass "mklink" +else + $JAVAEXE -version:1.0+ + if [ $? -eq 0 ]; then + TestLongMainClass "1.0+" + else + printf "Warning: TestLongMainClass skipped as there is no" + printf "viable MJRE installed.\n" + fi +fi + # # Because scribbling in the registry can be rather destructive, only a # subset of the tests are run on Windows. # -case "$OS" in - Windows* | CYGWIN* ) - exit 0; - ;; -esac +if [ `IsWindows` = "true" ]; then + exit 0; +fi # # Additional version specifiers containing spaces. (Sigh, unable to diff --git a/jdk/test/tools/launcher/ZipMeUp.java b/jdk/test/tools/launcher/ZipMeUp.java new file mode 100644 index 00000000000..84d3f186e7e --- /dev/null +++ b/jdk/test/tools/launcher/ZipMeUp.java @@ -0,0 +1,91 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * A simple class to create our erring Jar with a very long Main-Class + * attribute in the manifest. + */ +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.zip.CRC32; +import java.util.zip.CheckedOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +public class ZipMeUp { + + static final CRC32 crc = new CRC32(); + + private static String SOME_KLASS = ".Some"; + + static byte[] getManifestAsBytes(int nchars) throws IOException { + crc.reset(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + CheckedOutputStream cos = new CheckedOutputStream(baos, crc); + PrintStream ps = new PrintStream(cos); + ps.println("Manifest-Version: 1.0"); + ps.print("Main-Class: "); + for (int i = 0 ; i < nchars - SOME_KLASS.length() ; i++) { + ps.print(i%10); + } + ps.println(SOME_KLASS); + cos.flush(); + cos.close(); + ps.close(); + return baos.toByteArray(); + } + + /** + * The arguments are: filename_to_create length + * @param args + * @throws java.lang.Exception + */ + public static void main(String...args) throws Exception { + FileOutputStream fos = new FileOutputStream(args[0]); + ZipOutputStream zos = new ZipOutputStream(fos); + byte[] manifest = getManifestAsBytes(Integer.parseInt(args[1])); + ZipEntry ze = new ZipEntry("META-INF/MANIFEST.MF"); + ze.setMethod(ZipEntry.STORED); + ze.setSize(manifest.length); + ze.setCompressedSize(manifest.length); + ze.setCrc(crc.getValue()); + ze.setTime(System.currentTimeMillis()); + zos.putNextEntry(ze); + zos.write(manifest); + zos.flush(); + + // add a zero length class + ze = new ZipEntry(SOME_KLASS + ".class"); + ze.setMethod(ZipEntry.STORED); + ze.setSize(0); + ze.setCompressedSize(0); + ze.setCrc(0); + ze.setTime(System.currentTimeMillis()); + zos.putNextEntry(ze); + zos.flush(); + zos.closeEntry(); + zos.close(); + System.exit(0); + } +}