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