test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java
branchdatagramsocketimpl-branch
changeset 58678 9cf78a70fa4f
parent 54927 1512d88b24c6
child 58679 9c3209ff7550
equal deleted inserted replaced
58677:13588c901957 58678:9cf78a70fa4f
       
     1 /*
       
     2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  *
       
    23  */
       
    24 
       
    25 /*
       
    26  * @test
       
    27  * @summary SharedArchiveConsistency
       
    28  * @requires vm.cds
       
    29  * @library /test/lib
       
    30  * @modules jdk.jartool/sun.tools.jar
       
    31  * @build sun.hotspot.WhiteBox
       
    32  * @compile test-classes/Hello.java
       
    33  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
       
    34  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SharedArchiveConsistency
       
    35  */
       
    36 import jdk.test.lib.process.OutputAnalyzer;
       
    37 import jdk.test.lib.Utils;
       
    38 import java.io.File;
       
    39 import java.io.FileInputStream;
       
    40 import java.io.FileOutputStream;
       
    41 import java.io.IOException;
       
    42 import java.nio.ByteBuffer;
       
    43 import java.nio.ByteOrder;
       
    44 import java.nio.channels.FileChannel;
       
    45 import java.nio.file.Files;
       
    46 import java.nio.file.Path;
       
    47 import java.nio.file.Paths;
       
    48 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
       
    49 import java.nio.file.StandardOpenOption;
       
    50 import static java.nio.file.StandardOpenOption.READ;
       
    51 import static java.nio.file.StandardOpenOption.WRITE;
       
    52 import java.util.ArrayList;
       
    53 import java.util.HashSet;
       
    54 import java.util.List;
       
    55 import java.util.Random;
       
    56 import sun.hotspot.WhiteBox;
       
    57 
       
    58 public class SharedArchiveConsistency {
       
    59     public static WhiteBox wb;
       
    60     public static int offset_magic;     // CDSFileMapHeaderBase::_magic
       
    61     public static int offset_version;   // CDSFileMapHeaderBase::_version
       
    62     public static int offset_jvm_ident; // FileMapHeader::_jvm_ident
       
    63     public static int sp_offset_crc;    // CDSFileMapRegion::_crc
       
    64     public static int file_header_size = -1;// total size of header, variant, need calculation
       
    65     public static int CDSFileMapRegion_size; // size of CDSFileMapRegion
       
    66     public static int sp_offset;       // offset of CDSFileMapRegion
       
    67     public static int sp_used_offset;  // offset of CDSFileMapRegion::_used
       
    68     public static int size_t_size;     // size of size_t
       
    69     public static int int_size;        // size of int
       
    70 
       
    71     public static File jsa;        // will be updated during test
       
    72     public static File orgJsaFile; // kept the original file not touched.
       
    73     // The following should be consistent with the enum in the C++ MetaspaceShared class
       
    74     public static String[] shared_region_name = {
       
    75         "mc",          // MiscCode
       
    76         "rw",          // ReadWrite
       
    77         "ro",          // ReadOnly
       
    78         "md",          // MiscData
       
    79         "first_closed_archive",
       
    80         "last_closed_archive",
       
    81         "first_open_archive",
       
    82         "last_open_archive"
       
    83     };
       
    84 
       
    85     public static int num_regions = shared_region_name.length;
       
    86     public static String[] matchMessages = {
       
    87         "Unable to use shared archive",
       
    88         "An error has occurred while processing the shared archive file.",
       
    89         "Checksum verification failed.",
       
    90         "The shared archive file has been truncated."
       
    91     };
       
    92 
       
    93     public static void getFileOffsetInfo() throws Exception {
       
    94         wb = WhiteBox.getWhiteBox();
       
    95         offset_magic = wb.getOffsetForName("FileMapHeader::_magic");
       
    96         offset_version = wb.getOffsetForName("FileMapHeader::_version");
       
    97         offset_jvm_ident = wb.getOffsetForName("FileMapHeader::_jvm_ident");
       
    98         sp_offset_crc = wb.getOffsetForName("CDSFileMapRegion::_crc");
       
    99         try {
       
   100             int nonExistOffset = wb.getOffsetForName("FileMapHeader::_non_exist_offset");
       
   101             System.exit(-1); // should fail
       
   102         } catch (Exception e) {
       
   103             // success
       
   104         }
       
   105 
       
   106         sp_offset = wb.getOffsetForName("FileMapHeader::_space[0]") - offset_magic;
       
   107         sp_used_offset = wb.getOffsetForName("CDSFileMapRegion::_used") - sp_offset_crc;
       
   108         size_t_size = wb.getOffsetForName("size_t_size");
       
   109         CDSFileMapRegion_size  = wb.getOffsetForName("CDSFileMapRegion_size");
       
   110     }
       
   111 
       
   112     public static int getFileHeaderSize(FileChannel fc) throws Exception {
       
   113         if (file_header_size != -1) {
       
   114             return file_header_size;
       
   115         }
       
   116         // this is not real header size, it is struct size
       
   117         int_size = wb.getOffsetForName("int_size");
       
   118         file_header_size = wb.getOffsetForName("file_header_size");
       
   119         System.out.println("file_header_size      = " + file_header_size);
       
   120         file_header_size = (int)align_up_page(file_header_size);
       
   121         System.out.println("file_header_size (aligned to page) = " + file_header_size);
       
   122         return file_header_size;
       
   123     }
       
   124 
       
   125     public static long align_up_page(long l) throws Exception {
       
   126         // wb is obtained in getFileOffsetInfo() which is called first in main() else we should call
       
   127         // WhiteBox.getWhiteBox() here first.
       
   128         int pageSize = wb.getVMPageSize();
       
   129         return (l + pageSize -1) & (~ (pageSize - 1));
       
   130     }
       
   131 
       
   132     private static long getRandomBetween(long start, long end) throws Exception {
       
   133         if (start > end) {
       
   134             throw new IllegalArgumentException("start must be less than end");
       
   135         }
       
   136         Random aRandom = Utils.getRandomInstance();
       
   137         int d = aRandom.nextInt((int)(end - start));
       
   138         if (d < 1) {
       
   139             d = 1;
       
   140         }
       
   141         return start + d;
       
   142     }
       
   143 
       
   144     public static long readInt(FileChannel fc, long offset, int nbytes) throws Exception {
       
   145         ByteBuffer bb = ByteBuffer.allocate(nbytes);
       
   146         bb.order(ByteOrder.nativeOrder());
       
   147         fc.position(offset);
       
   148         fc.read(bb);
       
   149         return  (nbytes > 4 ? bb.getLong(0) : bb.getInt(0));
       
   150     }
       
   151 
       
   152     public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception {
       
   153         fc.position(offset);
       
   154         fc.write(bb);
       
   155     }
       
   156 
       
   157     public static FileChannel getFileChannel(File jsaFile) throws Exception {
       
   158         List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
       
   159         arry.add(READ);
       
   160         arry.add(WRITE);
       
   161         return FileChannel.open(jsaFile.toPath(), new HashSet<StandardOpenOption>(arry));
       
   162     }
       
   163 
       
   164     public static void modifyJsaContentRandomly(File jsaFile) throws Exception {
       
   165         FileChannel fc = getFileChannel(jsaFile);
       
   166         // corrupt random area in the data areas
       
   167         long[] used    = new long[num_regions];       // record used bytes
       
   168         long start0, start, end, off;
       
   169         int used_offset, path_info_size;
       
   170 
       
   171         int bufSize;
       
   172         System.out.printf("%-24s%12s%12s%16s\n", "Space Name", "Used bytes", "Reg Start", "Random Offset");
       
   173         start0 = getFileHeaderSize(fc);
       
   174         for (int i = 0; i < num_regions; i++) {
       
   175             used[i] = get_region_used_size_aligned(fc, i);
       
   176             start = start0;
       
   177             for (int j = 0; j < i; j++) {
       
   178                 start += align_up_page(used[j]);
       
   179             }
       
   180             end = start + used[i];
       
   181             if (start == end) {
       
   182                 continue; // Ignore empty regions
       
   183             }
       
   184             off = getRandomBetween(start, end);
       
   185             System.out.printf("%-24s%12d%12d%16d\n", shared_region_name[i], used[i], start, off);
       
   186             if (end - off < 1024) {
       
   187                 bufSize = (int)(end - off + 1);
       
   188             } else {
       
   189                 bufSize = 1024;
       
   190             }
       
   191             ByteBuffer bbuf = ByteBuffer.wrap(new byte[bufSize]);
       
   192             writeData(fc, off, bbuf);
       
   193         }
       
   194         if (fc.isOpen()) {
       
   195             fc.close();
       
   196         }
       
   197     }
       
   198 
       
   199     static long get_region_used_size_aligned(FileChannel fc, int region) throws Exception {
       
   200         long n = sp_offset + CDSFileMapRegion_size * region + sp_used_offset;
       
   201         long alignment = WhiteBox.getWhiteBox().metaspaceReserveAlignment();
       
   202         long used = readInt(fc, n, size_t_size);
       
   203         used = (used + alignment - 1) & ~(alignment - 1);
       
   204         return used;
       
   205     }
       
   206 
       
   207     public static boolean modifyJsaContent(int region, File jsaFile) throws Exception {
       
   208         FileChannel fc = getFileChannel(jsaFile);
       
   209         byte[] buf = new byte[4096];
       
   210         ByteBuffer bbuf = ByteBuffer.wrap(buf);
       
   211 
       
   212         long total = 0L;
       
   213         long[] used = new long[num_regions];
       
   214         System.out.printf("%-24s%12s\n", "Space name", "Used bytes");
       
   215         for (int i = 0; i < num_regions; i++) {
       
   216             used[i] = get_region_used_size_aligned(fc, i);
       
   217             System.out.printf("%-24s%12d\n", shared_region_name[i], used[i]);
       
   218             total += used[i];
       
   219         }
       
   220         System.out.printf("%-24s%12d\n", "Total: ", total);
       
   221         long header_size = getFileHeaderSize(fc);
       
   222         long region_start_offset = header_size;
       
   223         for (int i=0; i<region; i++) {
       
   224             region_start_offset += used[i];
       
   225         }
       
   226         if (used[region] == 0) {
       
   227             System.out.println("Region " + shared_region_name[region] + " is empty. Nothing to corrupt.");
       
   228             return false;
       
   229         }
       
   230         System.out.println("Corrupt " + shared_region_name[region] + " section, start = " + region_start_offset
       
   231                            + " (header_size + 0x" + Long.toHexString(region_start_offset-header_size) + ")");
       
   232         long bytes_written = 0L;
       
   233         while (bytes_written < used[region]) {
       
   234             writeData(fc, region_start_offset + bytes_written, bbuf);
       
   235             bbuf.clear();
       
   236             bytes_written += 4096;
       
   237         }
       
   238         if (fc.isOpen()) {
       
   239             fc.close();
       
   240         }
       
   241         return true;
       
   242     }
       
   243 
       
   244     public static void modifyJsaHeader(File jsaFile) throws Exception {
       
   245         FileChannel fc = getFileChannel(jsaFile);
       
   246         // screw up header info
       
   247         byte[] buf = new byte[getFileHeaderSize(fc)];
       
   248         ByteBuffer bbuf = ByteBuffer.wrap(buf);
       
   249         writeData(fc, 0L, bbuf);
       
   250         if (fc.isOpen()) {
       
   251             fc.close();
       
   252         }
       
   253     }
       
   254 
       
   255     public static void modifyJvmIdent() throws Exception {
       
   256         FileChannel fc = getFileChannel(jsa);
       
   257         int headerSize = getFileHeaderSize(fc);
       
   258         System.out.println("    offset_jvm_ident " + offset_jvm_ident);
       
   259         byte[] buf = new byte[256];
       
   260         ByteBuffer bbuf = ByteBuffer.wrap(buf);
       
   261         writeData(fc, (long)offset_jvm_ident, bbuf);
       
   262         if (fc.isOpen()) {
       
   263             fc.close();
       
   264         }
       
   265     }
       
   266 
       
   267     public static void modifyHeaderIntField(long offset, int value) throws Exception {
       
   268         FileChannel fc = getFileChannel(jsa);
       
   269         int headerSize = getFileHeaderSize(fc);
       
   270         System.out.println("    offset " + offset);
       
   271         byte[] buf = ByteBuffer.allocate(4).putInt(value).array();
       
   272         ByteBuffer bbuf = ByteBuffer.wrap(buf);
       
   273         writeData(fc, offset, bbuf);
       
   274         if (fc.isOpen()) {
       
   275             fc.close();
       
   276         }
       
   277     }
       
   278 
       
   279     public static void copyFile(File from, File to) throws Exception {
       
   280         if (to.exists()) {
       
   281             if(!to.delete()) {
       
   282                 throw new IOException("Could not delete file " + to);
       
   283             }
       
   284         }
       
   285         to.createNewFile();
       
   286         setReadWritePermission(to);
       
   287         Files.copy(from.toPath(), to.toPath(), REPLACE_EXISTING);
       
   288     }
       
   289 
       
   290     // Copy file with bytes deleted or inserted
       
   291     // del -- true, deleted, false, inserted
       
   292     public static void copyFile(File from, File to, boolean del) throws Exception {
       
   293         try (
       
   294             FileChannel inputChannel = new FileInputStream(from).getChannel();
       
   295             FileChannel outputChannel = new FileOutputStream(to).getChannel()
       
   296         ) {
       
   297             long size = inputChannel.size();
       
   298             int init_size = getFileHeaderSize(inputChannel);
       
   299             outputChannel.transferFrom(inputChannel, 0, init_size);
       
   300             int n = (int)getRandomBetween(0, 1024);
       
   301             if (del) {
       
   302                 System.out.println("Delete " + n + " bytes at data start section");
       
   303                 inputChannel.position(init_size + n);
       
   304                 outputChannel.transferFrom(inputChannel, init_size, size - init_size - n);
       
   305             } else {
       
   306                 System.out.println("Insert " + n + " bytes at data start section");
       
   307                 outputChannel.position(init_size);
       
   308                 outputChannel.write(ByteBuffer.wrap(new byte[n]));
       
   309                 outputChannel.transferFrom(inputChannel, init_size + n , size - init_size);
       
   310             }
       
   311         }
       
   312     }
       
   313 
       
   314     public static void restoreJsaFile() throws Exception {
       
   315         Files.copy(orgJsaFile.toPath(), jsa.toPath(), REPLACE_EXISTING);
       
   316     }
       
   317 
       
   318     public static void setReadWritePermission(File file) throws Exception {
       
   319         if (!file.canRead()) {
       
   320             if (!file.setReadable(true)) {
       
   321                 throw new IOException("Cannot modify file " + file + " as readable");
       
   322             }
       
   323         }
       
   324         if (!file.canWrite()) {
       
   325             if (!file.setWritable(true)) {
       
   326                 throw new IOException("Cannot modify file " + file + " as writable");
       
   327             }
       
   328         }
       
   329     }
       
   330 
       
   331     public static void testAndCheck(String[] execArgs) throws Exception {
       
   332         OutputAnalyzer output = TestCommon.execCommon(execArgs);
       
   333         String stdtxt = output.getOutput();
       
   334         System.out.println("Note: this test may fail in very rare occasions due to CRC32 checksum collision");
       
   335         for (String message : matchMessages) {
       
   336             if (stdtxt.contains(message)) {
       
   337                 // match any to return
       
   338                 return;
       
   339             }
       
   340         }
       
   341         TestCommon.checkExec(output);
       
   342     }
       
   343 
       
   344     // dump with hello.jsa, then
       
   345     // read the jsa file
       
   346     //   1) run normal
       
   347     //   2) modify header
       
   348     //   3) keep header correct but modify content in each region specified by shared_region_name[]
       
   349     //   4) update both header and content, test
       
   350     //   5) delete bytes in data begining
       
   351     //   6) insert bytes in data begining
       
   352     //   7) randomly corrupt data in each region specified by shared_region_name[]
       
   353     public static void main(String... args) throws Exception {
       
   354         // must call to get offset info first!!!
       
   355         getFileOffsetInfo();
       
   356         Path currentRelativePath = Paths.get("");
       
   357         String currentDir = currentRelativePath.toAbsolutePath().toString();
       
   358         System.out.println("Current relative path is: " + currentDir);
       
   359         // get jar file
       
   360         String jarFile = JarBuilder.getOrCreateHelloJar();
       
   361 
       
   362         // dump (appcds.jsa created)
       
   363         TestCommon.testDump(jarFile, null);
       
   364 
       
   365         // test, should pass
       
   366         System.out.println("1. Normal, should pass but may fail\n");
       
   367 
       
   368         String[] execArgs = {"-Xlog:cds", "-cp", jarFile, "Hello"};
       
   369         // tests that corrupt contents of the archive need to run with
       
   370         // VerifySharedSpaces enabled to detect inconsistencies
       
   371         String[] verifyExecArgs = {"-Xlog:cds", "-XX:+VerifySharedSpaces", "-cp", jarFile, "Hello"};
       
   372 
       
   373         OutputAnalyzer output = TestCommon.execCommon(execArgs);
       
   374 
       
   375         try {
       
   376             TestCommon.checkExecReturn(output, 0, true, "Hello World");
       
   377         } catch (Exception e) {
       
   378             TestCommon.checkExecReturn(output, 1, true, matchMessages[0]);
       
   379         }
       
   380 
       
   381         // get current archive name
       
   382         jsa = new File(TestCommon.getCurrentArchiveName());
       
   383         if (!jsa.exists()) {
       
   384             throw new IOException(jsa + " does not exist!");
       
   385         }
       
   386 
       
   387         setReadWritePermission(jsa);
       
   388 
       
   389         // save as original untouched
       
   390         orgJsaFile = new File(new File(currentDir), "appcds.jsa.bak");
       
   391         copyFile(jsa, orgJsaFile);
       
   392 
       
   393         // modify jsa header, test should fail
       
   394         System.out.println("\n2. Corrupt header, should fail\n");
       
   395         modifyJsaHeader(jsa);
       
   396         output = TestCommon.execCommon(execArgs);
       
   397         output.shouldContain("The shared archive file has a bad magic number");
       
   398         output.shouldNotContain("Checksum verification failed");
       
   399 
       
   400         copyFile(orgJsaFile, jsa);
       
   401         // modify _jvm_ident, test should fail
       
   402         System.out.println("\n2a. Corrupt _jvm_ident, should fail\n");
       
   403         modifyJvmIdent();
       
   404         output = TestCommon.execCommon(execArgs);
       
   405         output.shouldContain("The shared archive file was created by a different version or build of HotSpot");
       
   406         output.shouldNotContain("Checksum verification failed");
       
   407 
       
   408         copyFile(orgJsaFile, jsa);
       
   409         // modify _jvm_ident and run with -Xshare:auto
       
   410         System.out.println("\n2b. Corrupt _jvm_ident run with -Xshare:auto\n");
       
   411         modifyJvmIdent();
       
   412         output = TestCommon.execAuto(execArgs);
       
   413         output.shouldContain("The shared archive file was created by a different version or build of HotSpot");
       
   414         output.shouldContain("Hello World");
       
   415 
       
   416         copyFile(orgJsaFile, jsa);
       
   417         // modify _magic, test should fail
       
   418         System.out.println("\n2c. Corrupt _magic, should fail\n");
       
   419         modifyHeaderIntField(offset_magic, 0x00000000);
       
   420         output = TestCommon.execCommon(execArgs);
       
   421         output.shouldContain("The shared archive file has a bad magic number");
       
   422         output.shouldNotContain("Checksum verification failed");
       
   423 
       
   424         copyFile(orgJsaFile, jsa);
       
   425         // modify _version, test should fail
       
   426         System.out.println("\n2d. Corrupt _version, should fail\n");
       
   427         modifyHeaderIntField(offset_version, 0x00000000);
       
   428         output = TestCommon.execCommon(execArgs);
       
   429         output.shouldContain("The shared archive file has the wrong version");
       
   430         output.shouldNotContain("Checksum verification failed");
       
   431 
       
   432         File newJsaFile = null;
       
   433         // modify content
       
   434         System.out.println("\n3. Corrupt Content, should fail\n");
       
   435         for (int i=0; i<num_regions; i++) {
       
   436             newJsaFile = new File(TestCommon.getNewArchiveName(shared_region_name[i]));
       
   437             copyFile(orgJsaFile, newJsaFile);
       
   438             TestCommon.setCurrentArchiveName(newJsaFile.toString());
       
   439             if (modifyJsaContent(i, newJsaFile)) {
       
   440                 testAndCheck(verifyExecArgs);
       
   441             }
       
   442         }
       
   443 
       
   444         // modify both header and content, test should fail
       
   445         System.out.println("\n4. Corrupt Header and Content, should fail\n");
       
   446         newJsaFile = new File(TestCommon.getNewArchiveName("header-and-content"));
       
   447         copyFile(orgJsaFile, newJsaFile);
       
   448         TestCommon.setCurrentArchiveName(newJsaFile.toString());
       
   449         modifyJsaHeader(newJsaFile);
       
   450         modifyJsaContent(0, newJsaFile);  // this will not be reached since failed on header change first
       
   451         output = TestCommon.execCommon(execArgs);
       
   452         output.shouldContain("The shared archive file has a bad magic number");
       
   453         output.shouldNotContain("Checksum verification failed");
       
   454 
       
   455         // delete bytes in data section
       
   456         System.out.println("\n5. Delete bytes at beginning of data section, should fail\n");
       
   457         copyFile(orgJsaFile, jsa, true);
       
   458         TestCommon.setCurrentArchiveName(jsa.toString());
       
   459         testAndCheck(verifyExecArgs);
       
   460 
       
   461         // insert bytes in data section forward
       
   462         System.out.println("\n6. Insert bytes at beginning of data section, should fail\n");
       
   463         copyFile(orgJsaFile, jsa, false);
       
   464         testAndCheck(verifyExecArgs);
       
   465 
       
   466         System.out.println("\n7. modify Content in random areas, should fail\n");
       
   467         newJsaFile = new File(TestCommon.getNewArchiveName("random-areas"));
       
   468         copyFile(orgJsaFile, newJsaFile);
       
   469         TestCommon.setCurrentArchiveName(newJsaFile.toString());
       
   470         modifyJsaContentRandomly(newJsaFile);
       
   471         testAndCheck(verifyExecArgs);
       
   472     }
       
   473 }