From: Michael Jeanson Date: Mon, 12 Apr 2021 21:12:55 +0000 (-0400) Subject: Move fd-tracker to liblttng-ust-common X-Git-Tag: v2.13.0-rc1~131 X-Git-Url: http://git.lttng.org/?a=commitdiff_plain;h=df09bde567961bdf9cdda26f9737e3bbffd5cea9;p=lttng-ust.git Move fd-tracker to liblttng-ust-common The fd-tracker provides ABI symbols to liblttng-ust-ctl which shouldn't link directy on liblttng-ust, move it to liblttng-ust-common to accomodate this. This is in line with the goal to move the shared state across our different libraries to ust-common. Change-Id: I69de6a9e6eaec9599f22e47cb0292ef793f6115d Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- diff --git a/src/common/ust-fd.h b/src/common/ust-fd.h index ddebaa91..9d14583c 100644 --- a/src/common/ust-fd.h +++ b/src/common/ust-fd.h @@ -11,6 +11,8 @@ * The fd tracker feature is part of the ABI and used by liblttng-ust-fd. * However, some test code and documentation needs to be written before it is * exposed to users with a public header. + * + * These symbols are provided by 'liblttng-ust-common'. */ #include @@ -25,4 +27,6 @@ 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)); +void lttng_ust_fixup_fd_tracker_tls(void); + #endif /* _LTTNG_UST_FD_H */ diff --git a/src/lib/lttng-ust-ctl/Makefile.am b/src/lib/lttng-ust-ctl/Makefile.am index 43a88566..35999217 100644 --- a/src/lib/lttng-ust-ctl/Makefile.am +++ b/src/lib/lttng-ust-ctl/Makefile.am @@ -10,6 +10,7 @@ liblttng_ust_ctl_la_LDFLAGS = \ -version-info $(LTTNG_UST_CTL_LIBRARY_VERSION) liblttng_ust_ctl_la_LIBADD = \ + $(top_builddir)/src/liblttng-ust/liblttng-ust-common.la \ $(top_builddir)/src/liblttng-ust-comm/liblttng-ust-comm.la \ $(top_builddir)/src/liblttng-ust/liblttng-ust-support.la \ $(top_builddir)/src/common/libcommon.la \ diff --git a/src/liblttng-ust-comm/Makefile.am b/src/liblttng-ust-comm/Makefile.am index 5aa5938e..cad9c4ef 100644 --- a/src/liblttng-ust-comm/Makefile.am +++ b/src/liblttng-ust-comm/Makefile.am @@ -2,4 +2,4 @@ noinst_LTLIBRARIES = liblttng-ust-comm.la -liblttng_ust_comm_la_SOURCES = lttng-ust-comm.c lttng-ust-fd-tracker.c +liblttng_ust_comm_la_SOURCES = lttng-ust-comm.c diff --git a/src/liblttng-ust-comm/lttng-ust-fd-tracker.c b/src/liblttng-ust-comm/lttng-ust-fd-tracker.c deleted file mode 100644 index 3879a90d..00000000 --- a/src/liblttng-ust-comm/lttng-ust-fd-tracker.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-only - * - * Copyright (C) 2016 Aravind HT - * Copyright (C) 2016 Mathieu Desnoyers - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/ust-fd.h" -#include "common/macros.h" -#include -#include "common/logging.h" - -#include "../liblttng-ust/compat.h" -#include "../liblttng-ust/lttng-tracer-core.h" - -/* Operations on the fd set. */ -#define IS_FD_VALID(fd) ((fd) >= 0 && (fd) < lttng_ust_max_fd) -#define GET_FD_SET_FOR_FD(fd, fd_sets) (&((fd_sets)[(fd) / FD_SETSIZE])) -#define CALC_INDEX_TO_SET(fd) ((fd) % FD_SETSIZE) -#define IS_FD_STD(fd) (IS_FD_VALID(fd) && (fd) <= STDERR_FILENO) - -/* Check fd validity before calling these. */ -#define ADD_FD_TO_SET(fd, fd_sets) \ - FD_SET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) -#define IS_FD_SET(fd, fd_sets) \ - FD_ISSET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) -#define DEL_FD_FROM_SET(fd, fd_sets) \ - FD_CLR(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) - -/* - * Protect the lttng_fd_set. Nests within the ust_lock, and therefore - * within the libc dl lock. Therefore, we need to fixup the TLS before - * nesting into this lock. - * - * The ust_safe_guard_fd_mutex nests within the ust_mutex. This mutex - * is also held across fork. - */ -static pthread_mutex_t ust_safe_guard_fd_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* - * Cancel state when grabbing the ust_safe_guard_fd_mutex. Saved when - * locking, restored on unlock. Protected by ust_safe_guard_fd_mutex. - */ -static int ust_safe_guard_saved_cancelstate; - -/* - * Track whether we are within lttng-ust or application, for close - * system call override by LD_PRELOAD library. This also tracks whether - * we are invoking close() from a signal handler nested on an - * application thread. - */ -static DEFINE_URCU_TLS(int, ust_fd_mutex_nest); - -/* fd_set used to book keep fd being used by lttng-ust. */ -static fd_set *lttng_fd_set; -static int lttng_ust_max_fd; -static int num_fd_sets; -static int init_done; - -/* - * Force a read (imply TLS fixup for dlopen) of TLS variables. - */ -void lttng_ust_fixup_fd_tracker_tls(void) -{ - asm volatile ("" : : "m" (URCU_TLS(ust_fd_mutex_nest))); -} - -/* - * Allocate the fd set array based on the hard limit set for this - * process. This will be called during the constructor execution - * and will also be called in the child after fork via lttng_ust_init. - */ -void lttng_ust_init_fd_tracker(void) -{ - struct rlimit rlim; - int i; - - if (CMM_LOAD_SHARED(init_done)) - return; - - memset(&rlim, 0, sizeof(rlim)); - /* Get the current possible max number of fd for this process. */ - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) - abort(); - /* - * FD set array size determined using the hard limit. Even if - * the process wishes to increase its limit using setrlimit, it - * can only do so with the softlimit which will be less than the - * hard limit. - */ - lttng_ust_max_fd = rlim.rlim_max; - num_fd_sets = lttng_ust_max_fd / FD_SETSIZE; - if (lttng_ust_max_fd % FD_SETSIZE) - ++num_fd_sets; - if (lttng_fd_set != NULL) { - free(lttng_fd_set); - lttng_fd_set = NULL; - } - lttng_fd_set = malloc(num_fd_sets * (sizeof(fd_set))); - if (!lttng_fd_set) - abort(); - for (i = 0; i < num_fd_sets; i++) - FD_ZERO((<tng_fd_set[i])); - CMM_STORE_SHARED(init_done, 1); -} - -void lttng_ust_lock_fd_tracker(void) -{ - sigset_t sig_all_blocked, orig_mask; - int ret, oldstate; - - ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - if (ret) { - ERR("pthread_setcancelstate: %s", strerror(ret)); - } - sigfillset(&sig_all_blocked); - ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); - if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); - } - if (!URCU_TLS(ust_fd_mutex_nest)++) { - /* - * Ensure the compiler don't move the store after the close() - * call in case close() would be marked as leaf. - */ - cmm_barrier(); - pthread_mutex_lock(&ust_safe_guard_fd_mutex); - ust_safe_guard_saved_cancelstate = oldstate; - } - ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); - if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); - } -} - -void lttng_ust_unlock_fd_tracker(void) -{ - sigset_t sig_all_blocked, orig_mask; - int ret, newstate, oldstate; - bool restore_cancel = false; - - sigfillset(&sig_all_blocked); - ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); - if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); - } - /* - * Ensure the compiler don't move the store before the close() - * call, in case close() would be marked as leaf. - */ - cmm_barrier(); - if (!--URCU_TLS(ust_fd_mutex_nest)) { - newstate = ust_safe_guard_saved_cancelstate; - restore_cancel = true; - pthread_mutex_unlock(&ust_safe_guard_fd_mutex); - } - ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); - if (ret) { - ERR("pthread_sigmask: %s", strerror(ret)); - } - if (restore_cancel) { - ret = pthread_setcancelstate(newstate, &oldstate); - if (ret) { - ERR("pthread_setcancelstate: %s", strerror(ret)); - } - } -} - -static int dup_std_fd(int fd) -{ - int ret, i; - int fd_to_close[STDERR_FILENO + 1]; - int fd_to_close_count = 0; - int dup_cmd = F_DUPFD; /* Default command */ - int fd_valid = -1; - - if (!(IS_FD_STD(fd))) { - /* Should not be here */ - ret = -1; - goto error; - } - - /* Check for FD_CLOEXEC flag */ - ret = fcntl(fd, F_GETFD); - if (ret < 0) { - PERROR("fcntl on f_getfd"); - ret = -1; - goto error; - } - - if (ret & FD_CLOEXEC) { - dup_cmd = F_DUPFD_CLOEXEC; - } - - /* Perform dup */ - for (i = 0; i < STDERR_FILENO + 1; i++) { - ret = fcntl(fd, dup_cmd, 0); - if (ret < 0) { - PERROR("fcntl dup fd"); - goto error; - } - - if (!(IS_FD_STD(ret))) { - /* fd is outside of STD range, use it. */ - fd_valid = ret; - /* Close fd received as argument. */ - fd_to_close[i] = fd; - fd_to_close_count++; - break; - } - - fd_to_close[i] = ret; - fd_to_close_count++; - } - - /* Close intermediary fds */ - for (i = 0; i < fd_to_close_count; i++) { - ret = close(fd_to_close[i]); - if (ret) { - PERROR("close on temporary fd: %d.", fd_to_close[i]); - /* - * Not using an abort here would yield a complicated - * error handling for the caller. If a failure occurs - * here, the system is already in a bad state. - */ - abort(); - } - } - - ret = fd_valid; -error: - return ret; -} - -/* - * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd. - * Has strict checking of fd validity. - * - * If fd <= 2, dup the fd until fd > 2. This enables us to bypass - * problems that can be encountered if UST uses stdin, stdout, stderr - * fds for internal use (daemon etc.). This can happen if the - * application closes either of those file descriptors. Intermediary fds - * are closed as needed. - * - * Return -1 on error. - * - */ -int lttng_ust_add_fd_to_tracker(int fd) -{ - int ret; - /* - * Ensure the tracker is initialized when called from - * constructors. - */ - lttng_ust_init_fd_tracker(); - assert(URCU_TLS(ust_fd_mutex_nest)); - - if (IS_FD_STD(fd)) { - ret = dup_std_fd(fd); - if (ret < 0) { - goto error; - } - fd = ret; - } - - /* Trying to add an fd which we can not accommodate. */ - assert(IS_FD_VALID(fd)); - /* Setting an fd thats already set. */ - assert(!IS_FD_SET(fd, lttng_fd_set)); - - ADD_FD_TO_SET(fd, lttng_fd_set); - return fd; -error: - return ret; -} - -/* - * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd. - * Has strict checking for fd validity. - */ -void lttng_ust_delete_fd_from_tracker(int fd) -{ - /* - * Ensure the tracker is initialized when called from - * constructors. - */ - lttng_ust_init_fd_tracker(); - - assert(URCU_TLS(ust_fd_mutex_nest)); - /* Not a valid fd. */ - assert(IS_FD_VALID(fd)); - /* Deleting an fd which was not set. */ - assert(IS_FD_SET(fd, lttng_fd_set)); - - DEL_FD_FROM_SET(fd, lttng_fd_set); -} - -/* - * Interface allowing applications to close arbitrary file descriptors. - * We check if it is owned by lttng-ust, and return -1, errno=EBADF - * instead of closing it if it is the case. - */ -int lttng_ust_safe_close_fd(int fd, int (*close_cb)(int fd)) -{ - int ret = 0; - - lttng_ust_fixup_fd_tracker_tls(); - - /* - * Ensure the tracker is initialized when called from - * constructors. - */ - lttng_ust_init_fd_tracker(); - - /* - * If called from lttng-ust, we directly call close without - * validating whether the FD is part of the tracked set. - */ - if (URCU_TLS(ust_fd_mutex_nest)) - return close_cb(fd); - - lttng_ust_lock_fd_tracker(); - if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) { - ret = -1; - errno = EBADF; - } else { - ret = close_cb(fd); - } - lttng_ust_unlock_fd_tracker(); - - return ret; -} - -/* - * Interface allowing applications to close arbitrary streams. - * We check if it is owned by lttng-ust, and return -1, errno=EBADF - * instead of closing it if it is the case. - */ -int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream)) -{ - int ret = 0, fd; - - lttng_ust_fixup_fd_tracker_tls(); - - /* - * Ensure the tracker is initialized when called from - * constructors. - */ - lttng_ust_init_fd_tracker(); - - /* - * If called from lttng-ust, we directly call fclose without - * validating whether the FD is part of the tracked set. - */ - if (URCU_TLS(ust_fd_mutex_nest)) - return fclose_cb(stream); - - fd = fileno(stream); - - lttng_ust_lock_fd_tracker(); - if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) { - ret = -1; - errno = EBADF; - } else { - ret = fclose_cb(stream); - } - lttng_ust_unlock_fd_tracker(); - - return ret; -} - -#ifdef __OpenBSD__ -static void set_close_success(int *p) -{ - *p = 1; -} -static int test_close_success(const int *p) -{ - return *p; -} -#else -static void set_close_success(int *p __attribute__((unused))) -{ -} -static int test_close_success(const int *p __attribute__((unused))) -{ - return 1; -} -#endif - -/* - * Implement helper for closefrom() override. - */ -int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int fd)) -{ - int ret = 0, close_success = 0, i; - - lttng_ust_fixup_fd_tracker_tls(); - - /* - * Ensure the tracker is initialized when called from - * constructors. - */ - lttng_ust_init_fd_tracker(); - - if (lowfd < 0) { - /* - * NetBSD return EBADF if fd is invalid. - */ - errno = EBADF; - ret = -1; - goto end; - } - /* - * If called from lttng-ust, we directly call close without - * validating whether the FD is part of the tracked set. - */ - if (URCU_TLS(ust_fd_mutex_nest)) { - for (i = lowfd; i < lttng_ust_max_fd; i++) { - if (close_cb(i) < 0) { - switch (errno) { - case EBADF: - continue; - case EINTR: - default: - ret = -1; - goto end; - } - } - set_close_success(&close_success); - } - } else { - lttng_ust_lock_fd_tracker(); - for (i = lowfd; i < lttng_ust_max_fd; i++) { - if (IS_FD_VALID(i) && IS_FD_SET(i, lttng_fd_set)) - continue; - if (close_cb(i) < 0) { - switch (errno) { - case EBADF: - continue; - case EINTR: - default: - ret = -1; - lttng_ust_unlock_fd_tracker(); - goto end; - } - } - set_close_success(&close_success); - } - lttng_ust_unlock_fd_tracker(); - } - if (!test_close_success(&close_success)) { - /* - * OpenBSD return EBADF if fd is greater than all open - * file descriptors. - */ - ret = -1; - errno = EBADF; - } -end: - return ret; -} diff --git a/src/liblttng-ust/Makefile.am b/src/liblttng-ust/Makefile.am index 78d82f0e..87380bc4 100644 --- a/src/liblttng-ust/Makefile.am +++ b/src/liblttng-ust/Makefile.am @@ -8,9 +8,14 @@ lib_LTLIBRARIES = liblttng-ust-common.la liblttng-ust-tracepoint.la liblttng-ust # ust-common liblttng_ust_common_la_SOURCES = \ + fd-tracker.c \ + ust-common.c \ lttng-ust-urcu.c \ lttng-ust-urcu-pointer.c +liblttng_ust_common_la_LIBADD = \ + $(top_builddir)/src/common/libcommon.la + liblttng_ust_common_la_LDFLAGS = -no-undefined -version-info $(LTTNG_UST_LIBRARY_VERSION) liblttng_ust_tracepoint_la_SOURCES = \ diff --git a/src/liblttng-ust/fd-tracker.c b/src/liblttng-ust/fd-tracker.c new file mode 100644 index 00000000..df14f0e0 --- /dev/null +++ b/src/liblttng-ust/fd-tracker.c @@ -0,0 +1,481 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2016 Aravind HT + * Copyright (C) 2016 Mathieu Desnoyers + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/ust-fd.h" +#include "common/macros.h" +#include +#include "common/logging.h" + +#include "liblttng-ust/compat.h" + +/* Operations on the fd set. */ +#define IS_FD_VALID(fd) ((fd) >= 0 && (fd) < lttng_ust_max_fd) +#define GET_FD_SET_FOR_FD(fd, fd_sets) (&((fd_sets)[(fd) / FD_SETSIZE])) +#define CALC_INDEX_TO_SET(fd) ((fd) % FD_SETSIZE) +#define IS_FD_STD(fd) (IS_FD_VALID(fd) && (fd) <= STDERR_FILENO) + +/* Check fd validity before calling these. */ +#define ADD_FD_TO_SET(fd, fd_sets) \ + FD_SET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) +#define IS_FD_SET(fd, fd_sets) \ + FD_ISSET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) +#define DEL_FD_FROM_SET(fd, fd_sets) \ + FD_CLR(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets)) + +/* + * Protect the lttng_fd_set. Nests within the ust_lock, and therefore + * within the libc dl lock. Therefore, we need to fixup the TLS before + * nesting into this lock. + * + * The ust_safe_guard_fd_mutex nests within the ust_mutex. This mutex + * is also held across fork. + */ +static pthread_mutex_t ust_safe_guard_fd_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* + * Cancel state when grabbing the ust_safe_guard_fd_mutex. Saved when + * locking, restored on unlock. Protected by ust_safe_guard_fd_mutex. + */ +static int ust_safe_guard_saved_cancelstate; + +/* + * Track whether we are within lttng-ust or application, for close + * system call override by LD_PRELOAD library. This also tracks whether + * we are invoking close() from a signal handler nested on an + * application thread. + */ +static DEFINE_URCU_TLS(int, ust_fd_mutex_nest); + +/* fd_set used to book keep fd being used by lttng-ust. */ +static fd_set *lttng_fd_set; +static int lttng_ust_max_fd; +static int num_fd_sets; +static int init_done; + +/* + * Force a read (imply TLS fixup for dlopen) of TLS variables. + */ +void lttng_ust_fixup_fd_tracker_tls(void) +{ + asm volatile ("" : : "m" (URCU_TLS(ust_fd_mutex_nest))); +} + +/* + * Allocate the fd set array based on the hard limit set for this + * process. This will be called during the constructor execution + * and will also be called in the child after fork via lttng_ust_init. + */ +void lttng_ust_init_fd_tracker(void) +{ + struct rlimit rlim; + int i; + + if (CMM_LOAD_SHARED(init_done)) + return; + + memset(&rlim, 0, sizeof(rlim)); + /* Get the current possible max number of fd for this process. */ + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) + abort(); + /* + * FD set array size determined using the hard limit. Even if + * the process wishes to increase its limit using setrlimit, it + * can only do so with the softlimit which will be less than the + * hard limit. + */ + lttng_ust_max_fd = rlim.rlim_max; + num_fd_sets = lttng_ust_max_fd / FD_SETSIZE; + if (lttng_ust_max_fd % FD_SETSIZE) + ++num_fd_sets; + if (lttng_fd_set != NULL) { + free(lttng_fd_set); + lttng_fd_set = NULL; + } + lttng_fd_set = malloc(num_fd_sets * (sizeof(fd_set))); + if (!lttng_fd_set) + abort(); + for (i = 0; i < num_fd_sets; i++) + FD_ZERO((<tng_fd_set[i])); + CMM_STORE_SHARED(init_done, 1); +} + +void lttng_ust_lock_fd_tracker(void) +{ + sigset_t sig_all_blocked, orig_mask; + int ret, oldstate; + + ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); + if (ret) { + ERR("pthread_setcancelstate: %s", strerror(ret)); + } + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (!URCU_TLS(ust_fd_mutex_nest)++) { + /* + * Ensure the compiler don't move the store after the close() + * call in case close() would be marked as leaf. + */ + cmm_barrier(); + pthread_mutex_lock(&ust_safe_guard_fd_mutex); + ust_safe_guard_saved_cancelstate = oldstate; + } + ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } +} + +void lttng_ust_unlock_fd_tracker(void) +{ + sigset_t sig_all_blocked, orig_mask; + int ret, newstate, oldstate; + bool restore_cancel = false; + + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + /* + * Ensure the compiler don't move the store before the close() + * call, in case close() would be marked as leaf. + */ + cmm_barrier(); + if (!--URCU_TLS(ust_fd_mutex_nest)) { + newstate = ust_safe_guard_saved_cancelstate; + restore_cancel = true; + pthread_mutex_unlock(&ust_safe_guard_fd_mutex); + } + ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (restore_cancel) { + ret = pthread_setcancelstate(newstate, &oldstate); + if (ret) { + ERR("pthread_setcancelstate: %s", strerror(ret)); + } + } +} + +static int dup_std_fd(int fd) +{ + int ret, i; + int fd_to_close[STDERR_FILENO + 1]; + int fd_to_close_count = 0; + int dup_cmd = F_DUPFD; /* Default command */ + int fd_valid = -1; + + if (!(IS_FD_STD(fd))) { + /* Should not be here */ + ret = -1; + goto error; + } + + /* Check for FD_CLOEXEC flag */ + ret = fcntl(fd, F_GETFD); + if (ret < 0) { + PERROR("fcntl on f_getfd"); + ret = -1; + goto error; + } + + if (ret & FD_CLOEXEC) { + dup_cmd = F_DUPFD_CLOEXEC; + } + + /* Perform dup */ + for (i = 0; i < STDERR_FILENO + 1; i++) { + ret = fcntl(fd, dup_cmd, 0); + if (ret < 0) { + PERROR("fcntl dup fd"); + goto error; + } + + if (!(IS_FD_STD(ret))) { + /* fd is outside of STD range, use it. */ + fd_valid = ret; + /* Close fd received as argument. */ + fd_to_close[i] = fd; + fd_to_close_count++; + break; + } + + fd_to_close[i] = ret; + fd_to_close_count++; + } + + /* Close intermediary fds */ + for (i = 0; i < fd_to_close_count; i++) { + ret = close(fd_to_close[i]); + if (ret) { + PERROR("close on temporary fd: %d.", fd_to_close[i]); + /* + * Not using an abort here would yield a complicated + * error handling for the caller. If a failure occurs + * here, the system is already in a bad state. + */ + abort(); + } + } + + ret = fd_valid; +error: + return ret; +} + +/* + * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd. + * Has strict checking of fd validity. + * + * If fd <= 2, dup the fd until fd > 2. This enables us to bypass + * problems that can be encountered if UST uses stdin, stdout, stderr + * fds for internal use (daemon etc.). This can happen if the + * application closes either of those file descriptors. Intermediary fds + * are closed as needed. + * + * Return -1 on error. + * + */ +int lttng_ust_add_fd_to_tracker(int fd) +{ + int ret; + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_init_fd_tracker(); + assert(URCU_TLS(ust_fd_mutex_nest)); + + if (IS_FD_STD(fd)) { + ret = dup_std_fd(fd); + if (ret < 0) { + goto error; + } + fd = ret; + } + + /* Trying to add an fd which we can not accommodate. */ + assert(IS_FD_VALID(fd)); + /* Setting an fd thats already set. */ + assert(!IS_FD_SET(fd, lttng_fd_set)); + + ADD_FD_TO_SET(fd, lttng_fd_set); + return fd; +error: + return ret; +} + +/* + * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd. + * Has strict checking for fd validity. + */ +void lttng_ust_delete_fd_from_tracker(int fd) +{ + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_init_fd_tracker(); + + assert(URCU_TLS(ust_fd_mutex_nest)); + /* Not a valid fd. */ + assert(IS_FD_VALID(fd)); + /* Deleting an fd which was not set. */ + assert(IS_FD_SET(fd, lttng_fd_set)); + + DEL_FD_FROM_SET(fd, lttng_fd_set); +} + +/* + * Interface allowing applications to close arbitrary file descriptors. + * We check if it is owned by lttng-ust, and return -1, errno=EBADF + * instead of closing it if it is the case. + */ +int lttng_ust_safe_close_fd(int fd, int (*close_cb)(int fd)) +{ + int ret = 0; + + lttng_ust_fixup_fd_tracker_tls(); + + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_init_fd_tracker(); + + /* + * If called from lttng-ust, we directly call close without + * validating whether the FD is part of the tracked set. + */ + if (URCU_TLS(ust_fd_mutex_nest)) + return close_cb(fd); + + lttng_ust_lock_fd_tracker(); + if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) { + ret = -1; + errno = EBADF; + } else { + ret = close_cb(fd); + } + lttng_ust_unlock_fd_tracker(); + + return ret; +} + +/* + * Interface allowing applications to close arbitrary streams. + * We check if it is owned by lttng-ust, and return -1, errno=EBADF + * instead of closing it if it is the case. + */ +int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream)) +{ + int ret = 0, fd; + + lttng_ust_fixup_fd_tracker_tls(); + + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_init_fd_tracker(); + + /* + * If called from lttng-ust, we directly call fclose without + * validating whether the FD is part of the tracked set. + */ + if (URCU_TLS(ust_fd_mutex_nest)) + return fclose_cb(stream); + + fd = fileno(stream); + + lttng_ust_lock_fd_tracker(); + if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) { + ret = -1; + errno = EBADF; + } else { + ret = fclose_cb(stream); + } + lttng_ust_unlock_fd_tracker(); + + return ret; +} + +#ifdef __OpenBSD__ +static void set_close_success(int *p) +{ + *p = 1; +} +static int test_close_success(const int *p) +{ + return *p; +} +#else +static void set_close_success(int *p __attribute__((unused))) +{ +} +static int test_close_success(const int *p __attribute__((unused))) +{ + return 1; +} +#endif + +/* + * Implement helper for closefrom() override. + */ +int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int fd)) +{ + int ret = 0, close_success = 0, i; + + lttng_ust_fixup_fd_tracker_tls(); + + /* + * Ensure the tracker is initialized when called from + * constructors. + */ + lttng_ust_init_fd_tracker(); + + if (lowfd < 0) { + /* + * NetBSD return EBADF if fd is invalid. + */ + errno = EBADF; + ret = -1; + goto end; + } + /* + * If called from lttng-ust, we directly call close without + * validating whether the FD is part of the tracked set. + */ + if (URCU_TLS(ust_fd_mutex_nest)) { + for (i = lowfd; i < lttng_ust_max_fd; i++) { + if (close_cb(i) < 0) { + switch (errno) { + case EBADF: + continue; + case EINTR: + default: + ret = -1; + goto end; + } + } + set_close_success(&close_success); + } + } else { + lttng_ust_lock_fd_tracker(); + for (i = lowfd; i < lttng_ust_max_fd; i++) { + if (IS_FD_VALID(i) && IS_FD_SET(i, lttng_fd_set)) + continue; + if (close_cb(i) < 0) { + switch (errno) { + case EBADF: + continue; + case EINTR: + default: + ret = -1; + lttng_ust_unlock_fd_tracker(); + goto end; + } + } + set_close_success(&close_success); + } + lttng_ust_unlock_fd_tracker(); + } + if (!test_close_success(&close_success)) { + /* + * OpenBSD return EBADF if fd is greater than all open + * file descriptors. + */ + ret = -1; + errno = EBADF; + } +end: + return ret; +} diff --git a/src/liblttng-ust/lttng-tracer-core.h b/src/liblttng-ust/lttng-tracer-core.h index e127a660..04ba642e 100644 --- a/src/liblttng-ust/lttng-tracer-core.h +++ b/src/liblttng-ust/lttng-tracer-core.h @@ -69,9 +69,6 @@ void lttng_fixup_time_ns_tls(void) void lttng_fixup_uts_ns_tls(void) __attribute__((visibility("hidden"))); -void lttng_ust_fixup_fd_tracker_tls(void) - __attribute__((visibility("hidden"))); - const char *lttng_ust_obj_get_name(int id) __attribute__((visibility("hidden"))); diff --git a/src/liblttng-ust/ust-common.c b/src/liblttng-ust/ust-common.c new file mode 100644 index 00000000..ad52770a --- /dev/null +++ b/src/liblttng-ust/ust-common.c @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (C) 2021 Michael Jeanson + */ + +#include "common/logging.h" +#include "common/ust-fd.h" + +static +void lttng_ust_common_init(void) + __attribute__((constructor)); +static +void lttng_ust_common_init(void) +{ + /* Initialize logging for liblttng-ust-common */ + ust_err_init(); + + /* + * Initialize the fd-tracker, other libraries using it should also call + * this in their constructor in case it gets executed before this one. + */ + lttng_ust_init_fd_tracker(); +} diff --git a/src/lttng-ust-ctl.pc.in b/src/lttng-ust-ctl.pc.in index 3170b30a..a82a6689 100644 --- a/src/lttng-ust-ctl.pc.in +++ b/src/lttng-ust-ctl.pc.in @@ -7,6 +7,6 @@ Name: LTTng Userspace Tracer control Description: The LTTng Userspace Tracer (UST) is a library accompanied by a set of tools to trace userspace code. Version: @PACKAGE_VERSION@ Requires: -Libs: -L${libdir} -llttng-ust-ctl +Libs: -L${libdir} -llttng-ust-ctl -llttng-ust-common Cflags: -I${includedir} diff --git a/tests/unit/libringbuffer/Makefile.am b/tests/unit/libringbuffer/Makefile.am index 7b086442..253e9dae 100644 --- a/tests/unit/libringbuffer/Makefile.am +++ b/tests/unit/libringbuffer/Makefile.am @@ -6,6 +6,6 @@ noinst_PROGRAMS = test_shm test_shm_SOURCES = shm.c test_shm_LDADD = \ $(top_builddir)/src/common/libringbuffer.la \ - $(top_builddir)/src/liblttng-ust-comm/liblttng-ust-comm.la \ + $(top_builddir)/src/liblttng-ust/liblttng-ust-common.la \ $(top_builddir)/src/common/libcommon.la \ $(top_builddir)/tests/utils/libtap.a