1 /*
2  * Copyright (C) 2017 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 #include "base/sdk_version.h"
18 #include "dex/art_dex_file_loader.h"
19 #include "hidden_api.h"
20 #include "jni.h"
21 #include "runtime.h"
22 #include "ti-agent/scoped_utf_chars.h"
23 
24 namespace art {
25 namespace Test674HiddenApi {
26 
27 // Should be the same as dalvik.system.VMRuntime.PREVENT_META_REFLECTION_BLOCKLIST_ACCESS
28 static constexpr uint64_t kPreventMetaReflectionBlocklistAccess = 142365358;
29 
30 std::vector<std::vector<std::unique_ptr<const DexFile>>> opened_dex_files;
31 
Java_Main_init(JNIEnv *,jclass)32 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
33   Runtime* runtime = Runtime::Current();
34   runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
35   runtime->SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
36   runtime->SetTargetSdkVersion(
37       static_cast<uint32_t>(hiddenapi::ApiList::MaxTargetO().GetMaxAllowedSdkVersion()));
38   runtime->SetDedupeHiddenApiWarnings(false);
39 }
40 
Java_Main_setDexDomain(JNIEnv *,jclass,jint int_index,jboolean is_core_platform)41 extern "C" JNIEXPORT void JNICALL Java_Main_setDexDomain(
42     JNIEnv*, jclass, jint int_index, jboolean is_core_platform) {
43   size_t index = static_cast<size_t>(int_index);
44   CHECK_LT(index, opened_dex_files.size());
45   for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
46     const_cast<DexFile*>(dex_file.get())->SetHiddenapiDomain(
47         (is_core_platform == JNI_FALSE) ? hiddenapi::Domain::kPlatform
48                                         : hiddenapi::Domain::kCorePlatform);
49   }
50 }
51 
Java_Main_appendToBootClassLoader(JNIEnv * env,jclass klass,jstring jpath,jboolean is_core_platform)52 extern "C" JNIEXPORT jint JNICALL Java_Main_appendToBootClassLoader(
53     JNIEnv* env, jclass klass, jstring jpath, jboolean is_core_platform) {
54   ScopedUtfChars utf(env, jpath);
55   const char* path = utf.c_str();
56   CHECK(path != nullptr);
57 
58   const size_t index = opened_dex_files.size();
59   const jint int_index = static_cast<jint>(index);
60   opened_dex_files.push_back(std::vector<std::unique_ptr<const DexFile>>());
61 
62   DexFileLoader dex_loader(path);
63   std::string error_msg;
64 
65   if (!dex_loader.Open(/* verify */ false,
66                        /* verify_checksum */ true,
67                        &error_msg,
68                        &opened_dex_files[index])) {
69     LOG(FATAL) << "Could not open " << path << " for boot classpath extension: " << error_msg;
70     UNREACHABLE();
71   }
72 
73   Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
74 
75   Runtime::Current()->AppendToBootClassPath(path, path, opened_dex_files[index]);
76 
77   return int_index;
78 }
79 
Java_Main_setSdkAll(JNIEnv *,jclass,jboolean value)80 extern "C" JNIEXPORT void JNICALL Java_Main_setSdkAll(JNIEnv*, jclass, jboolean value) {
81   std::vector<std::string> exemptions;
82   if (value != JNI_FALSE) {
83     exemptions.push_back("L");
84   }
85   Runtime::Current()->SetHiddenApiExemptions(exemptions);
86 }
87 
NewInstance(JNIEnv * env,jclass klass)88 static jobject NewInstance(JNIEnv* env, jclass klass) {
89   jmethodID constructor = env->GetMethodID(klass, "<init>", "()V");
90   if (constructor == nullptr) {
91     return nullptr;
92   }
93   return env->NewObject(klass, constructor);
94 }
95 
Java_JNI_canDiscoverField(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)96 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverField(
97     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
98   ScopedUtfChars utf_name(env, name);
99   jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
100                              : env->GetFieldID(klass, utf_name.c_str(), "I");
101   if (field == nullptr) {
102     env->ExceptionClear();
103     return JNI_FALSE;
104   }
105 
106   return JNI_TRUE;
107 }
108 
Java_JNI_canGetField(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)109 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canGetField(
110     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
111   ScopedUtfChars utf_name(env, name);
112   jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
113                              : env->GetFieldID(klass, utf_name.c_str(), "I");
114   if (field == nullptr) {
115     env->ExceptionClear();
116     return JNI_FALSE;
117   }
118   if (is_static) {
119     env->GetStaticIntField(klass, field);
120   } else {
121     jobject obj = NewInstance(env, klass);
122     if (obj == nullptr) {
123       env->ExceptionDescribe();
124       env->ExceptionClear();
125       return JNI_FALSE;
126     }
127     env->GetIntField(obj, field);
128   }
129 
130   if (env->ExceptionOccurred()) {
131     env->ExceptionDescribe();
132     env->ExceptionClear();
133     return JNI_FALSE;
134   }
135 
136   return JNI_TRUE;
137 }
138 
Java_JNI_canSetField(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)139 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canSetField(
140     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
141   ScopedUtfChars utf_name(env, name);
142   jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
143                              : env->GetFieldID(klass, utf_name.c_str(), "I");
144   if (field == nullptr) {
145     env->ExceptionClear();
146     return JNI_FALSE;
147   }
148   if (is_static) {
149     env->SetStaticIntField(klass, field, 42);
150   } else {
151     jobject obj = NewInstance(env, klass);
152     if (obj == nullptr) {
153       env->ExceptionDescribe();
154       env->ExceptionClear();
155       return JNI_FALSE;
156     }
157     env->SetIntField(obj, field, 42);
158   }
159 
160   if (env->ExceptionOccurred()) {
161     env->ExceptionDescribe();
162     env->ExceptionClear();
163     return JNI_FALSE;
164   }
165 
166   return JNI_TRUE;
167 }
168 
Java_JNI_canDiscoverMethod(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)169 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverMethod(
170     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
171   ScopedUtfChars utf_name(env, name);
172   jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
173                                : env->GetMethodID(klass, utf_name.c_str(), "()I");
174   if (method == nullptr) {
175     env->ExceptionClear();
176     return JNI_FALSE;
177   }
178 
179   return JNI_TRUE;
180 }
181 
Java_JNI_canInvokeMethodA(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)182 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodA(
183     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
184   ScopedUtfChars utf_name(env, name);
185   jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
186                                : env->GetMethodID(klass, utf_name.c_str(), "()I");
187   if (method == nullptr) {
188     env->ExceptionClear();
189     return JNI_FALSE;
190   }
191 
192   if (is_static) {
193     env->CallStaticIntMethodA(klass, method, nullptr);
194   } else {
195     jobject obj = NewInstance(env, klass);
196     if (obj == nullptr) {
197       env->ExceptionDescribe();
198       env->ExceptionClear();
199       return JNI_FALSE;
200     }
201     env->CallIntMethodA(obj, method, nullptr);
202   }
203 
204   if (env->ExceptionOccurred()) {
205     env->ExceptionDescribe();
206     env->ExceptionClear();
207     return JNI_FALSE;
208   }
209 
210   return JNI_TRUE;
211 }
212 
Java_JNI_canInvokeMethodV(JNIEnv * env,jclass,jclass klass,jstring name,jboolean is_static)213 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeMethodV(
214     JNIEnv* env, jclass, jclass klass, jstring name, jboolean is_static) {
215   ScopedUtfChars utf_name(env, name);
216   jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
217                                : env->GetMethodID(klass, utf_name.c_str(), "()I");
218   if (method == nullptr) {
219     env->ExceptionClear();
220     return JNI_FALSE;
221   }
222 
223   if (is_static) {
224     env->CallStaticIntMethod(klass, method);
225   } else {
226     jobject obj = NewInstance(env, klass);
227     if (obj == nullptr) {
228       env->ExceptionDescribe();
229       env->ExceptionClear();
230       return JNI_FALSE;
231     }
232     env->CallIntMethod(obj, method);
233   }
234 
235   if (env->ExceptionOccurred()) {
236     env->ExceptionDescribe();
237     env->ExceptionClear();
238     return JNI_FALSE;
239   }
240 
241   return JNI_TRUE;
242 }
243 
244 static constexpr size_t kConstructorSignatureLength = 5;  // e.g. (IZ)V
245 static constexpr size_t kNumConstructorArgs = kConstructorSignatureLength - 3;
246 
Java_JNI_canDiscoverConstructor(JNIEnv * env,jclass,jclass klass,jstring args)247 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canDiscoverConstructor(
248     JNIEnv* env, jclass, jclass klass, jstring args) {
249   ScopedUtfChars utf_args(env, args);
250   jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
251   if (constructor == nullptr) {
252     env->ExceptionClear();
253     return JNI_FALSE;
254   }
255 
256   return JNI_TRUE;
257 }
258 
Java_JNI_canInvokeConstructorA(JNIEnv * env,jclass,jclass klass,jstring args)259 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorA(
260     JNIEnv* env, jclass, jclass klass, jstring args) {
261   ScopedUtfChars utf_args(env, args);
262   jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
263   if (constructor == nullptr) {
264     env->ExceptionClear();
265     return JNI_FALSE;
266   }
267 
268   // CheckJNI won't allow out-of-range values, so just zero everything.
269   CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
270   size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
271   jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
272   memset(initargs, 0, initargs_size);
273 
274   env->NewObjectA(klass, constructor, initargs);
275   if (env->ExceptionOccurred()) {
276     env->ExceptionDescribe();
277     env->ExceptionClear();
278     return JNI_FALSE;
279   }
280 
281   return JNI_TRUE;
282 }
283 
Java_JNI_canInvokeConstructorV(JNIEnv * env,jclass,jclass klass,jstring args)284 extern "C" JNIEXPORT jboolean JNICALL Java_JNI_canInvokeConstructorV(
285     JNIEnv* env, jclass, jclass klass, jstring args) {
286   ScopedUtfChars utf_args(env, args);
287   jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
288   if (constructor == nullptr) {
289     env->ExceptionClear();
290     return JNI_FALSE;
291   }
292 
293   // CheckJNI won't allow out-of-range values, so just zero everything.
294   CHECK_EQ(strlen(utf_args.c_str()), kConstructorSignatureLength);
295   size_t initargs_size = sizeof(jvalue) * kNumConstructorArgs;
296   jvalue *initargs = reinterpret_cast<jvalue*>(alloca(initargs_size));
297   memset(initargs, 0, initargs_size);
298 
299   static_assert(kNumConstructorArgs == 2, "Change the varargs below if you change the constant");
300   env->NewObject(klass, constructor, initargs[0], initargs[1]);
301   if (env->ExceptionOccurred()) {
302     env->ExceptionDescribe();
303     env->ExceptionClear();
304     return JNI_FALSE;
305   }
306 
307   return JNI_TRUE;
308 }
309 
Java_Reflection_getHiddenApiAccessFlags(JNIEnv *,jclass)310 extern "C" JNIEXPORT jint JNICALL Java_Reflection_getHiddenApiAccessFlags(JNIEnv*, jclass) {
311   return static_cast<jint>(kAccHiddenapiBits);
312 }
313 
Java_Reflection_setHiddenApiCheckHardening(JNIEnv *,jclass,jboolean value)314 extern "C" JNIEXPORT void JNICALL Java_Reflection_setHiddenApiCheckHardening(JNIEnv*, jclass,
315     jboolean value) {
316   CompatFramework& compat_framework = Runtime::Current()->GetCompatFramework();
317   std::set<uint64_t> disabled_changes = compat_framework.GetDisabledCompatChanges();
318   if (value == JNI_TRUE) {
319     // If hidden api check hardening is enabled, remove it from the set of disabled changes.
320     disabled_changes.erase(kPreventMetaReflectionBlocklistAccess);
321   } else {
322     // If hidden api check hardening is disabled, add it to the set of disabled changes.
323     disabled_changes.insert(kPreventMetaReflectionBlocklistAccess);
324   }
325   compat_framework.SetDisabledCompatChanges(disabled_changes);
326 }
327 
328 }  // namespace Test674HiddenApi
329 }  // namespace art
330