From: Mathieu Desnoyers Date: Thu, 18 Apr 2024 15:25:55 +0000 (-0400) Subject: Add close_range wrapper to liblttng-ust-fd.so X-Git-Url: http://git.liburcu.org/?p=lttng-ust.git;a=commitdiff_plain;h=4b01076fea0f635af6af6762a8edce1be03e5d39 Add close_range wrapper to liblttng-ust-fd.so glibc 2.34 implements close_range(2), which is used by the ssh client (amongst others). This needs to be overridden to make sure ssh does not close lttng-ust file descriptors. Signed-off-by: Mathieu Desnoyers Change-Id: Ic4e0046499e1f010395aec71a48316b9d1e9bf3f --- diff --git a/src/common/ust-fd.h b/src/common/ust-fd.h index 4e6c4a5d..85038231 100644 --- a/src/common/ust-fd.h +++ b/src/common/ust-fd.h @@ -25,5 +25,7 @@ void lttng_ust_unlock_fd_tracker(void); 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 */ diff --git a/src/lib/lttng-ust-common/fd-tracker.c b/src/lib/lttng-ust-common/fd-tracker.c index f5e6ed21..a39869ef 100644 --- a/src/lib/lttng-ust-common/fd-tracker.c +++ b/src/lib/lttng-ust-common/fd-tracker.c @@ -466,3 +466,62 @@ int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int fd)) 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_fd_tracker_alloc_tls(); + + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_fd_tracker_init(); + + 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; +} diff --git a/src/lib/lttng-ust-fd/lttng-ust-fd.c b/src/lib/lttng-ust-fd/lttng-ust-fd.c index d2ebcfbc..0360b6f2 100644 --- a/src/lib/lttng-ust-fd/lttng-ust-fd.c +++ b/src/lib/lttng-ust-fd/lttng-ust-fd.c @@ -21,6 +21,8 @@ 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 @@ -60,6 +62,24 @@ void *_lttng_ust_fd_init_plibc_fclose(void) 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)); @@ -75,6 +95,7 @@ void _lttng_ust_fd_ctor(void) */ (void) _lttng_ust_fd_init_plibc_close(); (void) _lttng_ust_fd_init_plibc_fclose(); + (void) _lttng_ust_fd_init_plibc_close_range(); } /* @@ -127,6 +148,30 @@ int fclose(FILE *stream) __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)