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