Fix: worker structure is leaked in run_as process
[lttng-tools.git] / src / common / runas.c
index 4bd356a0499f2119a227a69b324ab5248d7ba8a0..4fc229b0d0317452df0b3db08c78ab4c998a97d3 100644 (file)
@@ -218,7 +218,7 @@ int _open(struct run_as_data *data, struct run_as_ret *ret_value)
        ret_value->u.open.ret = open(data->u.open.path, data->u.open.flags, data->u.open.mode);
        ret_value->fd = ret_value->u.open.ret;
        ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.open.ret) ? true : false;
+       ret_value->_error = ret_value->u.open.ret < 0;
        return ret_value->u.open.ret;
 }
 
@@ -329,7 +329,8 @@ int do_send_fd(int sock, int fd)
        ssize_t len;
 
        if (fd < 0) {
-               ERR("Invalid file description");
+               ERR("Attempt to send invalid file descriptor to master (fd = %i)", fd);
+               /* Return 0 as this is not a fatal error. */
                return 0;
        }
 
@@ -346,11 +347,6 @@ int do_recv_fd(int sock, int *fd)
 {
        ssize_t len;
 
-       if (*fd < 0) {
-               ERR("Invalid file description");
-               return 0;
-       }
-
        len = lttcomm_recv_fds_unix_sock(sock, fd, 1);
 
        if (!len) {
@@ -359,6 +355,12 @@ int do_recv_fd(int sock, int *fd)
                PERROR("lttcomm_recv_fds_unix_sock");
                return -1;
        }
+       if (*fd < 0) {
+               ERR("Invalid file descriptor received from worker (fd = %i)", *fd);
+               /* Return 0 as this is not a fatal error. */
+               return 0;
+       }
+
        return 0;
 }
 
@@ -375,6 +377,11 @@ int send_fd_to_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
                return 0;
        }
 
+       if (fd < 0) {
+               ERR("Refusing to send invalid fd to worker (fd = %i)", fd);
+               return -1;
+       }
+
        ret = do_send_fd(worker->sockpair[0], fd);
        if (ret < 0) {
                PERROR("do_send_fd");
@@ -396,20 +403,21 @@ int send_fd_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
                return 0;
        }
 
+       if (fd < 0) {
+               DBG("Not sending file descriptor to master as it is invalid (fd = %i)", fd);
+               return 0;
+       }
        ret = do_send_fd(worker->sockpair[1], fd);
        if (ret < 0) {
                PERROR("do_send_fd error");
                ret = -1;
        }
 
-       if (fd < 0) {
-               goto end;
-       }
        ret_close = close(fd);
        if (ret_close < 0) {
                PERROR("close");
        }
-end:
+
        return ret;
 }
 
@@ -726,6 +734,11 @@ int run_as_cmd(struct run_as_worker *worker,
                goto end;
        }
 
+       if (ret_value->_error) {
+               /* Skip stage 5 on error as there will be no fd to receive. */
+               goto end;
+       }
+
        /*
         * Stage 5: Receive file descriptor if needed
         */
@@ -767,6 +780,194 @@ end:
        return ret;
 }
 
+static
+int reset_sighandler(void)
+{
+       int sig;
+
+       DBG("Resetting run_as worker signal handlers to default");
+       for (sig = 1; sig <= 31; sig++) {
+               (void) signal(sig, SIG_DFL);
+       }
+       return 0;
+}
+
+static
+void worker_sighandler(int sig)
+{
+       const char *signame;
+
+       /*
+        * The worker will inherit its parent's signals since they are part of
+        * the same process group. However, in the case of SIGINT and SIGTERM,
+        * we want to give the worker a chance to teardown gracefully when its
+        * parent closes the command socket.
+        */
+       switch (sig) {
+       case SIGINT:
+               signame = "SIGINT";
+               break;
+       case SIGTERM:
+               signame = "SIGTERM";
+               break;
+       default:
+               signame = NULL;
+       }
+
+       if (signame) {
+               DBG("run_as worker received signal %s", signame);
+       } else {
+               DBG("run_as_worker received signal %d", sig);
+       }
+}
+
+static
+int set_worker_sighandlers(void)
+{
+       int ret = 0;
+       sigset_t sigset;
+       struct sigaction sa;
+
+       if ((ret = sigemptyset(&sigset)) < 0) {
+               PERROR("sigemptyset");
+               goto end;
+       }
+
+       sa.sa_handler = worker_sighandler;
+       sa.sa_mask = sigset;
+       sa.sa_flags = 0;
+       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGINT");
+               goto end;
+       }
+
+       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGTERM");
+               goto end;
+       }
+
+       DBG("run_as signal handler set for SIGTERM and SIGINT");
+end:
+       return ret;
+}
+
+static
+int run_as_create_worker_no_lock(const char *procname)
+{
+       pid_t pid;
+       int i, ret = 0;
+       ssize_t readlen;
+       struct run_as_ret recvret;
+       struct run_as_worker *worker;
+
+       assert(!global_worker);
+       if (!use_clone()) {
+               /*
+                * Don't initialize a worker, all run_as tasks will be performed
+                * in the current process.
+                */
+               ret = 0;
+               goto end;
+       }
+       worker = zmalloc(sizeof(*worker));
+       if (!worker) {
+               ret = -ENOMEM;
+               goto end;
+       }
+       worker->procname = strdup(procname);
+       if (!worker->procname) {
+               ret = -ENOMEM;
+               goto error_procname_alloc;
+       }
+       /* Create unix socket. */
+       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
+               ret = -1;
+               goto error_sock;
+       }
+
+       /* Fork worker. */
+       pid = fork();
+       if (pid < 0) {
+               PERROR("fork");
+               ret = -1;
+               goto error_fork;
+       } else if (pid == 0) {
+               /* Child */
+
+               reset_sighandler();
+
+               set_worker_sighandlers();
+
+               /* Just close, no shutdown. */
+               if (close(worker->sockpair[0])) {
+                       PERROR("close");
+                       exit(EXIT_FAILURE);
+               }
+
+               /*
+                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
+                * Sockpair[1] is used as a control channel with the master
+                */
+               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
+                       if (i != worker->sockpair[1]) {
+                               (void) close(i);
+                       }
+               }
+
+               worker->sockpair[0] = -1;
+               ret = run_as_worker(worker);
+               if (lttcomm_close_unix_sock(worker->sockpair[1])) {
+                       PERROR("close");
+                       ret = -1;
+               }
+               worker->sockpair[1] = -1;
+               free(worker->procname);
+               free(worker);
+               LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
+               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
+       } else {
+               /* Parent */
+
+               /* Just close, no shutdown. */
+               if (close(worker->sockpair[1])) {
+                       PERROR("close");
+                       ret = -1;
+                       goto error_fork;
+               }
+               worker->sockpair[1] = -1;
+               worker->pid = pid;
+               /* Wait for worker to become ready. */
+               readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
+                               &recvret, sizeof(recvret));
+               if (readlen < sizeof(recvret)) {
+                       ERR("readlen: %zd", readlen);
+                       PERROR("Error reading response from run_as at creation");
+                       ret = -1;
+                       goto error_fork;
+               }
+               global_worker = worker;
+       }
+end:
+       return ret;
+
+       /* Error handling. */
+error_fork:
+       for (i = 0; i < 2; i++) {
+               if (worker->sockpair[i] < 0) {
+                       continue;
+               }
+               if (lttcomm_close_unix_sock(worker->sockpair[i])) {
+                       PERROR("close");
+               }
+               worker->sockpair[i] = -1;
+       }
+error_sock:
+       free(worker->procname);
+error_procname_alloc:
+       free(worker);
+       return ret;
+}
+
 static
 int run_as_restart_worker(struct run_as_worker *worker)
 {
@@ -779,7 +980,7 @@ int run_as_restart_worker(struct run_as_worker *worker)
        run_as_destroy_worker();
 
        /* Create a new run_as worker process*/
-       ret = run_as_create_worker(procname);
+       ret = run_as_create_worker_no_lock(procname);
        if (ret < 0 ) {
                ERR("Restarting the worker process failed");
                ret = -1;
@@ -795,15 +996,15 @@ int run_as(enum run_as_cmd cmd, struct run_as_data *data,
 {
        int ret, saved_errno;
 
+       pthread_mutex_lock(&worker_lock);
        if (use_clone()) {
                DBG("Using run_as worker");
-               pthread_mutex_lock(&worker_lock);
+
                assert(global_worker);
 
                ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
                saved_errno = ret_value->_errno;
 
-               pthread_mutex_unlock(&worker_lock);
                /*
                 * If the worker thread crashed the errno is set to EIO. we log
                 * the error and  start a new worker process.
@@ -822,6 +1023,7 @@ int run_as(enum run_as_cmd cmd, struct run_as_data *data,
                ret = run_as_noworker(cmd, data, ret_value, uid, gid);
        }
 err:
+       pthread_mutex_unlock(&worker_lock);
        return ret;
 }
 
@@ -993,187 +1195,13 @@ int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name,
        return 0;
 }
 
-static
-int reset_sighandler(void)
-{
-       int sig;
-
-       DBG("Resetting run_as worker signal handlers to default");
-       for (sig = 1; sig <= 31; sig++) {
-               (void) signal(sig, SIG_DFL);
-       }
-       return 0;
-}
-
-static
-void worker_sighandler(int sig)
-{
-       const char *signame;
-
-       /*
-        * The worker will inherit its parent's signals since they are part of
-        * the same process group. However, in the case of SIGINT and SIGTERM,
-        * we want to give the worker a chance to teardown gracefully when its
-        * parent closes the command socket.
-        */
-       switch (sig) {
-       case SIGINT:
-               signame = "SIGINT";
-               break;
-       case SIGTERM:
-               signame = "SIGTERM";
-               break;
-       default:
-               signame = NULL;
-       }
-
-       if (signame) {
-               DBG("run_as worker received signal %s", signame);
-       } else {
-               DBG("run_as_worker received signal %d", sig);
-       }
-}
-
-static
-int set_worker_sighandlers(void)
-{
-       int ret = 0;
-       sigset_t sigset;
-       struct sigaction sa;
-
-       if ((ret = sigemptyset(&sigset)) < 0) {
-               PERROR("sigemptyset");
-               goto end;
-       }
-
-       sa.sa_handler = worker_sighandler;
-       sa.sa_mask = sigset;
-       sa.sa_flags = 0;
-       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGINT");
-               goto end;
-       }
-
-       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGTERM");
-               goto end;
-       }
-
-       DBG("run_as signal handler set for SIGTERM and SIGINT");
-end:
-       return ret;
-}
-
 LTTNG_HIDDEN
-int run_as_create_worker(char *procname)
+int run_as_create_worker(const char *procname)
 {
-       pid_t pid;
-       int i, ret = 0;
-       ssize_t readlen;
-       struct run_as_ret recvret;
-       struct run_as_worker *worker;
+       int ret;
 
        pthread_mutex_lock(&worker_lock);
-       assert(!global_worker);
-       if (!use_clone()) {
-               /*
-                * Don't initialize a worker, all run_as tasks will be performed
-                * in the current process.
-                */
-               ret = 0;
-               goto end;
-       }
-       worker = zmalloc(sizeof(*worker));
-       if (!worker) {
-               ret = -ENOMEM;
-               goto end;
-       }
-       worker->procname = procname;
-       /* Create unix socket. */
-       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
-               ret = -1;
-               goto error_sock;
-       }
-
-       /* Fork worker. */
-       pid = fork();
-       if (pid < 0) {
-               PERROR("fork");
-               ret = -1;
-               goto error_fork;
-       } else if (pid == 0) {
-               /* Child */
-
-               reset_sighandler();
-
-               set_worker_sighandlers();
-
-               /* The child has no use for this lock. */
-               pthread_mutex_unlock(&worker_lock);
-               /* Just close, no shutdown. */
-               if (close(worker->sockpair[0])) {
-                       PERROR("close");
-                       exit(EXIT_FAILURE);
-               }
-
-               /*
-                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
-                * Sockpair[1] is used as a control channel with the master
-                */
-               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
-                       if (i != worker->sockpair[1]) {
-                               (void) close(i);
-                       }
-               }
-
-               worker->sockpair[0] = -1;
-               ret = run_as_worker(worker);
-               if (lttcomm_close_unix_sock(worker->sockpair[1])) {
-                       PERROR("close");
-                       ret = -1;
-               }
-               worker->sockpair[1] = -1;
-               LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
-               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
-       } else {
-               /* Parent */
-
-               /* Just close, no shutdown. */
-               if (close(worker->sockpair[1])) {
-                       PERROR("close");
-                       ret = -1;
-                       goto error_fork;
-               }
-               worker->sockpair[1] = -1;
-               worker->pid = pid;
-               /* Wait for worker to become ready. */
-               readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
-                               &recvret, sizeof(recvret));
-               if (readlen < sizeof(recvret)) {
-                       ERR("readlen: %zd", readlen);
-                       PERROR("Error reading response from run_as at creation");
-                       ret = -1;
-                       goto error_fork;
-               }
-               global_worker = worker;
-       }
-end:
-       pthread_mutex_unlock(&worker_lock);
-       return ret;
-
-       /* Error handling. */
-error_fork:
-       for (i = 0; i < 2; i++) {
-               if (worker->sockpair[i] < 0) {
-                       continue;
-               }
-               if (lttcomm_close_unix_sock(worker->sockpair[i])) {
-                       PERROR("close");
-               }
-               worker->sockpair[i] = -1;
-       }
-error_sock:
-       free(worker);
+       ret = run_as_create_worker_no_lock(procname);
        pthread_mutex_unlock(&worker_lock);
        return ret;
 }
@@ -1219,6 +1247,7 @@ void run_as_destroy_worker(void)
                        break;
                }
        }
+       free(worker->procname);
        free(worker);
        global_worker = NULL;
 end:
This page took 0.030172 seconds and 5 git commands to generate.