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