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