1 /* 2 * Copyright (c) 2025, Red Hat, Inc. 3 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * @test id=no_coh_no_cds 27 * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone 28 * @library /test/lib 29 * @requires vm.bits == 64 & vm.debug == true & vm.flagless 30 * @requires os.family != "aix" 31 * @modules java.base/jdk.internal.misc 32 * java.management 33 * @build jdk.test.whitebox.WhiteBox 34 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 35 * @run driver AccessZeroNKlassHitsProtectionZone no_coh_no_cds 36 */ 37 38 /* 39 * @test id=no_coh_cds 40 * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone 41 * @requires vm.cds & vm.bits == 64 & vm.debug == true & vm.flagless 42 * @requires os.family != "aix" 43 * @library /test/lib 44 * @modules java.base/jdk.internal.misc 45 * java.management 46 * @build jdk.test.whitebox.WhiteBox 47 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 48 * @run driver AccessZeroNKlassHitsProtectionZone no_coh_cds 49 */ 50 51 /* 52 * @test id=coh_no_cds 53 * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone 54 * @requires vm.bits == 64 & vm.debug == true & vm.flagless 55 * @requires os.family != "aix" 56 * @library /test/lib 57 * @modules java.base/jdk.internal.misc 58 * java.management 59 * @build jdk.test.whitebox.WhiteBox 60 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 61 * @run driver AccessZeroNKlassHitsProtectionZone coh_no_cds 62 */ 63 64 /* 65 * @test id=coh_cds 66 * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone 67 * @requires vm.cds & vm.bits == 64 & vm.debug == true & vm.flagless 68 * @requires os.family != "aix" 69 * @library /test/lib 70 * @modules java.base/jdk.internal.misc 71 * java.management 72 * @build jdk.test.whitebox.WhiteBox 73 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 74 * @run driver AccessZeroNKlassHitsProtectionZone coh_cds 75 */ 76 77 import jdk.test.lib.process.OutputAnalyzer; 78 import jdk.test.lib.process.ProcessTools; 79 import jdk.test.whitebox.WhiteBox; 80 import jtreg.SkippedException; 81 82 import java.io.File; 83 import java.io.IOException; 84 import java.util.ArrayList; 85 import java.util.regex.Pattern; 86 87 // Test that dereferencing a Klass that is the result of a narrowKlass=0 will give us immediate crashes 88 // that hit the protection zone at encoding base. 89 public class AccessZeroNKlassHitsProtectionZone { 90 91 private static OutputAnalyzer run_test(boolean COH, boolean CDS, String forceBaseString) throws IOException, SkippedException { 92 ArrayList<String> args = new ArrayList<>(); 93 args.add("-Xbootclasspath/a:."); 94 args.add("-XX:+UnlockDiagnosticVMOptions"); 95 args.add("-XX:+WhiteBoxAPI"); 96 args.add("-XX:CompressedClassSpaceSize=128m"); 97 args.add("-Xmx128m"); 98 args.add("-XX:-CreateCoredumpOnCrash"); 99 args.add("-Xlog:metaspace*"); 100 args.add("-Xlog:cds"); 101 if (COH) { 102 args.add("-XX:+UnlockExperimentalVMOptions"); 103 args.add("-XX:+UseCompactObjectHeaders"); 104 } 105 if (CDS) { 106 args.add("-Xshare:on"); 107 } else { 108 args.add("-Xshare:off"); 109 args.add("-XX:CompressedClassSpaceBaseAddress=" + forceBaseString); 110 } 111 args.add(AccessZeroNKlassHitsProtectionZone.class.getName()); 112 args.add("runwb"); 113 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args.toArray(new String[0])); 114 115 OutputAnalyzer output = new OutputAnalyzer(pb.start()); 116 output.reportDiagnosticSummary(); 117 return output; 118 } 119 120 private static void run_test(boolean COH, boolean CDS) throws IOException, SkippedException { 121 // Notes: 122 // We want to enforce zero-based encoding, to test the protection page in that case. For zero-based encoding, 123 // protection page is at address zero, no need to test that. 124 // If CDS is on, we never use zero-based, forceBase is ignored. 125 // If CDS is off, we use forceBase to (somewhat) reliably force the encoding base to beyond 32G, 126 // in order to prevent zero-based encoding. Since that may fail, we try several times. 127 OutputAnalyzer output = null; 128 long forceBase = -1; 129 if (CDS) { 130 output = run_test(COH, CDS, ""); 131 // Not all distributions build COH archives. We tolerate that. 132 if (COH) { 133 String s = output.firstMatch("Specified shared archive file not found .*coh.jsa"); 134 if (s != null) { 135 throw new SkippedException("Failed to find COH archive, was it not built? Skipping test."); 136 } 137 } 138 } else { 139 long g4 = 0x1_0000_0000L; 140 long start = g4 * 8; // 32g 141 long step = g4; 142 long end = start + step * 16; 143 for (forceBase = start; forceBase < end; forceBase += step) { 144 String thisBaseString = String.format("0x%016X", forceBase).toLowerCase(); 145 output = run_test(COH, CDS, thisBaseString); 146 if (output.contains("CompressedClassSpaceBaseAddress=" + thisBaseString + " given, but reserving class space failed.")) { 147 // try next one 148 } else if (output.contains("Successfully forced class space address to " + thisBaseString)) { 149 break; 150 } else { 151 throw new RuntimeException("Unexpected"); 152 } 153 } 154 if (forceBase >= end) { 155 throw new SkippedException("Failed to force ccs to any of the given bases. Skipping test."); 156 } 157 } 158 159 // Parse the encoding base from the output. In case of CDS, it depends on ASLR. Even in case of CDS=off, we want 160 // to double-check it is the force address. 161 String nKlassBaseString = output.firstMatch("Narrow klass base: 0x([0-9a-f]+)", 1); 162 if (nKlassBaseString == null) { 163 throw new RuntimeException("did not find Narrow klass base in log output"); 164 } 165 long nKlassBase = Long.valueOf(nKlassBaseString, 16); 166 167 if (!CDS && nKlassBase != forceBase) { 168 throw new RuntimeException("Weird - we should have mapped at force base"); // .. otherwise we would have skipped out above 169 } 170 if (nKlassBase == 0) { 171 throw new RuntimeException("We should not be running zero-based at this point."); 172 } 173 174 // Calculate the expected crash address pattern. The precise crash address is unknown, but should be located 175 // in the lower part of the guard page following the encoding base. We just accept any address matching the 176 // upper 52 digits (leaving 4K = 12 bits = 4 nibbles of wiggle room) 177 String expectedCrashAddressString = nKlassBaseString.substring(0, nKlassBaseString.length() - 3); 178 179 // output from whitebox function: Klass* should point to encoding base 180 output.shouldMatch("WB_DecodeNKlassAndAccessKlass: nk 0 k 0x" + nKlassBaseString); 181 182 // Then, we should have crashed 183 output.shouldNotHaveExitValue(0); 184 output.shouldContain("# A fatal error has been detected"); 185 186 // The hs-err file should contain a reference to the nKlass protection zone, like this: 187 // "RDI=0x0000000800000000 points into nKlass protection zone" 188 File hsErrFile = HsErrFileUtils.openHsErrFileFromOutput(output); 189 190 ArrayList<Pattern> hsErrPatternList = new ArrayList<>(); 191 hsErrPatternList.add(Pattern.compile(".*(SIGBUS|SIGSEGV|EXCEPTION_ACCESS_VIOLATION).*")); 192 193 hsErrPatternList.add(Pattern.compile(".*siginfo:.*" + expectedCrashAddressString + ".*")); 194 hsErrPatternList.add(Pattern.compile(".*" + expectedCrashAddressString + ".*points into nKlass protection zone.*")); 195 Pattern[] hsErrPattern = hsErrPatternList.toArray(new Pattern[0]); 196 HsErrFileUtils.checkHsErrFileContent(hsErrFile, hsErrPattern, true); 197 } 198 199 enum Argument { runwb, no_coh_no_cds, no_coh_cds, coh_no_cds, coh_cds }; 200 public static void main(String[] args) throws Exception { 201 if (args.length != 1) { 202 throw new RuntimeException("Expecting one argument"); 203 } 204 Argument arg = Argument.valueOf(args[0]); 205 System.out.println(arg); 206 switch (arg) { 207 case runwb -> WhiteBox.getWhiteBox().decodeNKlassAndAccessKlass(0); 208 case no_coh_no_cds -> run_test(false, false); 209 case no_coh_cds -> run_test(false, true); 210 // TODO 8348568 Re-enable 211 // case coh_no_cds -> run_test(true, false); 212 // case coh_cds -> run_test(true, true); 213 } 214 } 215 }