int lttng_ust_safe_close_fd(int fd, int (*close_cb)(int));
int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream));
int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int));
+int lttng_ust_safe_close_range_fd(unsigned int first, unsigned int last, int flags,
+ int (*close_range_cb)(unsigned int, unsigned int, int));
#endif /* _LTTNG_UST_FD_H */
end:
return ret;
}
+
+/*
+ * Implement helper for close_range() override.
+ */
+int lttng_ust_safe_close_range_fd(unsigned int first, unsigned int last, int flags,
+ int (*close_range_cb)(unsigned int first, unsigned int last, int flags))
+{
+ int ret = 0, i;
+
+ lttng_ust_fixup_fd_tracker_tls();
+
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
+
+ if (first > last || last > INT_MAX) {
+ ret = -1;
+ errno = EINVAL;
+ goto end;
+ }
+ /*
+ * If called from lttng-ust, we directly call close_range
+ * without validating whether the FD is part of the tracked set.
+ */
+ if (URCU_TLS(ust_fd_mutex_nest)) {
+ if (close_range_cb(first, last, flags) < 0) {
+ ret = -1;
+ goto end;
+ }
+ } else {
+ int last_check = last;
+
+ if (last > lttng_ust_max_fd)
+ last_check = lttng_ust_max_fd;
+ lttng_ust_lock_fd_tracker();
+ for (i = first; i <= last_check; i++) {
+ if (IS_FD_VALID(i) && IS_FD_SET(i, lttng_fd_set))
+ continue;
+ if (close_range_cb(i, i, flags) < 0) {
+ ret = -1;
+ /* propagate errno from close_range_cb. */
+ lttng_ust_unlock_fd_tracker();
+ goto end;
+ }
+ }
+ if (last > lttng_ust_max_fd) {
+ if (close_range_cb(lttng_ust_max_fd + 1, last, flags) < 0) {
+ ret = -1;
+ lttng_ust_unlock_fd_tracker();
+ goto end;
+ }
+ }
+ lttng_ust_unlock_fd_tracker();
+ }
+end:
+ return ret;
+}
static int (*__lttng_ust_fd_plibc_close)(int fd) = NULL;
static int (*__lttng_ust_fd_plibc_fclose)(FILE *stream) = NULL;
+static int (*__lttng_ust_fd_plibc_close_range)(unsigned int first,
+ unsigned int last, int flags) = NULL;
/*
* Use dlsym to find the original libc close() symbol and store it in
return __lttng_ust_fd_plibc_fclose;
}
+/*
+ * Use dlsym to find the original libc close_range() symbol and store it
+ * in __lttng_ust_fd_plibc_close_range. The close_range symbol only
+ * appears in glibc 2.34, so it is considered optional.
+ */
+static
+void *_lttng_ust_fd_init_plibc_close_range(void)
+{
+ if (__lttng_ust_fd_plibc_close_range == NULL) {
+ __lttng_ust_fd_plibc_close_range = dlsym(RTLD_NEXT, "close_range");
+
+ if (__lttng_ust_fd_plibc_close_range == NULL)
+ __lttng_ust_fd_plibc_close_range = (void *) LTTNG_UST_DLSYM_FAILED_PTR;
+ }
+
+ return __lttng_ust_fd_plibc_close_range;
+}
+
static
void _lttng_ust_fd_ctor(void)
__attribute__((constructor));
*/
(void) _lttng_ust_fd_init_plibc_close();
(void) _lttng_ust_fd_init_plibc_fclose();
+ (void) _lttng_ust_fd_init_plibc_close_range();
}
/*
__lttng_ust_fd_plibc_fclose);
}
+/*
+ * Override the libc close_range() symbol with our own, allowing
+ * applications to close arbitrary file descriptors. If the fd is owned
+ * by lttng-ust, return -1, errno=EBADF instead of closing it.
+ *
+ * If dlsym failed to find the original libc close_range() symbol,
+ * return -1, errno=ENOSYS.
+ *
+ * There is a short window before the library constructor has executed where
+ * this wrapper could call dlsym() and thus not be async-signal-safe.
+ */
+int close_range(unsigned int first, unsigned int last, int flags)
+{
+ /*
+ * We can't retry dlsym here since close is async-signal-safe.
+ */
+ if (_lttng_ust_fd_init_plibc_close_range() == (void *) LTTNG_UST_DLSYM_FAILED_PTR) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return lttng_ust_safe_close_range_fd(first, last, flags, __lttng_ust_fd_plibc_close_range);
+}
+
#if defined(__sun__) || defined(__FreeBSD__)
/* Solaris and FreeBSD. */
void closefrom(int lowfd)