Skip to content
Permalink
Browse files

systemd: #1577

  • Loading branch information...
MingcongBai committed Jan 16, 2019
1 parent d966fd2 commit c014b7686c44b70e30d99daffa9bf4597e9f670c
@@ -0,0 +1,201 @@
From c29b44cb90e2cc521533e6169cf847553ebefd81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 5 Dec 2018 18:38:39 +0100
Subject: [PATCH 03/11] journald: do not store the iovec entry for process
commandline on stack

This fixes a crash where we would read the commandline, whose length is under
control of the sending program, and then crash when trying to create a stack
allocation for it.

CVE-2018-16864
https://bugzilla.redhat.com/show_bug.cgi?id=1653855

The message actually doesn't get written to disk, because
journal_file_append_entry() returns -E2BIG.
---
src/basic/io-util.c | 10 ++++++++++
src/basic/io-util.h | 2 ++
src/coredump/coredump.c | 31 +++++++++++--------------------
src/journal/journald-server.c | 25 +++++++++++++++----------
4 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/src/basic/io-util.c b/src/basic/io-util.c
index 1f64cc933b..575398fbe6 100644
--- a/src/basic/io-util.c
+++ b/src/basic/io-util.c
@@ -8,6 +8,7 @@
#include <unistd.h>

#include "io-util.h"
+#include "string-util.h"
#include "time-util.h"

int flush_fd(int fd) {
@@ -252,3 +253,12 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {

return q - (const uint8_t*) p;
}
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
+ char *x;
+
+ x = strappend(field, value);
+ if (x)
+ iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
+ return x;
+}
diff --git a/src/basic/io-util.h b/src/basic/io-util.h
index ed189b5820..792a64ad5e 100644
--- a/src/basic/io-util.h
+++ b/src/basic/io-util.h
@@ -71,3 +71,5 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len)
#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 1d39f5338d..771df01d1e 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -1063,19 +1063,10 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
return 0;
}

-static char* set_iovec_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
- char *x;
-
- x = strappend(field, value);
- if (x)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
- return x;
-}
-
static char* set_iovec_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
char *x;

- x = set_iovec_field(iovec, n_iovec, field, value);
+ x = set_iovec_string_field(iovec, n_iovec, field, value);
free(value);
return x;
}
@@ -1125,36 +1116,36 @@ static int gather_pid_metadata(
disable_coredumps();
}

- set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
+ set_iovec_string_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
}

if (cg_pid_get_user_unit(pid, &t) >= 0)
set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t);

/* The next few are mandatory */
- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]))
return log_oom();

- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
+ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
return log_oom();

if (context[CONTEXT_EXE] &&
- !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
+ !set_iovec_string_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
return log_oom();

if (sd_pid_get_session(pid, &t) >= 0)
@@ -1222,7 +1213,7 @@ static int gather_pid_metadata(
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);

if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo))
- set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
+ set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));

return 0; /* we successfully acquired all metadata */
}
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index f09672531c..2a960ebb3e 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -905,6 +905,7 @@ static void dispatch_message_real(
pid_t object_pid) {

char source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
+ _cleanup_free_ char *cmdline1 = NULL, *cmdline2 = NULL;
uid_t journal_uid;
ClientContext *o;

@@ -921,20 +922,23 @@ static void dispatch_message_real(
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->uid, uid_t, uid_is_valid, UID_FMT, "_UID");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->gid, gid_t, gid_is_valid, GID_FMT, "_GID");

- IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM");
- IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE");
- IOVEC_ADD_STRING_FIELD(iovec, n, c->cmdline, "_CMDLINE");
- IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE");
+ IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM"); /* At most TASK_COMM_LENGTH (16 bytes) */
+ IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE"); /* A path, so at most PATH_MAX (4096 bytes) */

- IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
+ if (c->cmdline)
+ /* At most _SC_ARG_MAX (2MB usually), which is too much to put on stack.
+ * Let's use a heap allocation for this one. */
+ cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline);

+ IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */
+ IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");

- IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP");
+ IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP"); /* A path */
IOVEC_ADD_STRING_FIELD(iovec, n, c->session, "_SYSTEMD_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->owner_uid, uid_t, uid_is_valid, UID_FMT, "_SYSTEMD_OWNER_UID");
- IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT");
+ IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT"); /* Unit names are bounded by UNIT_NAME_MAX */
IOVEC_ADD_STRING_FIELD(iovec, n, c->user_unit, "_SYSTEMD_USER_UNIT");
IOVEC_ADD_STRING_FIELD(iovec, n, c->slice, "_SYSTEMD_SLICE");
IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE");
@@ -955,13 +959,14 @@ static void dispatch_message_real(
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_UID");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->gid, gid_t, gid_is_valid, GID_FMT, "OBJECT_GID");

+ /* See above for size limits, only ->cmdline may be large, so use a heap allocation for it. */
IOVEC_ADD_STRING_FIELD(iovec, n, o->comm, "OBJECT_COMM");
IOVEC_ADD_STRING_FIELD(iovec, n, o->exe, "OBJECT_EXE");
- IOVEC_ADD_STRING_FIELD(iovec, n, o->cmdline, "OBJECT_CMDLINE");
- IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
+ if (o->cmdline)
+ cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline);

+ IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");
-
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");

--
2.19.2

@@ -0,0 +1,46 @@
From 4489ac6683386805742f7ee678cb8580d669556b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 5 Dec 2018 22:45:02 +0100
Subject: [PATCH 06/11] journald: set a limit on the number of fields (1k)

We allocate a iovec entry for each field, so with many short entries,
our memory usage and processing time can be large, even with a relatively
small message size. Let's refuse overly long entries.

CVE-2018-16865
https://bugzilla.redhat.com/show_bug.cgi?id=1653861

What from I can see, the problem is not from an alloca, despite what the CVE
description says, but from the attack multiplication that comes from creating
many very small iovecs: (void* + size_t) for each three bytes of input message.
---
src/journal/journald-native.c | 5 +++++
src/shared/journal-importer.h | 3 +++
2 files changed, 8 insertions(+)

--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -140,6 +140,11 @@
}

/* A property follows */
+ if (n > ENTRY_FIELD_COUNT_MAX) {
+ log_debug("Received an entry that has more than " STRINGIFY(ENTRY_FIELD_COUNT_MAX) " fields, ignoring entry.");
+ r = 1;
+ goto finish;
+ }

/* n existing properties, 1 new, +1 for _TRANSPORT */
if (!GREEDY_REALLOC(iovec, m,
--- a/src/basic/journal-importer.h
+++ b/src/basic/journal-importer.h
@@ -16,6 +16,9 @@
#define DATA_SIZE_MAX (1024*1024*768u)
#define LINE_CHUNK 8*1024u

+/* The maximum number of fields in an entry */
+#define ENTRY_FIELD_COUNT_MAX 1024
+
struct iovec_wrapper {
struct iovec *iovec;
size_t size_bytes;
@@ -0,0 +1,71 @@
From ce1475b4f69f0a4382c6190f55e080d91de84611 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 7 Dec 2018 10:48:10 +0100
Subject: [PATCH 11/11] journal-remote: set a limit on the number of fields in
a message

Existing use of E2BIG is replaced with ENOBUFS (entry too long), and E2BIG is
reused for the new error condition (too many fields).

This matches the change done for systemd-journald, hence forming the second
part of the fix for CVE-2018-16865
(https://bugzilla.redhat.com/show_bug.cgi?id=1653861).
---
src/journal-remote/journal-remote-main.c | 7 +++++--
src/journal-remote/journal-remote.c | 3 +++
src/shared/journal-importer.c | 5 ++++-
3 files changed, 12 insertions(+), 3 deletions(-)

--- a/src/basic/journal-importer.c
+++ b/src/basic/journal-importer.c
@@ -23,6 +23,9 @@
};

static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
+ if (iovw->count >= ENTRY_FIELD_COUNT_MAX)
+ return -E2BIG;
+
if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
return log_oom();

@@ -98,7 +101,7 @@
imp->scanned = imp->filled;
if (imp->scanned >= DATA_SIZE_MAX) {
log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
- return -E2BIG;
+ return -ENOBUFS;
}

if (imp->passive_fd)
--- a/src/journal-remote/journal-remote-main.c
+++ b/src/journal-remote/journal-remote-main.c
@@ -212,10 +212,16 @@
break;
else if (r < 0) {
log_warning("Failed to process data for connection %p", connection);
- if (r == -E2BIG)
+ if (r == -ENOBUFS)
return mhd_respondf(connection,
r, MHD_HTTP_PAYLOAD_TOO_LARGE,
"Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
+
+ else if (r == -E2BIG)
+ return mhd_respondf(connection,
+ r, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ "Entry with more fields than the maximum of " STRINGIFY(ENTRY_FIELD_COUNT_MAX) ".");
+
else
return mhd_respondf(connection,
r, MHD_HTTP_UNPROCESSABLE_ENTITY,
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -408,6 +408,9 @@
log_debug("%zu active sources remaining", s->active);
return 0;
} else if (r == -E2BIG) {
+ log_notice("Entry with too many fields, skipped");
+ return 1;
+ } else if (r == -ENOBUFS) {
log_notice_errno(E2BIG, "Entry too big, skipped");
return 1;
} else if (r == -EAGAIN) {
@@ -0,0 +1,60 @@
From a6aadf4ae0bae185dc4c414d492a4a781c80ffe5 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 8 Aug 2018 15:06:36 +0900
Subject: [PATCH] journal: fix syslog_parse_identifier()

Fixes #9829.
---
src/journal/journald-syslog.c | 6 +++---
src/journal/test-journal-syslog.c | 10 ++++++++--
2 files changed, 11 insertions(+), 5 deletions(-)

--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -194,7 +194,7 @@
e = l;
l--;

- if (p[l-1] == ']') {
+ if (l > 0 && p[l-1] == ']') {
size_t k = l-1;

for (;;) {
@@ -219,8 +219,8 @@
if (t)
*identifier = t;

- if (strchr(WHITESPACE, p[e]))
- e++;
+ e += strspn(p + e, WHITESPACE);
+
*buf = p + e;
return e;
}
--- a/src/journal/test-journal-syslog.c
+++ b/src/journal/test-journal-syslog.c
@@ -6,8 +6,8 @@
#include "string-util.h"
#include "syslog-util.h"

-static void test_syslog_parse_identifier(const char* str,
- const char *ident, const char*pid, int ret) {
+static void test_syslog_parse_identifier(const char *str,
+ const char *ident, const char *pid, int ret) {
const char *buf = str;
_cleanup_free_ char *ident2 = NULL, *pid2 = NULL;
int ret2;
@@ -33,7 +33,13 @@
int main(void) {
test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11);
test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6);
+ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 7);
test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0);
+ test_syslog_parse_identifier(":", "", NULL, 1);
+ test_syslog_parse_identifier(": ", "", NULL, 3);
+ test_syslog_parse_identifier("pidu:", "pidu", NULL, 5);
+ test_syslog_parse_identifier("pidu: ", "pidu", NULL, 6);
+ test_syslog_parse_identifier("pidu : ", NULL, NULL, 0);

test_syslog_parse_priority("<>", 0, 0);
test_syslog_parse_priority("<>aaa", 0, 0);
@@ -1,3 +1,3 @@
VER=239
REL=4
REL=5
SRCTBL="https://github.com/systemd/systemd/archive/v$VER.tar.gz"

0 comments on commit c014b76

Please sign in to comment.
You can’t perform that action at this time.