changeset 1122:392c4225d636

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
author ksrini
date Wed, 18 Feb 2009 14:14:03 -0800
parents 6a4e03cc03bb
children 7f4cf1eb7586
files src/share/native/com/sun/java/util/jar/pack/bands.cpp src/share/native/com/sun/java/util/jar/pack/coding.cpp src/share/native/com/sun/java/util/jar/pack/defines.h src/share/native/com/sun/java/util/jar/pack/unpack.cpp test/tools/pack200/MemoryAllocatorTest.java
diffstat 5 files changed, 97 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/native/com/sun/java/util/jar/pack/bands.cpp	Mon Jan 05 11:28:43 2009 -0800
+++ b/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;
--- a/src/share/native/com/sun/java/util/jar/pack/coding.cpp	Mon Jan 05 11:28:43 2009 -0800
+++ b/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;
--- a/src/share/native/com/sun/java/util/jar/pack/defines.h	Mon Jan 05 11:28:43 2009 -0800
+++ b/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"
 
--- a/src/share/native/com/sun/java/util/jar/pack/unpack.cpp	Mon Jan 05 11:28:43 2009 -0800
+++ b/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) {
--- a/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<String> alist = new ArrayList<String>();
-        ProcessBuilder pb = new ProcessBuilder(getUnpack200Cmd(),
-                packFile.getName(), "testout.jar");
-        Map<String, String> 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<String> alist = new ArrayList<String>();
-        ProcessBuilder pb = new ProcessBuilder(getUnpack200Cmd(), "--version");
-        Map<String, String> 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<String> testOutput;
-        String description;
-
-        public TestResult(String str, int rv, List<String> 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;
-        }
-    }
-}