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