1 /*
2 * Copyright (C) 2021 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 "service.h"
18
19 #include <jni.h>
20
21 #include <filesystem>
22 #include <string_view>
23
24 #include "android-base/errors.h"
25 #include "android-base/file.h"
26 #include "android-base/properties.h"
27 #include "android-base/result.h"
28 #include "class_loader_context.h"
29 #include "gc/heap.h"
30 #include "nativehelper/JNIHelp.h"
31 #include "nativehelper/utils.h"
32 #include "runtime.h"
33 #include "tools/tools.h"
34
35 namespace art {
36 namespace service {
37
38 using ::android::base::Dirname;
39 using ::android::base::Result;
40 using ::android::base::SetProperty;
41
ValidateAbsoluteNormalPath(const std::string & path_str)42 Result<void> ValidateAbsoluteNormalPath(const std::string& path_str) {
43 if (path_str.empty()) {
44 return Errorf("Path is empty");
45 }
46 if (path_str.find('\0') != std::string::npos) {
47 return Errorf("Path '{}' has invalid character '\\0'", path_str);
48 }
49 std::filesystem::path path(path_str);
50 if (!path.is_absolute()) {
51 return Errorf("Path '{}' is not an absolute path", path_str);
52 }
53 if (path.lexically_normal() != path_str) {
54 return Errorf("Path '{}' is not in normal form", path_str);
55 }
56 return {};
57 }
58
ValidatePathElementSubstring(const std::string & path_element_substring,const std::string & name)59 Result<void> ValidatePathElementSubstring(const std::string& path_element_substring,
60 const std::string& name) {
61 if (path_element_substring.empty()) {
62 return Errorf("{} is empty", name);
63 }
64 if (path_element_substring.find('/') != std::string::npos) {
65 return Errorf("{} '{}' has invalid character '/'", name, path_element_substring);
66 }
67 if (path_element_substring.find('\0') != std::string::npos) {
68 return Errorf("{} '{}' has invalid character '\\0'", name, path_element_substring);
69 }
70 return {};
71 }
72
ValidatePathElement(const std::string & path_element,const std::string & name)73 Result<void> ValidatePathElement(const std::string& path_element, const std::string& name) {
74 OR_RETURN(ValidatePathElementSubstring(path_element, name));
75 if (path_element == "." || path_element == "..") {
76 return Errorf("Invalid {} '{}'", name, path_element);
77 }
78 return {};
79 }
80
ValidateDexPath(const std::string & dex_path)81 Result<void> ValidateDexPath(const std::string& dex_path) {
82 OR_RETURN(ValidateAbsoluteNormalPath(dex_path));
83 return {};
84 }
85
ValidateClassLoaderContext(std::string_view dex_path,const std::string & class_loader_context)86 android::base::Result<void> ValidateClassLoaderContext(std::string_view dex_path,
87 const std::string& class_loader_context) {
88 if (class_loader_context == ClassLoaderContext::kUnsupportedClassLoaderContextEncoding) {
89 return {};
90 }
91
92 std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(class_loader_context);
93 if (context == nullptr) {
94 return Errorf("Class loader context '{}' is invalid", class_loader_context);
95 }
96
97 std::vector<std::string> flattened_context = context->FlattenDexPaths();
98 std::string dex_dir = Dirname(dex_path);
99 for (const std::string& context_element : flattened_context) {
100 std::string context_path = std::filesystem::path(dex_dir).append(context_element);
101 OR_RETURN(ValidateDexPath(context_path));
102 }
103
104 return {};
105 }
106
GetGarbageCollector()107 std::string GetGarbageCollector() {
108 return Runtime::Current()->GetHeap()->GetForegroundCollectorName();
109 }
110
111 extern "C" JNIEXPORT jstring JNICALL
Java_com_android_server_art_ArtJni_validateDexPathNative(JNIEnv * env,jobject,jstring j_dex_path)112 Java_com_android_server_art_ArtJni_validateDexPathNative(JNIEnv* env, jobject, jstring j_dex_path) {
113 std::string dex_path(GET_UTF_OR_RETURN(env, j_dex_path));
114
115 if (Result<void> result = ValidateDexPath(dex_path); !result.ok()) {
116 return CREATE_UTF_OR_RETURN(env, result.error().message()).release();
117 } else {
118 return nullptr;
119 }
120 }
121
122 extern "C" JNIEXPORT jstring JNICALL
Java_com_android_server_art_ArtJni_validateClassLoaderContextNative(JNIEnv * env,jobject,jstring j_dex_path,jstring j_class_loader_context)123 Java_com_android_server_art_ArtJni_validateClassLoaderContextNative(
124 JNIEnv* env, jobject, jstring j_dex_path, jstring j_class_loader_context) {
125 ScopedUtfChars dex_path = GET_UTF_OR_RETURN(env, j_dex_path);
126 std::string class_loader_context(GET_UTF_OR_RETURN(env, j_class_loader_context));
127
128 if (Result<void> result = ValidateClassLoaderContext(dex_path, class_loader_context);
129 !result.ok()) {
130 return CREATE_UTF_OR_RETURN(env, result.error().message()).release();
131 } else {
132 return nullptr;
133 }
134 }
135
136 extern "C" JNIEXPORT jstring JNICALL
Java_com_android_server_art_ArtJni_getGarbageCollectorNative(JNIEnv * env,jobject)137 Java_com_android_server_art_ArtJni_getGarbageCollectorNative(JNIEnv* env, jobject) {
138 return CREATE_UTF_OR_RETURN(env, GetGarbageCollector()).release();
139 }
140
Java_com_android_server_art_ArtJni_setPropertyNative(JNIEnv * env,jobject,jstring j_key,jstring j_value)141 extern "C" JNIEXPORT void JNICALL Java_com_android_server_art_ArtJni_setPropertyNative(
142 JNIEnv* env, jobject, jstring j_key, jstring j_value) {
143 std::string key(GET_UTF_OR_RETURN_VOID(env, j_key));
144 std::string value(GET_UTF_OR_RETURN_VOID(env, j_value));
145 if (!SetProperty(key, value)) {
146 jniThrowExceptionFmt(env,
147 "java/lang/IllegalStateException",
148 "Failed to set property '%s' to '%s'",
149 key.c_str(),
150 value.c_str());
151 }
152 }
153
Java_com_android_server_art_ArtJni_ensureNoProcessInDirNative(JNIEnv * env,jobject,jstring j_dir,jint j_timeout_ms)154 extern "C" JNIEXPORT void JNICALL Java_com_android_server_art_ArtJni_ensureNoProcessInDirNative(
155 JNIEnv* env, jobject, jstring j_dir, jint j_timeout_ms) {
156 if (j_timeout_ms < 0) {
157 jniThrowExceptionFmt(
158 env, "java/lang/IllegalArgumentException", "Negative timeout '%d'", j_timeout_ms);
159 return;
160 }
161 std::string dir(GET_UTF_OR_RETURN_VOID(env, j_dir));
162 if (Result<void> result = tools::EnsureNoProcessInDir(dir, j_timeout_ms, /*try_kill=*/true);
163 !result.ok()) {
164 jniThrowException(env, "java/io/IOException", result.error().message().c_str());
165 }
166 }
167
168 } // namespace service
169 } // namespace art
170