Fix shm open race
[lttng-ust.git] / libust / lttng-ust-comm.c
index b6c5de51e456e5e3d8f501fd75265819eeebf199..53e0b1efe647c1b4c8a7ec1a3cf22003ae0a4606 100644 (file)
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/prctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <pthread.h>
@@ -37,6 +42,7 @@
 #include <ust/tracepoint.h>
 #include <ust/tracepoint-internal.h>
 #include <ust/ust.h>
+#include "ltt-tracer-core.h"
 
 /*
  * Has lttng ust comm constructor been called ?
@@ -73,30 +79,42 @@ static int sem_count = { 2 };
  */
 struct sock_info {
        const char *name;
-       char sock_path[PATH_MAX];
-       int socket;
        pthread_t ust_listener; /* listener thread */
        int root_handle;
        int constructor_sem_posted;
        int allowed;
+       int global;
+
+       char sock_path[PATH_MAX];
+       int socket;
+
+       char wait_shm_path[PATH_MAX];
+       char *wait_shm_mmap;
 };
 
 /* Socket from app (connect) to session daemon (listen) for communication */
 struct sock_info global_apps = {
        .name = "global",
-       .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
-       .socket = -1,
+       .global = 1,
+
        .root_handle = -1,
        .allowed = 1,
+
+       .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
+       .socket = -1,
+
+       .wait_shm_path = DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH,
 };
 
 /* TODO: allow global_apps_sock_path override */
 
 struct sock_info local_apps = {
        .name = "local",
-       .socket = -1,
+       .global = 0,
        .root_handle = -1,
        .allowed = 0,   /* Check setuid bit first */
+
+       .socket = -1,
 };
 
 extern void ltt_ring_buffer_client_overwrite_init(void);
@@ -110,11 +128,13 @@ static
 int setup_local_apps(void)
 {
        const char *home_dir;
+       uid_t uid;
 
+       uid = getuid();
        /*
         * Disallow per-user tracing for setuid binaries.
         */
-       if (getuid() != geteuid()) {
+       if (uid != geteuid()) {
                local_apps.allowed = 0;
                return 0;
        } else {
@@ -125,6 +145,8 @@ int setup_local_apps(void)
                return -ENOENT;
        snprintf(local_apps.sock_path, PATH_MAX,
                 DEFAULT_HOME_APPS_UNIX_SOCK, home_dir);
+       snprintf(local_apps.wait_shm_path, PATH_MAX,
+                DEFAULT_HOME_APPS_WAIT_SHM_PATH, uid);
        return 0;
 }
 
@@ -267,7 +289,7 @@ void cleanup_sock_info(struct sock_info *sock_info)
        if (sock_info->socket != -1) {
                ret = close(sock_info->socket);
                if (ret) {
-                       ERR("Error closing local apps socket");
+                       ERR("Error closing apps socket");
                }
                sock_info->socket = -1;
        }
@@ -278,6 +300,199 @@ void cleanup_sock_info(struct sock_info *sock_info)
                }
                sock_info->root_handle = -1;
        }
+       sock_info->constructor_sem_posted = 0;
+       if (sock_info->wait_shm_mmap) {
+               ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE));
+               if (ret) {
+                       ERR("Error unmapping wait shm");
+               }
+               sock_info->wait_shm_mmap = NULL;
+       }
+}
+
+/*
+ * Using fork to set umask to 0777 in the child process (not
+ * multi-thread safe).
+ */
+static
+int get_wait_shm(struct sock_info *sock_info, size_t mmap_size)
+{
+       int wait_shm_fd, ret;
+       int read_mode;
+       pid_t pid;
+
+       /*
+        * At this point, we should be able to open it for
+        * reading. If it fails, then it's because there is
+        * something wrong: bail out in that case.
+        */
+       read_mode = S_IRUSR | S_IRGRP;
+       if (sock_info->global)
+               read_mode |= S_IROTH;
+
+       /*
+        * Try to open read-only. If it is set read-only, it
+        * means the shm size has been already set with
+        * ftruncate. Note: all processes creating shm need to
+        * call ftruncate on the shm before restricting its
+        * access rights to read-only. The shm should never be
+        * unlinked. It a rogue process try to create a non-accessible
+        * shm or to unlink it, the worse-case scenario is that we don't
+        * use the shm wakeup method and sleep/retry instead.
+        */
+       wait_shm_fd = shm_open(sock_info->wait_shm_path,
+                       O_RDONLY, read_mode);
+       if (wait_shm_fd >= 0) {
+               goto end;
+       } else if (wait_shm_fd < 0 && errno != ENOENT) {
+               /*
+                * Real-only open did not work. It's a failure
+                * that prohibits using shm.
+                */
+               ERR("Error opening shm %s", sock_info->wait_shm_path);
+               goto end;
+       }
+
+       /*
+        * If the open failed because the file did not exist, try
+        * creating it ourself.
+        */
+       pid = fork();
+       if (pid > 0) {
+               int status;
+
+               /*
+                * Parent: wait for child to return, in which case the
+                * shared memory map will have been created.
+                */
+               pid = wait(&status);
+               if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+                       wait_shm_fd = -1;
+                       goto end;
+               }
+               /*
+                * Try to open read-only again after creation.
+                */
+               wait_shm_fd = shm_open(sock_info->wait_shm_path,
+                               O_RDONLY, read_mode);
+               if (wait_shm_fd < 0) {
+                       /*
+                        * Real-only open did not work. It's a failure
+                        * that prohibits using shm.
+                        */
+                       ERR("Error opening shm %s", sock_info->wait_shm_path);
+                       goto end;
+               }
+               goto end;
+       } else if (pid == 0) {
+               int create_mode;
+
+               /* Child */
+               create_mode = S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
+               if (sock_info->global)
+                       create_mode |= S_IROTH | S_IWOTH;
+               /*
+                * We're alone in a child process, so we can modify the
+                * process-wide umask.
+                */
+               umask(create_mode);
+               /*
+                * First try creating shm (or get rw access). We need to start
+                * by this because of the ftruncate vs concurrent map race.
+                * We need to give write access to everyone because of the
+                * ftruncate vs mmap race too. We don't do an exclusive
+                * open, because we allow other processes to
+                * create+ftruncate it concurrently.
+                */
+               wait_shm_fd = shm_open(sock_info->wait_shm_path,
+                               O_RDWR | O_CREAT, create_mode);
+               if (wait_shm_fd >= 0) {
+                       ret = ftruncate(wait_shm_fd, mmap_size);
+                       if (ret) {
+                               PERROR("ftruncate");
+                               exit(EXIT_FAILURE);
+                       }
+                       ret = fchmod(wait_shm_fd, read_mode);
+                       if (ret) {
+                               PERROR("fchmod");
+                               exit(EXIT_FAILURE);
+                       }
+                       exit(EXIT_SUCCESS);
+               }
+               if (errno != EACCES) {
+                       ERR("Error opening shm %s", sock_info->wait_shm_path);
+                       exit(EXIT_FAILURE);
+               }
+               /*
+                * The shm exists, but we cannot open it RW. It means it
+                * has already been setup and ftruncated, so we can
+                * let the child exit.
+                */
+               exit(EXIT_SUCCESS);
+       } else {
+               return -1;
+       }
+end:
+       return wait_shm_fd;
+}
+
+static
+char *get_map_shm(struct sock_info *sock_info)
+{
+       size_t mmap_size = sysconf(_SC_PAGE_SIZE);
+       int wait_shm_fd, ret;
+       char *wait_shm_mmap;
+
+       wait_shm_fd = get_wait_shm(sock_info, mmap_size);
+       if (wait_shm_fd < 0) {
+               goto error;
+       }
+       wait_shm_mmap = mmap(NULL, mmap_size, PROT_READ,
+                 MAP_SHARED, wait_shm_fd, 0);
+       if (wait_shm_mmap == MAP_FAILED) {
+               PERROR("mmap");
+               goto error;
+       }
+       /* close shm fd immediately after taking the mmap reference */
+       ret = close(wait_shm_fd);
+       if (ret) {
+               ERR("Error closing fd");
+       }
+       return wait_shm_mmap;
+
+error:
+       return NULL;
+}
+
+static
+void wait_for_sessiond(struct sock_info *sock_info)
+{
+       ust_lock();
+       if (lttng_ust_comm_should_quit) {
+               goto quit;
+       }
+       if (!sock_info->wait_shm_mmap) {
+               sock_info->wait_shm_mmap = get_map_shm(sock_info);
+               if (!sock_info->wait_shm_mmap)
+                       goto error;
+       }
+       ust_unlock();
+
+       DBG("Waiting for %s apps sessiond", sock_info->name);
+       /* Wait for futex wakeup TODO */
+       sleep(5);
+
+       return;
+
+quit:
+       ust_unlock();
+       return;
+
+error:
+       ust_unlock();
+       /* Error handling: fallback on a 5 seconds sleep. */
+       sleep(5);
+       return;
 }
 
 /*
@@ -310,8 +525,6 @@ restart:
                sock_info->socket = -1;
        }
 
-       /* Check for sessiond availability with pipe TODO */
-
        /* Register */
        ret = lttcomm_connect_unix_sock(sock_info->sock_path);
        if (ret < 0) {
@@ -323,7 +536,9 @@ restart:
                ret = handle_register_done(sock_info);
                assert(!ret);
                ust_unlock();
-               sleep(5);
+
+               /* Wait for sessiond availability with pipe */
+               wait_for_sessiond(sock_info);
                goto restart;
        }
 
@@ -353,7 +568,7 @@ restart:
                ret = handle_register_done(sock_info);
                assert(!ret);
                ust_unlock();
-               sleep(5);
+               wait_for_sessiond(sock_info);
                goto restart;
        }
        ust_unlock();
@@ -618,7 +833,7 @@ void ust_after_fork_child(ust_fork_info_t *fork_info)
        /* Release urcu mutexes */
        rcu_bp_after_fork_child();
        lttng_ust_cleanup(0);
-       lttng_ust_init();
        /* Release mutexes and reenable signals */
        ust_after_fork_common(fork_info);
+       lttng_ust_init();
 }
This page took 0.028014 seconds and 4 git commands to generate.