[systemd-devel] [PATCH] Fix strerror_r segfault on non-glibc systems

Ioan-Adrian Ratiu adi at adirat.com
Thu Mar 29 11:41:30 UTC 2018


POSIX strerror_r returns an int while the glibc "extension" returns a char* and
this causes segfaults when running on systems with libc's like musl which only
implement the portable version or deliberately don't provide a flag to identify
compiling using their headers.

Glibc provides the POSIX variant of the function, but only if _GNU_SOURCE is
not set i.e. all gnu extensions are disabled. Meson sets _GNU_SOURCE globally
at build time.

So detect during build if we have the char* version and #ifdef the logic.

Signed-off-by: Ioan-Adrian Ratiu <adi at adirat.com>
---
 meson.build                       | 13 +++++++++++++
 src/journal/journal-send.c        |  9 +++++++++
 src/libsystemd/sd-bus/bus-error.c |  9 +++++++++
 3 files changed, 31 insertions(+)

diff --git a/meson.build b/meson.build
index b53dfaa94..fe807237f 100644
--- a/meson.build
+++ b/meson.build
@@ -383,6 +383,19 @@ if cc.compiles('''
         add_project_arguments('-Werror=shadow', language : 'c')
 endif
 
+if cc.compiles('
+   #include <string.h>
+   int func (void) {
+           char error_string[256];
+           char *ptr = strerror_r(-2, error_string, 256);
+           char c = *strerror_r(-2, error_string, 256);
+           return c != 0 && ptr != (void*) 0L;
+   }
+', name : 'strerror_r() returns char *', args : '-D_GNU_SOURCE')
+        conf.set('STRERROR_R_CHAR_P', 1,
+                  description: 'Defined if strerror_r returns char *')
+endif
+
 if cc.get_id() == 'clang'
         foreach arg : ['-Wno-typedef-redefinition',
                        '-Wno-gnu-variable-sized-type-not-at-end',
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index b6adf64c2..9b33c6c3b 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -355,7 +355,16 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove
                 char* j;
 
                 errno = 0;
+
+#ifdef STRERROR_R_CHAR_P
                 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
+#else
+                j = buffer + 8 + k;
+                int ret = strerror_r(_saved_errno_, j, n - 8 - k);
+                if (ret)
+                        errno = ret;
+#endif
+
                 if (errno == 0) {
                         char error[STRLEN("ERRNO=") + DECIMAL_STR_MAX(int) + 1];
 
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 66a09a35f..10390f0d8 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -378,7 +378,16 @@ static void bus_error_strerror(sd_bus_error *e, int error) {
                         return;
 
                 errno = 0;
+
+#ifdef STRERROR_R_CHAR_P
                 x = strerror_r(error, m, k);
+#else
+                int ret = strerror_r(error, m, k);
+                if (ret)
+                        errno = ret;
+                x = m;
+#endif
+
                 if (errno == ERANGE || strlen(x) >= k - 1) {
                         free(m);
                         k *= 2;
-- 
2.16.3



More information about the systemd-devel mailing list