1 /*
2 * Copyright (C) 2024 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 /*
18 * This file is compiled into a single SO file, which we load at the very first.
19 * We can do process-wide initialization here.
20 * Please be aware that all symbols defined in this SO file will be reloaded
21 * as `RTLD_GLOBAL`, so make sure all functions are static except those we EXPLICITLY
22 * want to expose and override globally.
23 */
24
25 #include <dlfcn.h>
26 #include <fcntl.h>
27
28 #include <set>
29
30 #include "jni_helper.h"
31
32 // Implement a rudimentary system properties data store
33
34 #define PROP_VALUE_MAX 92
35
36 namespace {
37
38 struct prop_info {
39 std::string key;
40 mutable std::string value;
41 mutable uint32_t serial;
42
prop_info__anon2e97a4bd0111::prop_info43 prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
44 };
45
46 struct prop_info_cmp {
47 using is_transparent = void;
operator ()__anon2e97a4bd0111::prop_info_cmp48 bool operator()(const prop_info& lhs, const prop_info& rhs) {
49 return lhs.key < rhs.key;
50 }
operator ()__anon2e97a4bd0111::prop_info_cmp51 bool operator()(std::string_view lhs, const prop_info& rhs) {
52 return lhs < rhs.key;
53 }
operator ()__anon2e97a4bd0111::prop_info_cmp54 bool operator()(const prop_info& lhs, std::string_view rhs) {
55 return lhs.key < rhs;
56 }
57 };
58
59 } // namespace
60
61 static auto& g_properties_lock = *new std::mutex;
62 static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
63
property_set(const char * key,const char * value)64 static bool property_set(const char* key, const char* value) {
65 if (key == nullptr || *key == '\0') return false;
66 if (value == nullptr) value = "";
67 bool read_only = !strncmp(key, "ro.", 3);
68 if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
69
70 std::lock_guard lock(g_properties_lock);
71 auto [it, success] = g_properties.emplace(key, value);
72 if (read_only) return success;
73 if (!success) {
74 it->value = value;
75 ++it->serial;
76 }
77 return true;
78 }
79
80 template <typename Func>
property_get(const char * key,Func callback)81 static void property_get(const char* key, Func callback) {
82 std::lock_guard lock(g_properties_lock);
83 auto it = g_properties.find(key);
84 if (it != g_properties.end()) {
85 callback(*it);
86 }
87 }
88
89 // Redefine the __system_property_XXX functions here so we can perform
90 // logging and access checks for all sysprops in native code.
91
92 static void check_system_property_access(const char* key, bool write);
93
94 extern "C" {
95
__system_property_set(const char * key,const char * value)96 int __system_property_set(const char* key, const char* value) {
97 check_system_property_access(key, true);
98 return property_set(key, value) ? 0 : -1;
99 }
100
__system_property_get(const char * key,char * value)101 int __system_property_get(const char* key, char* value) {
102 check_system_property_access(key, false);
103 *value = '\0';
104 property_get(key, [&](const prop_info& info) {
105 snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
106 });
107 return strlen(value);
108 }
109
__system_property_find(const char * key)110 const prop_info* __system_property_find(const char* key) {
111 check_system_property_access(key, false);
112 const prop_info* pi = nullptr;
113 property_get(key, [&](const prop_info& info) { pi = &info; });
114 return pi;
115 }
116
__system_property_read_callback(const prop_info * pi,void (* callback)(void *,const char *,const char *,uint32_t),void * cookie)117 void __system_property_read_callback(const prop_info* pi,
118 void (*callback)(void*, const char*, const char*, uint32_t),
119 void* cookie) {
120 std::lock_guard lock(g_properties_lock);
121 callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
122 }
123
124 } // extern "C"
125
126 // ---- JNI ----
127
128 static JavaVM* gVM = nullptr;
129 static jclass gRunnerState = nullptr;
130 static jmethodID gCheckSystemPropertyAccess;
131
reloadNativeLibrary(JNIEnv * env,jclass,jstring javaPath)132 static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
133 ScopedUtfChars path(env, javaPath);
134 // Force reload ourselves as global
135 dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
136 }
137
138 // Call back into Java code to check property access
check_system_property_access(const char * key,bool write)139 static void check_system_property_access(const char* key, bool write) {
140 if (gVM != nullptr && gRunnerState != nullptr) {
141 JNIEnv* env;
142 if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
143 ALOGV("%s access to system property '%s'", write ? "Write" : "Read", key);
144 env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
145 env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
146 return;
147 }
148 }
149 // Not on JVM thread, abort
150 LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
151 }
152
getSystemProperty(JNIEnv * env,jclass,jstring javaKey)153 static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
154 ScopedUtfChars key(env, javaKey);
155 jstring value = nullptr;
156 property_get(key.c_str(),
157 [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
158 return value;
159 }
160
setSystemProperty(JNIEnv * env,jclass,jstring javaKey,jstring javaValue)161 static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
162 ScopedUtfChars key(env, javaKey);
163 ScopedUtfChars value(env, javaValue);
164 return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
165 }
166
removeSystemProperty(JNIEnv * env,jclass,jstring javaKey)167 static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
168 std::lock_guard lock(g_properties_lock);
169
170 if (javaKey == nullptr) {
171 g_properties.clear();
172 return JNI_TRUE;
173 } else {
174 ScopedUtfChars key(env, javaKey);
175 auto it = g_properties.find(key);
176 if (it != g_properties.end()) {
177 g_properties.erase(it);
178 return JNI_TRUE;
179 } else {
180 return JNI_FALSE;
181 }
182 }
183 }
184
maybeRedirectLog()185 static void maybeRedirectLog() {
186 auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
187 if (ravenwoodLogOut == NULL) {
188 return;
189 }
190 ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
191
192 // Redirect stdin / stdout to /dev/tty.
193 int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND);
194 if (ttyFd == -1) {
195 ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
196 strerror(errno));
197 return;
198 }
199 dup2(ttyFd, 1);
200 dup2(ttyFd, 2);
201 }
202
203 static const JNINativeMethod sMethods[] = {
204 {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
205 {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
206 {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
207 {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
208 };
209
JNI_OnLoad(JavaVM * vm,void *)210 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
211 ALOGV("%s: JNI_OnLoad", __FILE__);
212
213 maybeRedirectLog();
214
215 JNIEnv* env = GetJNIEnvOrDie(vm);
216 gVM = vm;
217
218 // Fetch several references for future use
219 gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
220 gCheckSystemPropertyAccess =
221 GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
222 "(Ljava/lang/String;Z)V");
223
224 // Expose raw property methods as JNI methods
225 jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
226 if (res < 0) return -1;
227
228 return JNI_VERSION_1_4;
229 }
230