# HG changeset patch # User ksrini # Date 1234995243 28800 # Node ID 7a11e5916ddad9a9c5cdb4242fc7d9ffafe7a0ca # Parent 09ad5edb53300b34fe2744b45ad8f9795bfa2df0 6792554: Java JAR Pack200 header checks are insufficent Summary: Added several checks to ensure that the values read from the headers are consistent Reviewed-by: jrose diff -r 09ad5edb5330 -r 7a11e5916dda jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp --- a/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp Mon Jan 05 11:28:43 2009 -0800 +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp Wed Feb 18 14:14:03 2009 -0800 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2009 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 @@ -94,6 +94,7 @@ assert(!valc->isMalloc); } xvs.init(u->rp, u->rplimit, valc); + CHECK; int X = xvs.getInt(); if (valc->S() != 0) { assert(valc->min <= -256); @@ -117,6 +118,7 @@ byte XB_byte = (byte) XB; byte* XB_ptr = &XB_byte; cm.init(u->rp, u->rplimit, XB_ptr, 0, defc, length, null); + CHECK; } else { NOT_PRODUCT(byte* meta_rp0 = u->meta_rp); assert(u->meta_rp != null); @@ -215,8 +217,19 @@ if (length == 0) return 0; if (total_memo > 0) return total_memo-1; int total = getInt(); + // overflow checks require that none of the addends are <0, + // and that the partial sums never overflow (wrap negative) + if (total < 0) { + abort("overflow detected"); + return 0; + } for (int k = length-1; k > 0; k--) { + int prev_total = total; total += vs[0].getInt(); + if (total < prev_total) { + abort("overflow detected"); + return 0; + } } rewind(); total_memo = total+1; diff -r 09ad5edb5330 -r 7a11e5916dda jdk/src/share/native/com/sun/java/util/jar/pack/coding.cpp --- a/jdk/src/share/native/com/sun/java/util/jar/pack/coding.cpp Mon Jan 05 11:28:43 2009 -0800 +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/coding.cpp Wed Feb 18 14:14:03 2009 -0800 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2009 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 @@ -809,6 +809,7 @@ } band_rp = vs.rp; } + CHECK; // Get an accurate upper limit now. vs0.rplimit = band_rp; diff -r 09ad5edb5330 -r 7a11e5916dda jdk/src/share/native/com/sun/java/util/jar/pack/defines.h --- a/jdk/src/share/native/com/sun/java/util/jar/pack/defines.h Mon Jan 05 11:28:43 2009 -0800 +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/defines.h Wed Feb 18 14:14:03 2009 -0800 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 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 @@ -155,6 +155,8 @@ #define CHECK_NULL_(y,p) _CHECK_DO((p)==null, return y) #define CHECK_NULL_0(p) _CHECK_DO((p)==null, return 0) +#define CHECK_COUNT(t) if (t < 0){abort("bad value count");} CHECK + #define STR_TRUE "true" #define STR_FALSE "false" diff -r 09ad5edb5330 -r 7a11e5916dda jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp --- a/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp Mon Jan 05 11:28:43 2009 -0800 +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp Wed Feb 18 14:14:03 2009 -0800 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 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 @@ -504,15 +504,39 @@ enum { MAGIC_BYTES = 4, AH_LENGTH_0 = 3, //minver, majver, options are outside of archive_size + AH_LENGTH_0_MAX = AH_LENGTH_0 + 1, // options might have 2 bytes AH_LENGTH = 26, //maximum archive header length (w/ all fields) // Length contributions from optional header fields: AH_FILE_HEADER_LEN = 5, // sizehi/lo/next/modtime/files + AH_ARCHIVE_SIZE_LEN = 2, // sizehi/lo only; part of AH_FILE_HEADER_LEN AH_CP_NUMBER_LEN = 4, // int/float/long/double AH_SPECIAL_FORMAT_LEN = 2, // layouts/band-headers AH_LENGTH_MIN = AH_LENGTH -(AH_FILE_HEADER_LEN+AH_SPECIAL_FORMAT_LEN+AH_CP_NUMBER_LEN), + ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - (AH_LENGTH_0 + AH_ARCHIVE_SIZE_LEN), FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN }; + + assert(AH_LENGTH_MIN == 15); // # of UNSIGNED5 fields required after archive_magic + assert(ARCHIVE_SIZE_MIN == 10); // # of UNSIGNED5 fields required after archive_size + // An absolute minimum null archive is magic[4], {minver,majver,options}[3], + // archive_size[0], cp_counts[8], class_counts[4], for a total of 19 bytes. + // (Note that archive_size is optional; it may be 0..10 bytes in length.) + // The first read must capture everything up through the options field. + // This happens to work even if {minver,majver,options} is a pathological + // 15 bytes long. Legal pack files limit those three fields to 1+1+2 bytes. + assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0 * B_MAX); + + // Up through archive_size, the largest possible archive header is + // magic[4], {minver,majver,options}[4], archive_size[10]. + // (Note only the low 12 bits of options are allowed to be non-zero.) + // In order to parse archive_size, we need at least this many bytes + // in the first read. Of course, if archive_size_hi is more than + // a byte, we probably will fail to allocate the buffer, since it + // will be many gigabytes long. This is a practical, not an + // architectural limit to Pack200 archive sizes. + assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0_MAX + 2*B_MAX); + bool foreign_buf = (read_input_fn == null); byte initbuf[FIRST_READ + C_SLOP + 200]; // 200 is for JAR I/O if (foreign_buf) { @@ -528,7 +552,7 @@ // There is no way to tell the caller that we used only part of them. // Therefore, the caller must use only a bare minimum of read-ahead. if (inbytes.len > FIRST_READ) { - abort("too much pushback"); + abort("too much read-ahead"); return; } input.set(initbuf, sizeof(initbuf)); @@ -538,7 +562,7 @@ rplimit += inbytes.len; bytes_read += inbytes.len; } - // Read only 19 bytes, which is certain to contain #archive_size fields, + // Read only 19 bytes, which is certain to contain #archive_options fields, // but is certain not to overflow past the archive_header. input.b.len = FIRST_READ; if (!ensure_input(FIRST_READ)) @@ -610,9 +634,9 @@ #undef ORBIT if ((archive_options & ~OPTION_LIMIT) != 0) { fprintf(errstrm, "Warning: Illegal archive options 0x%x\n", - archive_options); - // Do not abort. If the format really changes, version numbers will bump. - //abort("illegal archive options"); + archive_options); + abort("illegal archive options"); + return; } if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) { @@ -643,8 +667,17 @@ return; } } else if (archive_size != 0) { + if (archive_size < ARCHIVE_SIZE_MIN) { + abort("impossible archive size"); // bad input data + return; + } + if (archive_size < header_size_1) { + abort("too much read-ahead"); // somehow we pre-fetched too much? + return; + } input.set(U_NEW(byte, add_size(header_size_0, archive_size, C_SLOP)), (size_t) header_size_0 + archive_size); + CHECK; assert(input.limit()[0] == 0); // Move all the bytes we read initially into the real buffer. input.b.copyFrom(initbuf, header_size); @@ -659,6 +692,7 @@ rp = rplimit = input.base(); // Set up input buffer as if we already read the header: input.b.copyFrom(initbuf, header_size); + CHECK; rplimit += header_size; while (ensure_input(input.limit() - rp)) { size_t dataSoFar = input_remaining(); @@ -694,8 +728,10 @@ if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) { archive_next_count = hdr.getInt(); + CHECK_COUNT(archive_next_count); archive_modtime = hdr.getInt(); file_count = hdr.getInt(); + CHECK_COUNT(file_count); hdrVals += 3; } else { hdrValsSkipped += 3; @@ -703,7 +739,9 @@ if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0) { band_headers_size = hdr.getInt(); + CHECK_COUNT(band_headers_size); attr_definition_count = hdr.getInt(); + CHECK_COUNT(attr_definition_count); hdrVals += 2; } else { hdrValsSkipped += 2; @@ -723,13 +761,16 @@ } } cp_counts[k] = hdr.getInt(); + CHECK_COUNT(cp_counts[k]); hdrVals += 1; } ic_count = hdr.getInt(); + CHECK_COUNT(ic_count); default_class_minver = hdr.getInt(); default_class_majver = hdr.getInt(); class_count = hdr.getInt(); + CHECK_COUNT(class_count); hdrVals += 4; // done with archive_header @@ -783,7 +824,6 @@ bytes::of(band_headers.limit(), C_SLOP).clear(_meta_error); } - void unpacker::finish() { if (verbose >= 1) { fprintf(errstrm, @@ -2089,6 +2129,7 @@ field_descr.readData(field_count); read_attrs(ATTR_CONTEXT_FIELD, field_count); + CHECK; method_descr.readData(method_count); read_attrs(ATTR_CONTEXT_METHOD, method_count); @@ -2096,6 +2137,7 @@ CHECK; read_attrs(ATTR_CONTEXT_CLASS, class_count); + CHECK; read_code_headers(); @@ -2122,10 +2164,12 @@ assert(endsWith(xxx_flags_hi.name, "_flags_hi")); if (haveLongFlags) xxx_flags_hi.readData(obj_count); + CHECK; band& xxx_flags_lo = ad.xxx_flags_lo(); assert(endsWith(xxx_flags_lo.name, "_flags_lo")); xxx_flags_lo.readData(obj_count); + CHECK; // pre-scan flags, counting occurrences of each index bit julong indexMask = ad.flagIndexMask(); // which flag bits are index bits? @@ -2148,11 +2192,13 @@ assert(endsWith(xxx_attr_count.name, "_attr_count")); // There is one count element for each 1<<16 bit set in flags: xxx_attr_count.readData(ad.predefCount(X_ATTR_OVERFLOW)); + CHECK; band& xxx_attr_indexes = ad.xxx_attr_indexes(); assert(endsWith(xxx_attr_indexes.name, "_attr_indexes")); int overflowIndexCount = xxx_attr_count.getIntTotal(); xxx_attr_indexes.readData(overflowIndexCount); + CHECK; // pre-scan attr indexes, counting occurrences of each value for (i = 0; i < overflowIndexCount; i++) { idx = xxx_attr_indexes.getInt(); @@ -2183,6 +2229,7 @@ } } ad.xxx_attr_calls().readData(backwardCounts); + CHECK; // Read built-in bands. // Mostly, these are hand-coded equivalents to readBandData(). @@ -2191,42 +2238,53 @@ count = ad.predefCount(CLASS_ATTR_SourceFile); class_SourceFile_RUN.readData(count); + CHECK; count = ad.predefCount(CLASS_ATTR_EnclosingMethod); class_EnclosingMethod_RC.readData(count); class_EnclosingMethod_RDN.readData(count); + CHECK; count = ad.predefCount(X_ATTR_Signature); class_Signature_RS.readData(count); + CHECK; ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); count = ad.predefCount(CLASS_ATTR_InnerClasses); class_InnerClasses_N.readData(count); + CHECK; + count = class_InnerClasses_N.getIntTotal(); class_InnerClasses_RC.readData(count); class_InnerClasses_F.readData(count); + CHECK; // Drop remaining columns wherever flags are zero: count -= class_InnerClasses_F.getIntCount(0); class_InnerClasses_outer_RCN.readData(count); class_InnerClasses_name_RUN.readData(count); + CHECK; count = ad.predefCount(CLASS_ATTR_ClassFile_version); class_ClassFile_version_minor_H.readData(count); class_ClassFile_version_major_H.readData(count); + CHECK; break; case ATTR_CONTEXT_FIELD: count = ad.predefCount(FIELD_ATTR_ConstantValue); field_ConstantValue_KQ.readData(count); + CHECK; count = ad.predefCount(X_ATTR_Signature); field_Signature_RS.readData(count); + CHECK; ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); + CHECK; break; case ATTR_CONTEXT_METHOD: @@ -2238,15 +2296,18 @@ method_Exceptions_N.readData(count); count = method_Exceptions_N.getIntTotal(); method_Exceptions_RC.readData(count); + CHECK; count = ad.predefCount(X_ATTR_Signature); method_Signature_RS.readData(count); + CHECK; ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); ad.readBandData(METHOD_ATTR_RuntimeVisibleParameterAnnotations); ad.readBandData(METHOD_ATTR_RuntimeInvisibleParameterAnnotations); ad.readBandData(METHOD_ATTR_AnnotationDefault); + CHECK; break; case ATTR_CONTEXT_CODE: @@ -2258,8 +2319,10 @@ return; } code_StackMapTable_N.readData(count); + CHECK; count = code_StackMapTable_N.getIntTotal(); code_StackMapTable_frame_T.readData(count); + CHECK; // the rest of it depends in a complicated way on frame tags { int fat_frame_count = 0; @@ -2293,18 +2356,23 @@ // deal completely with fat frames: offset_count += fat_frame_count; code_StackMapTable_local_N.readData(fat_frame_count); + CHECK; type_count += code_StackMapTable_local_N.getIntTotal(); code_StackMapTable_stack_N.readData(fat_frame_count); type_count += code_StackMapTable_stack_N.getIntTotal(); + CHECK; // read the rest: code_StackMapTable_offset.readData(offset_count); code_StackMapTable_T.readData(type_count); + CHECK; // (7) [RCH] count = code_StackMapTable_T.getIntCount(7); code_StackMapTable_RC.readData(count); + CHECK; // (8) [PH] count = code_StackMapTable_T.getIntCount(8); code_StackMapTable_P.readData(count); + CHECK; } count = ad.predefCount(CODE_ATTR_LineNumberTable); @@ -2626,6 +2694,7 @@ code_max_na_locals.readData(); code_handler_count.readData(); totalHandlerCount += code_handler_count.getIntTotal(); + CHECK; // Read handler specifications. // Cf. PackageReader.readCodeHandlers. @@ -2633,8 +2702,10 @@ code_handler_end_PO.readData(totalHandlerCount); code_handler_catch_PO.readData(totalHandlerCount); code_handler_class_RCN.readData(totalHandlerCount); + CHECK; read_attrs(ATTR_CONTEXT_CODE, totalFlagsCount); + CHECK; } static inline bool is_in_range(uint n, uint min, uint max) { diff -r 09ad5edb5330 -r 7a11e5916dda jdk/test/tools/pack200/MemoryAllocatorTest.java --- a/jdk/test/tools/pack200/MemoryAllocatorTest.java Mon Jan 05 11:28:43 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,369 +0,0 @@ -/* - * 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. - */ - -/* - * @test - * @bug 6755943 - * @summary Checks any memory overruns in archive length. - * @run main/timeout=1200 MemoryAllocatorTest - */ -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class MemoryAllocatorTest { - - /* - * The smallest possible pack file with 1 empty resource - */ - static int[] magic = { - 0xCA, 0xFE, 0xD0, 0x0D - }; - static int[] version_info = { - 0x07, // minor - 0x96 // major - }; - static int[] option = { - 0x10 - }; - static int[] size_hi = { - 0x00 - }; - static int[] size_lo_ulong = { - 0xFF, 0xFC, 0xFC, 0xFC, 0xFC // ULONG_MAX 0xFFFFFFFF - }; - static int[] size_lo_correct = { - 0x17 - }; - static int[] data = { - 0x00, 0xEC, 0xDA, 0xDE, 0xF8, 0x45, 0x01, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x31, 0x01, 0x00 - }; - // End of pack file data - - static final String JAVA_HOME = System.getProperty("java.home"); - - static final boolean debug = Boolean.getBoolean("MemoryAllocatorTest.Debug"); - static final boolean WINDOWS = System.getProperty("os.name").startsWith("Windows"); - static final boolean LINUX = System.getProperty("os.name").startsWith("Linux"); - static final boolean SIXTYFOUR_BIT = System.getProperty("sun.arch.data.model", "32").equals("64"); - static final private int EXPECTED_EXIT_CODE = (WINDOWS) ? -1 : 255; - - static int testExitValue = 0; - - static byte[] bytes(int[] a) { - byte[] b = new byte[a.length]; - for (int i = 0; i < b.length; i++) { - b[i] = (byte) a[i]; - } - return b; - } - - static void createPackFile(boolean good, File packFile) throws IOException { - FileOutputStream fos = new FileOutputStream(packFile); - fos.write(bytes(magic)); - fos.write(bytes(version_info)); - fos.write(bytes(option)); - fos.write(bytes(size_hi)); - if (good) { - fos.write(bytes(size_lo_correct)); - } else { - fos.write(bytes(size_lo_ulong)); - } - fos.write(bytes(data)); - } - - /* - * This method modifies the LSB of the size_lo for various wicked - * values between MAXINT-0x3F and MAXINT. - */ - static int modifyPackFile(File packFile) throws IOException { - RandomAccessFile raf = new RandomAccessFile(packFile, "rws"); - long len = packFile.length(); - FileChannel fc = raf.getChannel(); - MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_WRITE, 0, len); - int pos = magic.length + version_info.length + option.length + - size_hi.length; - byte value = bb.get(pos); - value--; - bb.position(pos); - bb.put(value); - bb.force(); - fc.truncate(len); - fc.close(); - return value & 0xFF; - } - - static String getUnpack200Cmd() throws Exception { - File binDir = new File(JAVA_HOME, "bin"); - File unpack200File = WINDOWS - ? new File(binDir, "unpack200.exe") - : new File(binDir, "unpack200"); - - String cmd = unpack200File.getAbsolutePath(); - if (!unpack200File.canExecute()) { - throw new Exception("please check" + - cmd + " exists and is executable"); - } - return cmd; - } - - static TestResult runUnpacker(File packFile) throws Exception { - if (!packFile.exists()) { - throw new Exception("please check" + packFile + " exists"); - } - ArrayList alist = new ArrayList(); - ProcessBuilder pb = new ProcessBuilder(getUnpack200Cmd(), - packFile.getName(), "testout.jar"); - Map env = pb.environment(); - pb.directory(new File(".")); - int retval = 0; - try { - pb.redirectErrorStream(true); - Process p = pb.start(); - BufferedReader rd = new BufferedReader( - new InputStreamReader(p.getInputStream()), 8192); - String in = rd.readLine(); - while (in != null) { - alist.add(in); - System.out.println(in); - in = rd.readLine(); - } - retval = p.waitFor(); - p.destroy(); - } catch (Exception ex) { - ex.printStackTrace(); - throw new RuntimeException(ex.getMessage()); - } - return new TestResult("", retval, alist); - } - - /* - * The debug version builds of unpack200 call abort(3) which might set - * an unexpected return value, therefore this test is to determine - * if we are using a product or non-product build and check the - * return value appropriately. - */ - static boolean isNonProductVersion() throws Exception { - ArrayList alist = new ArrayList(); - ProcessBuilder pb = new ProcessBuilder(getUnpack200Cmd(), "--version"); - Map env = pb.environment(); - pb.directory(new File(".")); - int retval = 0; - try { - pb.redirectErrorStream(true); - Process p = pb.start(); - BufferedReader rd = new BufferedReader( - new InputStreamReader(p.getInputStream()), 8192); - String in = rd.readLine(); - while (in != null) { - alist.add(in); - System.out.println(in); - in = rd.readLine(); - } - retval = p.waitFor(); - p.destroy(); - } catch (Exception ex) { - ex.printStackTrace(); - throw new RuntimeException(ex.getMessage()); - } - for (String x : alist) { - if (x.contains("non-product")) { - return true; - } - } - return false; - } - - /** - * @param args the command line arguments - * @throws java.lang.Exception - */ - public static void main(String[] args) throws Exception { - - File packFile = new File("tiny.pack"); - boolean isNPVersion = isNonProductVersion(); - - // Create a good pack file and test if everything is ok - createPackFile(true, packFile); - TestResult tr = runUnpacker(packFile); - tr.setDescription("a good pack file"); - tr.checkPositive(); - tr.isOK(); - System.out.println(tr); - - /* - * jprt systems on windows and linux seem to have abundant memory - * therefore can take a very long time to run, and even if it does - * the error message is not accurate for us to discern if the test - * passess successfully. - */ - if (SIXTYFOUR_BIT && (LINUX || WINDOWS)) { - System.out.println("Warning: Windows/Linux 64bit tests passes vacuously"); - return; - } - - /* - * debug builds call abort, the exit code under these conditions - * are not really relevant. - */ - if (isNPVersion) { - System.out.println("Warning: non-product build: exit values not checked"); - } - - // create a bad pack file - createPackFile(false, packFile); - tr = runUnpacker(packFile); - tr.setDescription("a wicked pack file"); - tr.contains("Native allocation failed"); - if(!isNPVersion) { - tr.checkValue(EXPECTED_EXIT_CODE); - } - System.out.println(tr); - int value = modifyPackFile(packFile); - tr.setDescription("value=" + value); - - // continue creating bad pack files by modifying the specimen pack file. - while (value >= 0xc0) { - tr = runUnpacker(packFile); - tr.contains("Native allocation failed"); - if (!isNPVersion) { - tr.checkValue(EXPECTED_EXIT_CODE); - } - tr.setDescription("wicked value=0x" + - Integer.toHexString(value & 0xFF)); - System.out.println(tr); - value = modifyPackFile(packFile); - } - if (testExitValue != 0) { - throw new Exception("Pack200 archive length tests(" + - testExitValue + ") failed "); - } else { - System.out.println("All tests pass"); - } - } - - /* - * A class to encapsulate the test results and stuff, with some ease - * of use methods to check the test results. - */ - static class TestResult { - - StringBuilder status; - int exitValue; - List testOutput; - String description; - - public TestResult(String str, int rv, List oList) { - status = new StringBuilder(str); - exitValue = rv; - testOutput = oList; - } - - void setDescription(String description) { - this.description = description; - } - - void checkValue(int value) { - if (exitValue != value) { - status = - status.append(" Error: test expected exit value " + - value + "got " + exitValue); - testExitValue++; - } - } - - void checkNegative() { - if (exitValue == 0) { - status = status.append( - " Error: test did not expect 0 exit value"); - - testExitValue++; - } - } - - void checkPositive() { - if (exitValue != 0) { - status = status.append( - " Error: test did not return 0 exit value"); - testExitValue++; - } - } - - boolean isOK() { - return exitValue == 0; - } - - boolean isZeroOutput() { - if (!testOutput.isEmpty()) { - status = status.append(" Error: No message from cmd please"); - testExitValue++; - return false; - } - return true; - } - - boolean isNotZeroOutput() { - if (testOutput.isEmpty()) { - status = status.append(" Error: Missing message"); - testExitValue++; - return false; - } - return true; - } - - public String toString() { - if (debug) { - for (String x : testOutput) { - status = status.append(x + "\n"); - } - } - if (description != null) { - status.insert(0, description); - } - return status.append("\nexitValue = " + exitValue).toString(); - } - - boolean contains(String str) { - for (String x : testOutput) { - if (x.contains(str)) { - return true; - } - } - status = status.append(" Error: string <" + str + "> not found "); - testExitValue++; - return false; - } - } -}