summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2014-01-25 23:35:28 -0500
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2014-01-27 23:17:02 -0500
commit65b3903ff576488eaabb51d3c4fbf9c73d867d7c (patch)
tree0343e9d79057f6ca67acb87d6cc06f45ab7a1384
parent8e33886ec582336564ae11b80023abe93d7599c0 (diff)
journal: guarantee async-signal-safety in sd_journald_sendv
signal(7) provides a list of functions which may be called from a signal handler. Other functions, which only call those functions and don't access global memory and are reentrant are also safe. sd_j_sendv was mostly OK, but would call mkostemp and writev in a fallback path, which are unsafe. Being able to call sd_j_sendv in a async-signal-safe way is important because it allows it be used in signal handlers. Safety is achieved by replacing mkostemp with open(O_TMPFILE) and an open-coded writev replacement which uses write. Unfortunately, O_TMPFILE is only available on kernels >= 3.11. When O_TMPFILE is unavailable, an open-coded mkostemp is used. https://bugzilla.gnome.org/show_bug.cgi?id=722889
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am7
-rw-r--r--man/sd_journal_print.xml31
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/shared/def.h1
-rw-r--r--src/shared/missing.h16
-rw-r--r--src/shared/util.c54
-rw-r--r--src/shared/util.h3
-rw-r--r--src/test/test-tmpfiles.c51
-rw-r--r--src/test/test-util.c26
10 files changed, 178 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
index 36b91b4a7d..271f225465 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,7 +33,7 @@
/hostnamectl
/install-tree
/journalctl
-/libsystemd-login.c
+/libsystemd-*.c
/libtool
/localectl
/loginctl
@@ -184,6 +184,7 @@
/test-strxcpyx
/test-tables
/test-time
+/test-tmpfiles
/test-udev
/test-unit-file
/test-unit-name
diff --git a/Makefile.am b/Makefile.am
index 23f7d2fba6..49ac55fee1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1126,6 +1126,7 @@ tests += \
test-utf8 \
test-ellipsize \
test-util \
+ test-tmpfiles \
test-namespace \
test-date \
test-sleep \
@@ -1232,6 +1233,12 @@ test_util_SOURCES = \
test_util_LDADD = \
libsystemd-core.la
+test_tmpfiles_SOURCES = \
+ src/test/test-tmpfiles.c
+
+test_tmpfiles_LDADD = \
+ libsystemd-shared.la
+
test_namespace_SOURCES = \
src/test/test-namespace.c
diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml
index a716cc35e6..57d908fe22 100644
--- a/man/sd_journal_print.xml
+++ b/man/sd_journal_print.xml
@@ -153,8 +153,8 @@
for details) instead of the format string. Each
structure should reference one field of the entry to
submit. The second argument specifies the number of
- structures in the
- array. <function>sd_journal_sendv()</function> is
+ structures in the array.
+ <function>sd_journal_sendv()</function> is
particularly useful to submit binary objects to the
journal where that is necessary.</para>
@@ -221,6 +221,19 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
</refsect1>
<refsect1>
+ <title>Async signal safety</title>
+ <para><function>sd_journal_sendv()</function> is "async signal
+ safe" in the meaning of <citerefentry><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
+
+ <para><function>sd_journal_print</function>,
+ <function>sd_journal_printv</function>,
+ <function>sd_journal_send</function>, and
+ <function>sd_journal_perror</function> are
+ not async signal safe.</para>
+ </refsect1>
+
+ <refsect1>
<title>Notes</title>
<para>The <function>sd_journal_print()</function>,
@@ -234,6 +247,16 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
</refsect1>
<refsect1>
+ <title>History</title>
+
+ <para><function>sd_journal_sendv()</function> was
+ modified to guarantee async-signal-safety in
+ systemd-209. Before that, it would behave safely only
+ when entry size was small enough to fit in one (large)
+ datagram.</para>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
@@ -243,7 +266,9 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
<citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>perror</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index ca9199f718..960c5776b0 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -314,7 +314,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
if (buffer_fd < 0)
return buffer_fd;
- n = writev(buffer_fd, w, j);
+ n = writev_safe(buffer_fd, w, j);
if (n < 0) {
close_nointr_nofail(buffer_fd);
return -errno;
diff --git a/src/shared/def.h b/src/shared/def.h
index a2304fddda..7777756aad 100644
--- a/src/shared/def.h
+++ b/src/shared/def.h
@@ -44,6 +44,7 @@
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
+#define ALPHANUMERICAL LETTERS DIGITS
#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
diff --git a/src/shared/missing.h b/src/shared/missing.h
index 6c038d9f08..4e62100030 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -301,25 +301,29 @@ static inline int name_to_handle_at(int fd, const char *name, struct file_handle
#endif
#ifndef CIFS_MAGIC_NUMBER
-#define CIFS_MAGIC_NUMBER 0xFF534D42
+# define CIFS_MAGIC_NUMBER 0xFF534D42
#endif
#ifndef TFD_TIMER_CANCEL_ON_SET
-#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+# define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
#ifndef SO_REUSEPORT
-#define SO_REUSEPORT 15
+# define SO_REUSEPORT 15
#endif
#ifndef EVIOCREVOKE
-#define EVIOCREVOKE _IOW('E', 0x91, int)
+# define EVIOCREVOKE _IOW('E', 0x91, int)
#endif
#ifndef DRM_IOCTL_SET_MASTER
-#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
+# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
#endif
#ifndef DRM_IOCTL_DROP_MASTER
-#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
+# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
+#endif
+
+#ifndef TMP_MAX
+# define TMP_MAX 238328
#endif
diff --git a/src/shared/util.c b/src/shared/util.c
index 27fc9594cd..a437d9b127 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -6109,6 +6109,53 @@ int getpeersec(int fd, char **ret) {
return 0;
}
+int writev_safe(int fd, const struct iovec *w, int j) {
+ for (int i = 0; i < j; i++) {
+ size_t written = 0;
+
+ while (written < w[i].iov_len) {
+ ssize_t r;
+
+ r = write(fd, (char*) w[i].iov_base + written, w[i].iov_len - written);
+ if (r < 0 && errno != -EINTR)
+ return -errno;
+
+ written += r;
+ }
+ }
+
+ return 0;
+}
+
+int mkostemp_safe(char *pattern, int flags) {
+ char *s = pattern + strlen(pattern) - 6;
+ uint64_t tries = TMP_MAX;
+ int randfd, fd, i;
+
+ assert(streq(s, "XXXXXX"));
+
+ randfd = open("/dev/urandom", O_RDONLY);
+ if (randfd < 0)
+ return -ENOSYS;
+
+ while (tries--) {
+ fd = read(randfd, s, 6);
+ if (fd == 0)
+ return -ENOSYS;
+
+ for (i = 0; i < 6; i++)
+ s[i] = ALPHANUMERICAL[(unsigned) s[i] % strlen(ALPHANUMERICAL)];
+
+ fd = open(pattern, flags|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ return fd;
+ if (!IN_SET(errno, EEXIST, EINTR))
+ return -errno;
+ }
+
+ return -EEXIST;
+}
+
int open_tmpfile(const char *path, int flags) {
int fd;
char *p;
@@ -6120,12 +6167,9 @@ int open_tmpfile(const char *path, int flags) {
#endif
p = strappenda(path, "/systemd-tmp-XXXXXX");
- RUN_WITH_UMASK(0077) {
- fd = mkostemp(p, O_RDWR|O_CLOEXEC);
- }
-
+ fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC);
if (fd < 0)
- return -errno;
+ return fd;
unlink(p);
return fd;
diff --git a/src/shared/util.h b/src/shared/util.h
index 630137a53a..1169864c3a 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -850,4 +850,7 @@ bool pid_valid(pid_t pid);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
+int writev_safe(int fd, const struct iovec *w, int j);
+
+int mkostemp_safe(char *pattern, int flags);
int open_tmpfile(const char *path, int flags);
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
new file mode 100644
index 0000000000..f25a0dca52
--- /dev/null
+++ b/src/test/test-tmpfiles.c
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "util.h"
+
+int main(int argc, char** argv) {
+ const char *p = argv[1] ?: "/tmp";
+ char *pattern = strappenda(p, "/systemd-test-XXXXXX");
+ _cleanup_close_ int fd, fd2;
+ _cleanup_free_ char *cmd, *cmd2;
+
+ fd = open_tmpfile(p, O_RDWR);
+ assert(fd >= 0);
+
+ assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
+ system(cmd);
+
+ fd2 = mkostemp_safe(pattern, O_RDWR);
+ assert(fd >= 0);
+ assert_se(unlink(pattern) == 0);
+
+ assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0);
+ system(cmd2);
+
+ return 0;
+}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 05b2294e24..f819589b52 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -28,6 +28,8 @@
#include "util.h"
#include "strv.h"
+#include "def.h"
+#include "fileio.h"
static void test_streq_ptr(void) {
assert_se(streq_ptr(NULL, NULL));
@@ -578,6 +580,29 @@ static void test_in_set(void) {
assert_se(!IN_SET(0, 1, 2, 3, 4));
}
+static void test_writev_safe(void) {
+ char name[] = "/tmp/test-writev_safe.XXXXXX";
+ _cleanup_free_ char *contents;
+ size_t size;
+ int fd, r;
+
+ struct iovec iov[3];
+ IOVEC_SET_STRING(iov[0], "abc\n");
+ IOVEC_SET_STRING(iov[1], ALPHANUMERICAL "\n");
+ IOVEC_SET_STRING(iov[2], "");
+
+ fd = mkstemp(name);
+ printf("test_writev_safe: %s", name);
+
+ r = writev_safe(fd, iov, 3);
+ assert(r == 0);
+
+ r = read_full_file(name, &contents, &size);
+ assert(r == 0);
+ printf("contents: %s", contents);
+ assert(streq(contents, "abc\n" ALPHANUMERICAL "\n"));
+}
+
int main(int argc, char *argv[]) {
test_streq_ptr();
test_first_word();
@@ -615,6 +640,7 @@ int main(int argc, char *argv[]) {
test_fstab_node_to_udev_node();
test_get_files_in_directory();
test_in_set();
+ test_writev_safe();
return 0;
}