#include <common/compat/dlfcn.h>
#include <unistd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <signal.h>
#include <sched.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <errno.h>
+#include <pthread.h>
+
#include <lttng/ust-fork.h>
+#include <urcu/uatomic.h>
+
+#include "common/macros.h"
+
+struct libc_pointer {
+ void **procedure;
+ const char *symbol;
+};
+
+#define DEFINE_LIBC_POINTER(name) { (void**)&plibc_## name, #name }
+
+#ifdef __linux__
+
+struct user_desc;
+
+static int (*plibc_clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, pid_t *ptid,
+ struct user_desc *tls, pid_t *ctid) = NULL;
+
+static int (*plibc_setns)(int fd, int nstype) = NULL;
+
+static int (*plibc_setresgid)(gid_t rgid, gid_t egid, gid_t sgid) = NULL;
+
+static int (*plibc_setresuid)(uid_t ruid, uid_t euid, uid_t suid) = NULL;
+
+static int (*plibc_unshare)(int flags) = NULL;
+
+#elif defined (__FreeBSD__)
+
+static pid_t (*plibc_rfork)(int flags) = NULL;
+
+#endif
+
+static int (*plibc_daemon)(int nochdir, int noclose) = NULL;
+
+static pid_t (*plibc_fork)(void) = NULL;
+
+static int (*plibc_setegid)(gid_t egid) = NULL;
+
+static int (*plibc_seteuid)(uid_t euid) = NULL;
+
+static int (*plibc_setgid)(gid_t gid) = NULL;
+
+static int (*plibc_setregid)(gid_t rgid, gid_t egid) = NULL;
+
+static int (*plibc_setreuid)(uid_t ruid, uid_t euid) = NULL;
+
+static int (*plibc_setuid)(uid_t uid) = NULL;
+
+static void lttng_ust_fork_wrapper_ctor(void)
+ __attribute__((constructor));
+
+static pthread_mutex_t initialization_guard = PTHREAD_MUTEX_INITIALIZER;
+static bool was_initialized = false;
+
+/*
+ * Must be called with initialization_guard held.
+ */
+static void initialize(void)
+{
+ const struct libc_pointer libc_pointers[] = {
+#ifdef __linux__
+ DEFINE_LIBC_POINTER(clone),
+ DEFINE_LIBC_POINTER(setns),
+ DEFINE_LIBC_POINTER(setresgid),
+ DEFINE_LIBC_POINTER(setresuid),
+ DEFINE_LIBC_POINTER(unshare),
+#elif defined (__FreeBSD__)
+ DEFINE_LIBC_POINTER(rfork),
+#endif
+ DEFINE_LIBC_POINTER(daemon),
+ DEFINE_LIBC_POINTER(fork),
+ DEFINE_LIBC_POINTER(setegid),
+ DEFINE_LIBC_POINTER(seteuid),
+ DEFINE_LIBC_POINTER(setgid),
+ DEFINE_LIBC_POINTER(setregid),
+ DEFINE_LIBC_POINTER(setreuid),
+ DEFINE_LIBC_POINTER(setuid),
+ };
+
+ size_t k;
+
+ for (k = 0; k < LTTNG_ARRAY_SIZE(libc_pointers); ++k) {
+ void *procedure = dlsym(RTLD_NEXT, libc_pointers[k].symbol);
+
+ if (NULL == procedure) {
+ fprintf(stderr,
+ "libustfork: unable to find \"%s\" symbol\n",
+ libc_pointers[k].symbol);
+ continue;
+ }
+
+ uatomic_set(libc_pointers[k].procedure, procedure);
+ }
+}
+
+/*
+ * Lazy initialization is required because it is possible for a shared library
+ * to have a constructor that is executed before our constructor, which could
+ * call some libc functions that we are wrapping.
+ *
+ * It is also possible for this library constructor to create a thread using the
+ * raw system call. Therefore, the lazy initialization must be multi-thread safe.
+ */
+static void *lazy_initialize(void **pfunc)
+{
+ void *func = uatomic_read(pfunc);
+
+ /*
+ * If *pfunc != NULL, then it is assumed that some thread has already
+ * called the initialization routine.
+ */
+ if (caa_likely(func)) {
+ goto out;
+ }
+
+ pthread_mutex_lock(&initialization_guard);
+ if (!was_initialized) {
+ initialize();
+ was_initialized = true;
+ }
+ func = *pfunc;
+ pthread_mutex_unlock(&initialization_guard);
+out:
+ return func;
+}
+
+#define LAZY_INITIALIZE_OR_NOSYS(ptr) \
+ ({ \
+ void *ret; \
+ \
+ ret = lazy_initialize((void**)&(ptr)); \
+ if (NULL == ret) { \
+ errno = ENOSYS; \
+ return -1; \
+ } \
+ \
+ ret; \
+ })
+
+static void lttng_ust_fork_wrapper_ctor(void)
+{
+ /*
+ * Using fork here because it is defined on all supported OS.
+ */
+ (void) lazy_initialize((void**)&plibc_fork);
+}
+
pid_t fork(void)
{
- static pid_t (*plibc_func)(void) = NULL;
sigset_t sigset;
pid_t retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "fork");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"fork\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ pid_t (*func)(void) = LAZY_INITIALIZE_OR_NOSYS(plibc_fork);
lttng_ust_before_fork(&sigset);
/* Do the real fork */
- retval = plibc_func();
+ retval = func();
saved_errno = errno;
if (retval == 0) {
/* child */
int daemon(int nochdir, int noclose)
{
- static int (*plibc_func)(int nochdir, int noclose) = NULL;
sigset_t sigset;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "daemon");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"daemon\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(int, int) = LAZY_INITIALIZE_OR_NOSYS(plibc_daemon);
lttng_ust_before_fork(&sigset);
/* Do the real daemon call */
- retval = plibc_func(nochdir, noclose);
+ retval = func(nochdir, noclose);
saved_errno = errno;
if (retval == 0) {
/* child, parent called _exit() directly */
int setuid(uid_t uid)
{
- static int (*plibc_func)(uid_t uid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setuid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setuid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(uid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setuid);
/* Do the real setuid */
- retval = plibc_func(uid);
+ retval = func(uid);
saved_errno = errno;
lttng_ust_after_setuid();
int setgid(gid_t gid)
{
- static int (*plibc_func)(gid_t gid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setgid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setgid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setgid);
/* Do the real setgid */
- retval = plibc_func(gid);
+ retval = func(gid);
saved_errno = errno;
lttng_ust_after_setgid();
int seteuid(uid_t euid)
{
- static int (*plibc_func)(uid_t euid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "seteuid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"seteuid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(uid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_seteuid);
/* Do the real seteuid */
- retval = plibc_func(euid);
+ retval = func(euid);
saved_errno = errno;
lttng_ust_after_seteuid();
int setegid(gid_t egid)
{
- static int (*plibc_func)(gid_t egid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setegid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setegid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setegid);
/* Do the real setegid */
- retval = plibc_func(egid);
+ retval = func(egid);
saved_errno = errno;
lttng_ust_after_setegid();
int setreuid(uid_t ruid, uid_t euid)
{
- static int (*plibc_func)(uid_t ruid, uid_t euid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setreuid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setreuid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(uid_t, uid_t) =
+ LAZY_INITIALIZE_OR_NOSYS(plibc_setreuid);
/* Do the real setreuid */
- retval = plibc_func(ruid, euid);
+ retval = func(ruid, euid);
saved_errno = errno;
lttng_ust_after_setreuid();
int setregid(gid_t rgid, gid_t egid)
{
- static int (*plibc_func)(gid_t rgid, gid_t egid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setregid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setregid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(gid_t, gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setregid);
/* Do the real setregid */
- retval = plibc_func(rgid, egid);
+ retval = func(rgid, egid);
saved_errno = errno;
lttng_ust_after_setregid();
#ifdef __linux__
-struct user_desc;
-
struct ustfork_clone_info {
int (*fn)(void *);
void *arg;
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
{
- static int (*plibc_func)(int (*fn)(void *), void *child_stack,
- int flags, void *arg, pid_t *ptid,
- struct user_desc *tls, pid_t *ctid) = NULL;
/* var args */
pid_t *ptid;
struct user_desc *tls;
ctid = va_arg(ap, pid_t *);
va_end(ap);
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "clone");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"clone\" symbol.\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(int (*)(void *), void *, int , void *, pid_t *,
+ struct user_desc *, pid_t *) =
+ LAZY_INITIALIZE_OR_NOSYS(plibc_clone);
if (flags & CLONE_VM) {
/*
* Creating a thread, no need to intervene, just pass on
* the arguments.
*/
- retval = plibc_func(fn, child_stack, flags, arg, ptid,
- tls, ctid);
+ retval = func(fn, child_stack, flags, arg, ptid,
+ tls, ctid);
saved_errno = errno;
} else {
/* Creating a real process, we need to intervene. */
struct ustfork_clone_info info = { .fn = fn, .arg = arg };
lttng_ust_before_fork(&info.sigset);
- retval = plibc_func(clone_fn, child_stack, flags, &info,
- ptid, tls, ctid);
+ retval = func(clone_fn, child_stack, flags, &info,
+ ptid, tls, ctid);
saved_errno = errno;
/* The child doesn't get here. */
lttng_ust_after_fork_parent(&info.sigset);
int setns(int fd, int nstype)
{
- static int (*plibc_func)(int fd, int nstype) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setns");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setns\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(int, int) = LAZY_INITIALIZE_OR_NOSYS(plibc_setns);
/* Do the real setns */
- retval = plibc_func(fd, nstype);
+ retval = func(fd, nstype);
saved_errno = errno;
lttng_ust_after_setns();
int unshare(int flags)
{
- static int (*plibc_func)(int flags) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "unshare");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"unshare\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(int) = LAZY_INITIALIZE_OR_NOSYS(plibc_unshare);
/* Do the real setns */
- retval = plibc_func(flags);
+ retval = func(flags);
saved_errno = errno;
lttng_ust_after_unshare();
int setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
- static int (*plibc_func)(uid_t ruid, uid_t euid, uid_t suid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setresuid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setresuid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(uid_t, uid_t, uid_t) =
+ LAZY_INITIALIZE_OR_NOSYS(plibc_setresuid);
/* Do the real setresuid */
- retval = plibc_func(ruid, euid, suid);
+ retval = func(ruid, euid, suid);
saved_errno = errno;
lttng_ust_after_setresuid();
int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
{
- static int (*plibc_func)(gid_t rgid, gid_t egid, gid_t sgid) = NULL;
int retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "setresgid");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"setresgid\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ int (*func)(gid_t, gid_t, gid_t) =
+ LAZY_INITIALIZE_OR_NOSYS(plibc_setresgid);
/* Do the real setresgid */
- retval = plibc_func(rgid, egid, sgid);
+ retval = func(rgid, egid, sgid);
saved_errno = errno;
lttng_ust_after_setresgid();
pid_t rfork(int flags)
{
- static pid_t (*plibc_func)(void) = NULL;
sigset_t sigset;
pid_t retval;
int saved_errno;
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "rfork");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"rfork\" symbol\n");
- errno = ENOSYS;
- return -1;
- }
- }
+ pid_t (*func)(int) = LAZY_INITIALIZE_OR_NOSYS(plibc_rfork);
lttng_ust_before_fork(&sigset);
/* Do the real rfork */
- retval = plibc_func();
+ retval = func(flags);
saved_errno = errno;
if (retval == 0) {
/* child */