Fix: agent port file is o+w when launching as root
[lttng-tools.git] / src / bin / lttng-sessiond / shm.c
index 7bb52d4ee2feeb513d59079bf11813f319d8c48a..8422230708253fd3a246fc8ec1eb5fdaeada5fe7 100644 (file)
@@ -1,22 +1,12 @@
 /*
- * Copyright (C)  2011 - David Goulet <david.goulet@polymtl.ca>
- *                       Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2011 EfficiOS Inc.
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2 only,
- * as published by the Free Software Foundation.
+ * SPDX-License-Identifier: GPL-2.0-only
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#define _GNU_SOURCE
+#define _LGPL_SOURCE
 #include <fcntl.h>
 #include <limits.h>
 #include <sys/mman.h>
@@ -31,8 +21,7 @@
 #include "shm.h"
 
 /*
- * Using fork to set umask in the child process (not multi-thread safe). We
- * deal with the shm_open vs ftruncate race (happening when the sessiond owns
+ * We deal with the shm_open vs ftruncate race (happening when the sessiond owns
  * the shm and does not let everybody modify it, to ensure safety against
  * shm_unlink) by simply letting the mmap fail and retrying after a few
  * seconds. For global shm, everybody has rw access to it until the sessiond
@@ -41,7 +30,7 @@
 static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
 {
        int wait_shm_fd, ret;
-       mode_t mode;
+       mode_t mode, old_mode;
 
        assert(shm_path);
 
@@ -61,26 +50,55 @@ static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
                mode |= S_IROTH | S_IWOTH;
        }
 
-       /*
-        * We're alone in a child process, so we can modify the process-wide
-        * umask.
-        */
-       umask(~mode);
+       old_mode = umask(~mode);
 
        /*
         * Try creating shm (or get rw access). We don't do an exclusive open,
         * because we allow other processes to create+ftruncate it concurrently.
+        *
+        * A sysctl, fs.protected_regular may prevent the session daemon from
+        * opening a previously created shm when the O_CREAT flag is provided.
+        * Systemd enables this ABI-breaking change by default since v241.
+        *
+        * First, attempt to use the create-or-open semantic that is
+        * desired here. If this fails with EACCES, work around this broken
+        * behaviour and attempt to open the shm without the O_CREAT flag.
+        *
+        * The two attempts are made in this order since applications are
+        * expected to race with the session daemon to create this shm.
+        * Attempting an shm_open() without the O_CREAT flag first could fail
+        * because the file doesn't exist. It could then be created by an
+        * application, which would cause a second try with the O_CREAT flag to
+        * fail with EACCES.
+        *
+        * Note that this introduces a new failure mode where a user could
+        * launch an application (creating the shm) and unlink the shm while
+        * the session daemon is launching, causing the second attempt
+        * to fail. This is not recovered-from as unlinking the shm will
+        * prevent userspace tracing from succeeding anyhow: the sessiond would
+        * use a now-unlinked shm, while the next application would create
+        * a new named shm.
         */
        wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode);
        if (wait_shm_fd < 0) {
-               PERROR("shm_open wait shm");
-               goto error;
+               if (errno == EACCES) {
+                       /* Work around sysctl fs.protected_regular. */
+                       DBG("shm_open of %s returned EACCES, this may be caused "
+                                       "by the fs.protected_regular sysctl. "
+                                       "Attempting to open the shm without "
+                                       "creating it.", shm_path);
+                       wait_shm_fd = shm_open(shm_path, O_RDWR, mode);
+               }
+               if (wait_shm_fd < 0) {
+                       PERROR("Failed to open wait shm at %s", shm_path);
+                       goto error;
+               }
        }
 
        ret = ftruncate(wait_shm_fd, mmap_size);
        if (ret < 0) {
                PERROR("ftruncate wait shm");
-               exit(EXIT_FAILURE);
+               goto error;
        }
 
 #ifndef __FreeBSD__
@@ -88,7 +106,7 @@ static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
                ret = fchown(wait_shm_fd, 0, 0);
                if (ret < 0) {
                        PERROR("fchown");
-                       exit(EXIT_FAILURE);
+                       goto error;
                }
                /*
                 * If global session daemon, any application can
@@ -99,13 +117,13 @@ static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
                ret = fchmod(wait_shm_fd, mode);
                if (ret < 0) {
                        PERROR("fchmod");
-                       exit(EXIT_FAILURE);
+                       goto error;
                }
        } else {
                ret = fchown(wait_shm_fd, getuid(), getgid());
                if (ret < 0) {
                        PERROR("fchown");
-                       exit(EXIT_FAILURE);
+                       goto error;
                }
        }
 #else
@@ -113,13 +131,20 @@ static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
 #endif
 
        DBG("Got the wait shm fd %d", wait_shm_fd);
-
+end:
+       (void) umask(old_mode);
        return wait_shm_fd;
 
 error:
        DBG("Failing to get the wait shm fd");
+       if (wait_shm_fd >= 0) {
+               if (close(wait_shm_fd)) {
+                       PERROR("Failed to close wait shm file descriptor during error handling");
+               }
+       }
 
-       return -1;
+       wait_shm_fd = -1;
+       goto end;
 }
 
 /*
@@ -132,12 +157,20 @@ error:
  */
 char *shm_ust_get_mmap(char *shm_path, int global)
 {
-       size_t mmap_size = sysconf(_SC_PAGE_SIZE);
+       size_t mmap_size;
        int wait_shm_fd, ret;
        char *wait_shm_mmap;
+       long sys_page_size;
 
        assert(shm_path);
 
+       sys_page_size = sysconf(_SC_PAGE_SIZE);
+       if (sys_page_size < 0) {
+               PERROR("sysconf PAGE_SIZE");
+               goto error;
+       }
+       mmap_size = sys_page_size;
+
        wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
        if (wait_shm_fd < 0) {
                goto error;
This page took 0.026962 seconds and 4 git commands to generate.