1 /*
2  * Copyright 2022 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 #include <jni.h>
17 #include <inttypes.h>
18 #include <android/fdsan.h>
19 #include <android/sync.h>
20 #include <android/log.h>
21 #include <cstdint>
22 #include <poll.h>
23 #include <unistd.h>
24 #include <sys/syscall.h>
25 #include <android/file_descriptor_jni.h>
26 #include <errno.h>
27 #include <dlfcn.h>
28 #include <mutex>
29 
30 static constexpr int64_t SIGNAL_TIME_INVALID = -1;
31 static constexpr int64_t SIGNAL_TIME_PENDING = INT64_MAX;
32 
33 #define SYNC_FENCE "SYNC_FENCE"
34 #define ALOGE(msg, ...) \
35     __android_log_print(ANDROID_LOG_ERROR, SYNC_FENCE, (msg), __VA_ARGS__)
36 #define ALOGW(msg, ...) \
37     __android_log_print(ANDROID_LOG_ERROR, SYNC_FENCE, (msg))
38 
SyncFence_nClose(JNIEnv * env,jobject,jint fd)39 void SyncFence_nClose(JNIEnv *env, jobject, jint fd) {
40     close(fd);
41 }
42 
43 /**
44  * Flag to ensure that we only attempt to load libsync.so once
45  */
46 static std::once_flag load_sync_lib_flag;
47 
48 static std::once_flag load_fdsan_lib_flag;
49 
50 /**
51  * Function pointer to the dynamically linked sync_file_info method
52  */
53 static decltype(&sync_file_info) sync_file_info_ptr = nullptr;
54 
55 /**
56  * Function pointer to the dynamically linked sync_file_info_free method
57  */
58 static decltype(&sync_file_info_free) sync_file_info_free_ptr = nullptr;
59 
60 /**
61  * Function pointer to resolve the corresponding owner tag from a file descriptor
62  */
63 static uint64_t (*android_fdsan_get_owner_tag_ptr)(int) = nullptr;
64 
65 /**
66  * Function pointer to close the corresponding file descriptor with the given tag
67  */
68 static int (*android_fdsan_close_with_tag_ptr)(int, uint64_t) = nullptr;
69 
70 /**
71  * Helper method to attempt to load libsync.so. It only attempts to load the library once and the
72  * result is cached. On successful load, this method resolves both sync_file_info and
73  * sync_file_info_free methods as well.
74  */
load_libsync()75 static void load_libsync() {
76     std::call_once(load_sync_lib_flag, []() {
77         void* handle = dlopen("libsync.so", RTLD_NOW);
78         if (handle) {
79             sync_file_info_ptr =
80                     reinterpret_cast<decltype(&sync_file_info)>(
81                             dlsym(handle, "sync_file_info"));
82             if (!sync_file_info_ptr) {
83                 ALOGW("Unable to resolve sync_file_info from dlsym");
84             }
85             sync_file_info_free_ptr =
86                     reinterpret_cast<decltype(&sync_file_info_free)>(
87                             dlsym(handle, "sync_file_info_free"));
88             if (!sync_file_info_free_ptr) {
89                 ALOGW("Unable to resolve sync_file_info_free from dlsym");
90             }
91         } else  {
92             ALOGW("Unable to load libsync.so");
93             sync_file_info_ptr = nullptr;
94             sync_file_info_free_ptr = nullptr;
95         }
96     });
97 }
98 
load_lib_fdsan()99 static void load_lib_fdsan() {
100     std::call_once(load_fdsan_lib_flag, []() {
101         void* handle = dlopen("libc.so", RTLD_NOW);
102         if (handle) {
103             android_fdsan_get_owner_tag_ptr =
104                     reinterpret_cast<uint64_t (*)(int)>(
105                             dlsym(handle, "android_fdsan_get_owner_tag"));
106             if (!android_fdsan_get_owner_tag_ptr) {
107                 ALOGW("Unable to resolve android_fdsan_get_owner_tag");
108             }
109 
110             android_fdsan_close_with_tag_ptr =
111                     reinterpret_cast<int(*)(int, uint64_t)>(
112                             dlsym(handle, "android_fdsan_close_with_tag"));
113             if (!android_fdsan_close_with_tag_ptr) {
114                 ALOGW("Unable to resolve android_fdsan_close_with_tag");
115             }
116 
117         } else {
118             ALOGW("Unable to load libc.so");
119             android_fdsan_close_with_tag_ptr = nullptr;
120             android_fdsan_get_owner_tag_ptr = nullptr;
121         }
122     });
123 }
124 
125 /**
126  * Obtains a sync_file_info object from the provided file descriptor.
127  * Internally this attempts to dynamcially resolve the sync_file_info method from libsync.so
128  * @param fd File descriptor to resolve the corresponding sync_file_info from
129  * @return sync_file_info or nullptr if the method cannot be resolved or there is no sync_file_info
130  * associated with the provided file descriptor
131  */
get_sync_file_info(int fd)132 static struct sync_file_info* get_sync_file_info(int fd) {
133     load_libsync();
134     if (sync_file_info_ptr) {
135         return sync_file_info_ptr(fd);
136     } else {
137         return nullptr;
138     }
139 }
140 
141 /**
142  * Helper method that resolves and invokes sync_file_info_free from libsync.so.
143  * This attempts to dynamically resolve the method once and caches the function pointer.
144  * If the release method cannot be invoked this method call is a no-op.
145  * @param info sync_file_info instance to release
146  */
release_sync_file_info(struct sync_file_info * info)147 static void release_sync_file_info(struct sync_file_info* info) {
148     load_libsync();
149     if (sync_file_info_free_ptr) {
150         sync_file_info_free_ptr(info);
151     }
152 }
153 
SyncFenceBindings_nForceClose(JNIEnv * env,jclass,jint fd)154 void SyncFenceBindings_nForceClose(JNIEnv *env, jclass, jint fd) {
155     load_lib_fdsan();
156     if (android_fdsan_get_owner_tag_ptr && android_fdsan_close_with_tag_ptr) {
157         uint64_t tag = android_fdsan_get_owner_tag_ptr(static_cast<int>(fd));
158         uint64_t type = tag >> 56;
159         // From fdsan.h docs on android_fdsan_owner_type, native file descriptors have their upper
160         // most bits as all zeros. So limit the closure of file descriptors to only this type in
161         // order to avoid potential double closure instances.
162         if (type == android_fdsan_owner_type::ANDROID_FDSAN_OWNER_TYPE_GENERIC_00) {
163             android_fdsan_close_with_tag_ptr(fd, tag);
164         }
165     }
166 }
167 
SyncFenceBindings_nGetSignalTime(JNIEnv * env,jclass,jint fd)168 jlong SyncFenceBindings_nGetSignalTime(JNIEnv *env, jclass, jint fd) {
169     // Implementation sampled from Fence::getSignalTime in the framework
170     if (fd == -1) {
171         return SIGNAL_TIME_INVALID;
172     }
173 
174     struct sync_file_info* finfo = get_sync_file_info(fd);
175     if (finfo == nullptr) {
176         return SIGNAL_TIME_INVALID;
177     }
178 
179     if (finfo->status != 1) {
180         const auto status = finfo->status;
181         if (status < 0) {
182             ALOGE("nGetSignalTime: sync_file_info contains an error: <%d> for fd: <%d>", status,
183                   fd);
184         }
185         release_sync_file_info(finfo);
186         return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING;
187     }
188 
189     uint64_t timestamp = 0;
190     struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
191     for (size_t i = 0; i < finfo->num_fences; i++) {
192         if (pinfo[i].timestamp_ns > timestamp) {
193             timestamp = pinfo[i].timestamp_ns;
194         }
195     }
196 
197     release_sync_file_info(finfo);
198     return static_cast<int64_t>(timestamp);
199 }
200 
201 // Implementation of sync_wait obtained from libsync/sync.c in the framework
sync_wait(int fd,int timeout)202 static int sync_wait(int fd, int timeout)
203 {
204     struct pollfd fds{};
205     int ret;
206 
207     if (fd < 0) {
208         errno = EINVAL;
209         return -1;
210     }
211 
212     fds.fd = fd;
213     fds.events = POLLIN;
214 
215     do {
216         ret = poll(&fds, 1, timeout);
217         if (ret > 0) {
218             if (fds.revents & (POLLERR | POLLNVAL)) {
219                 errno = EINVAL;
220                 return -1;
221             }
222             return 0;
223         } else if (ret == 0) {
224             errno = ETIME;
225             return -1;
226         }
227     } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
228 
229     return ret;
230 }
231 
SyncFence_nWait(JNIEnv * env,jobject,jint fd,jint timeout_millis)232 jboolean SyncFence_nWait(JNIEnv *env, jobject, jint fd, jint timeout_millis) {
233     if (fd == -1) {
234         return static_cast<jboolean>(true);
235     }
236 
237     // SyncFence#wait takes a timeout as a long in nanoseconds, however, the poll
238     // API appears to consume an int. Also the documentation in Fence.cpp seems to indicate
239     // that the timeout is consumed as an int in milliseconds
240     int err = sync_wait(fd, timeout_millis);
241     return static_cast<jboolean>(err == 0);
242 }
243 
SyncFenceBindings_nResolveSyncFileInfo(JNIEnv * env,jclass)244 jboolean SyncFenceBindings_nResolveSyncFileInfo(JNIEnv *env, jclass) {
245     load_libsync();
246     return sync_file_info_ptr != nullptr;
247 }
248 
SyncFenceBindings_nResolveSyncFileInfoFree(JNIEnv * env,jclass)249 jboolean SyncFenceBindings_nResolveSyncFileInfoFree(JNIEnv *env, jclass) {
250     load_libsync();
251     return sync_file_info_free_ptr != nullptr;
252 }
253 
SyncFence_nDup(JNIEnv * env,jobject,jint fd)254 jint SyncFence_nDup(JNIEnv *env, jobject, jint fd) {
255     return static_cast<jint>(dup(static_cast<int>(fd)));
256 }
257 
258 static const JNINativeMethod SYNC_FENCE_METHOD_TABLE[] = {
259         {
260             "nClose",
261             "(I)V",
262             (void*)SyncFence_nClose
263         },
264         {
265             "nWait",
266             "(II)Z",
267             (void*)SyncFence_nWait
268         },
269         {
270             "nDup",
271             "(I)I",
272             (void*)SyncFence_nDup
273         }
274 };
275 
276 static const JNINativeMethod SYNC_FENCE_BINDINGS_METHOD_TABLE[] = {
277         {
278             "nResolveSyncFileInfo",
279             "()Z",
280             (void*)SyncFenceBindings_nResolveSyncFileInfo
281         },
282         {
283             "nResolveSyncFileInfoFree",
284             "()Z",
285             (void*)SyncFenceBindings_nResolveSyncFileInfoFree
286         },
287         {
288             "nGetSignalTime",
289             "(I)J",
290             (void*)SyncFenceBindings_nGetSignalTime
291         },
292         {
293             "nForceClose",
294             "(I)V",
295                 (void *) SyncFenceBindings_nForceClose
296         }
297 };
298 
loadSyncFenceMethods(JNIEnv * env)299 jint loadSyncFenceMethods(JNIEnv* env) {
300     jclass syncFenceClass = env->FindClass("androidx/hardware/SyncFenceV19");
301     if (syncFenceClass == nullptr) {
302         return JNI_ERR;
303     }
304 
305     if (env->RegisterNatives(syncFenceClass, SYNC_FENCE_METHOD_TABLE,
306                              sizeof(SYNC_FENCE_METHOD_TABLE) / sizeof(JNINativeMethod)) != JNI_OK) {
307         return JNI_ERR;
308     }
309 
310     jclass syncFenceTestBindings = env->FindClass("androidx/hardware/SyncFenceBindings");
311     if (syncFenceTestBindings == nullptr) {
312         return JNI_ERR;
313     }
314 
315     if (env->RegisterNatives(syncFenceTestBindings, SYNC_FENCE_BINDINGS_METHOD_TABLE,
316                     sizeof(SYNC_FENCE_BINDINGS_METHOD_TABLE) / sizeof(JNINativeMethod)) != JNI_OK) {
317         return JNI_ERR;
318     }
319 
320     return JNI_OK;
321 }
322