diff --git a/src/netlog/netlog-manager.c b/src/netlog/netlog-manager.c index 1cffe37..8ac9289 100644 --- a/src/netlog/netlog-manager.c +++ b/src/netlog/netlog-manager.c @@ -46,38 +46,81 @@ static const char *const log_format_table[_SYSLOG_TRANSMISSION_LOG_FORMAT_MAX] = DEFINE_STRING_TABLE_LOOKUP(log_format, int); -static int parse_field(const void *data, size_t length, const char *field, char **target) { - size_t fl, nl; - void *buf; +typedef struct ParseFieldVec { + const char *field; + size_t field_len; + char **target; + size_t *target_len; +} ParseFieldVec; + +#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) { \ + .field = _field, \ + .field_len = strlen(_field), \ + .target = _target, \ + .target_len = _target_len \ + } + +static int parse_field( + const void *data, + size_t length, + const char *field, + size_t field_len, + char **target, + size_t *target_len) { + + size_t nl; + char *buf; assert(data); assert(field); assert(target); - fl = strlen(field); - if (length < fl) + if (length < field_len) return 0; - if (memcmp(data, field, fl)) + if (memcmp(data, field, field_len)) return 0; - nl = length - fl; - buf = malloc(nl+1); + nl = length - field_len; + + buf = newdup_suffix0(char, (const char*) data + field_len, nl); if (!buf) - return -ENOMEM; + return log_oom(); - memcpy(buf, (const char*) data + fl, nl); - ((char*)buf)[nl] = 0; + free_and_replace(*target, buf); - free(*target); - *target = buf; + if (target_len) + *target_len = nl; return 1; } +static int parse_fieldv( + const void *data, + size_t length, + const ParseFieldVec *fields, + size_t n_fields) { + + int r; + + for (size_t i = 0; i < n_fields; i++) { + const ParseFieldVec *f = &fields[i]; + + r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len); + if (r < 0) + return r; + if (r > 0) + break; + } + + return 0; +} + static int manager_read_journal_input(Manager *m) { _cleanup_free_ char *facility = NULL, *identifier = NULL, *priority = NULL, *message = NULL, *pid = NULL, *hostname = NULL, *structured_data = NULL, *msgid = NULL; + size_t hostname_len = 0, identifier_len = 0, message_len = 0, priority_len = 0, facility_len = 0, + structured_data_len = 0, msgid_len = 0, pid_len = 0; unsigned sev = JOURNAL_DEFAULT_SEVERITY; unsigned fac = JOURNAL_DEFAULT_FACILITY; struct timeval tv; @@ -86,6 +129,16 @@ static int manager_read_journal_input(Manager *m) { size_t length; char *cursor; int r; + const ParseFieldVec fields[] = { + PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len ), + PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len ), + PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len ), + PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len ), + PARSE_FIELD_VEC_ENTRY("SYSLOG_FACILITY=", &facility, &facility_len ), + PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len ), + PARSE_FIELD_VEC_ENTRY("SYSLOG_STRUCTURED_DATA=", &structured_data, &structured_data_len ), + PARSE_FIELD_VEC_ENTRY("SYSLOG_MSGID", &msgid, &msgid_len ), + }; assert(m); assert(m->journal); @@ -97,50 +150,21 @@ static int manager_read_journal_input(Manager *m) { log_debug("Reading from journal cursor=%s", cursor); JOURNAL_FOREACH_DATA_RETVAL(m->journal, data, length, r) { - - r = parse_field(data, length, "PRIORITY=", &priority); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_FACILITY=", &facility); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_HOSTNAME=", &hostname); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_PID=", &pid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "MESSAGE=", &message); + r = parse_fieldv(data, length, fields, ELEMENTSOF(fields)); if (r < 0) return r; + } - r = parse_field(data, length, "SYSLOG_STRUCTURED_DATA=", &structured_data); - if (r < 0) - return r; - else if (r > 0) - continue; + if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to get journal fields: %m"); - r = parse_field(data, length, "SYSLOG_MSGID=", &msgid); - if (r < 0) - return r; + if (!message) { + log_debug("Skipping message without MESSAGE= field."); + return 0; } r = sd_journal_get_realtime_usec(m->journal, &realtime); diff --git a/src/share/alloc-util.c b/src/share/alloc-util.c index 51878b6..fd5f5ec 100644 --- a/src/share/alloc-util.c +++ b/src/share/alloc-util.c @@ -20,6 +20,24 @@ void* memdup(const void *p, size_t l) { return r; } +void* memdup_suffix0(const void *p, size_t l) { + void *ret; + + assert(l == 0 || p); + + /* The same as memdup() but place a safety NUL byte after the allocated memory */ + + if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */ + return NULL; + + ret = malloc(l + 1); + if (!ret) + return NULL; + + ((uint8_t*) ret)[l] = 0; + return memcpy_safe(ret, p, l); +} + void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { size_t a, newalloc; void *q; diff --git a/src/share/alloc-util.h b/src/share/alloc-util.h index 12fc36f..def71a8 100644 --- a/src/share/alloc-util.h +++ b/src/share/alloc-util.h @@ -8,6 +8,33 @@ #include "macro.h" +/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */ +static inline void *memcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return dst; + assert(src); + return memcpy(dst, src, n); +} + +/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */ +static inline void *mempcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return dst; + assert(src); + return mempcpy(dst, src, n); +} + +/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */ +static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { + if (n == 0) + return 0; + assert(s1); + assert(s2); + return memcmp(s1, s2, n); +} + +#define zero(x) (memzero(&(x), sizeof(x))) + #define new(t, n) ((t*) malloc_multiply(n, sizeof(t))) #define new0(t, n) ((t*) calloc((n), sizeof(t))) @@ -25,14 +52,17 @@ static inline void *mfree(void *memory) { return NULL; } -void* memdup(const void *p, size_t l) _alloc_(2); - static inline void freep(void *p) { free(*(void**) p); } #define _cleanup_free_ _cleanup_(freep) +void* memdup(const void *p, size_t l) _alloc_(2); +void* memdup_suffix0(const void *p, size_t l); + +#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t))) + static inline bool size_multiply_overflow(size_t size, size_t need) { return _unlikely_(need != 0 && size > (SIZE_MAX / need)); } @@ -58,6 +88,15 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si return memdup(p, size * need); } +/* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger + * than the product of its parameters. */ +static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) { + if (size_multiply_overflow(size, need)) + return NULL; + + return memdup_suffix0(p, size * need); +} + void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); @@ -91,3 +130,16 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); _new_ = alloca_align(_size_, (align)); \ (void*)memset(_new_, 0, _size_); \ }) + +#define free_and_replace_full(a, b, free_func) \ + ({ \ + typeof(a)* _a = &(a); \ + typeof(b)* _b = &(b); \ + free_func(*_a); \ + *_a = *_b; \ + *_b = NULL; \ + 0; \ + }) + +#define free_and_replace(a, b) \ + free_and_replace_full(a, b, free) diff --git a/src/share/util.h b/src/share/util.h index b582155..3978572 100644 --- a/src/share/util.h +++ b/src/share/util.h @@ -70,18 +70,12 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_ qsort(base, nmemb, size, compar); } -/** - * Normal memcpy requires src to be nonnull. We do nothing if n is 0. - */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} +#define memzero(x, l) \ + ({ \ + size_t _l_ = (l); \ + _l_ > 0 ? memset((x), 0, _l_) : (x); \ + }) -#define memzero(x,l) (memset((x), 0, (l))) -#define zero(x) (memzero(&(x), sizeof(x))) static inline void *mempset(void *s, int c, size_t n) { memset(s, c, n);