1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.io.File; 18 import java.io.IOException; 19 20 import dalvik.system.VMDebug; 21 22 public class Main { 23 24 private static final String TEMP_FILE_NAME_PREFIX = "test"; 25 private static final String TEMP_FILE_NAME_SUFFIX = ".trace"; 26 createTempFile()27 private static File createTempFile() throws Exception { 28 try { 29 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 30 } catch (IOException e) { 31 System.setProperty("java.io.tmpdir", "/data/local/tmp"); 32 try { 33 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 34 } catch (IOException e2) { 35 System.setProperty("java.io.tmpdir", "/sdcard"); 36 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 37 } 38 } 39 } 40 main(String[] args)41 public static void main(String[] args) throws Exception { 42 System.loadLibrary(args[0]); 43 44 // In case we already run in tracing mode, disable it. 45 if (VMDebug.getMethodTracingMode() != 0) { 46 VMDebug.stopMethodTracing(); 47 } 48 49 File tempFile = createTempFile(); 50 51 // Start method tracing so that native methods get the generic JNI stub. 52 VMDebug.startMethodTracing(tempFile.getPath(), 0, 0, false, 0); 53 54 // We need the caller of `throwsException` to be nterp or compiled, because the clinit check is 55 // executed within the generic JNI stub. The switch interpreter does a clinit check before the 56 // invoke, and that doesn't trigger the bug. 57 Main.ensureJitCompiled(Runner.class, "run"); 58 59 // We want the `Test` class to be loaded after `startMethodTracing`. So we call it through 60 // `Runner` to avoid the verification of the `Main` class preload `Test`. 61 Runner.run(); 62 } 63 ensureJitCompiled(Class<?> cls, String method)64 public static native void ensureJitCompiled(Class<?> cls, String method); 65 } 66 67 class Runner { run()68 public static void run() { 69 try { 70 // Will call through the generic JNI stub and when returning from the native code will need 71 // to walk the stack to throw the exception. We used to crash when walking the stack because 72 // we did not expect to have a generic JNI PC with an entrypoint from a shared boot image 73 // JNI stub. 74 Test.throwsException(); 75 } catch (Test e) { 76 if (!Test.ranInitializer) { 77 throw new Error("Expected Test.ranInitializer to be true"); 78 } 79 return; 80 } 81 throw new Error("Expected exception"); 82 } 83 } 84 85 class Test extends Exception { 86 static { 87 ranInitializer = true; 88 // Will update the entrypoint of `Test.throwsException` to point to a JNI stub from the boot 89 // image. VMDebug.stopMethodTracing()90 VMDebug.stopMethodTracing(); 91 } 92 93 static boolean ranInitializer; throwsException()94 static native void throwsException() throws Test; 95 } 96