diff --git a/src/journal/journald-file.c b/src/journal/journald-file.c index 4e095acc93..d4765b98df 100644 --- a/src/journal/journald-file.c +++ b/src/journal/journald-file.c @@ -23,7 +23,7 @@ static int journald_file_truncate(JournalFile *f) { int r; /* truncate excess from the end of archives */ - r = journal_file_tail_end(f, &p); + r = journal_file_tail_end_by_pread(f, &p); if (r < 0) return log_debug_errno(r, "Failed to determine end of tail object: %m"); @@ -45,7 +45,7 @@ static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint return 0; for (uint64_t q = p; q != 0; q = le64toh(o.entry_array.next_entry_array_offset)) { - r = journal_file_read_object(f, OBJECT_ENTRY_ARRAY, q, &o); + r = journal_file_read_object_header(f, OBJECT_ENTRY_ARRAY, q, &o); if (r < 0) return r; @@ -119,7 +119,7 @@ static int journald_file_punch_holes(JournalFile *f) { for (uint64_t q = le64toh(items[j].head_hash_offset); q != 0; q = le64toh(o.data.next_hash_offset)) { - r = journal_file_read_object(f, OBJECT_DATA, q, &o); + r = journal_file_read_object_header(f, OBJECT_DATA, q, &o); if (r < 0) { log_debug_errno(r, "Invalid data object: %m, ignoring"); break; diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index dc866b8905..67eb77be23 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -91,8 +91,7 @@ # pragma GCC diagnostic ignored "-Waddress-of-packed-member" #endif -int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { - Object tail; +int journal_file_tail_end_by_pread(JournalFile *f, uint64_t *ret_offset) { uint64_t p; int r; @@ -100,13 +99,17 @@ int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { assert(f->header); assert(ret_offset); + /* Same as journal_file_tail_end_by_mmap() below, but operates with pread() to avoid the mmap cache + * (and thus is thread safe) */ + p = le64toh(f->header->tail_object_offset); if (p == 0) p = le64toh(f->header->header_size); else { + Object tail; uint64_t sz; - r = journal_file_read_object(f, OBJECT_UNUSED, p, &tail); + r = journal_file_read_object_header(f, OBJECT_UNUSED, p, &tail); if (r < 0) return r; @@ -126,6 +129,43 @@ int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { return 0; } +int journal_file_tail_end_by_mmap(JournalFile *f, uint64_t *ret_offset) { + uint64_t p; + int r; + + assert(f); + assert(f->header); + assert(ret_offset); + + /* Same as journal_file_tail_end_by_pread() above, but operates with the usual mmap logic */ + + p = le64toh(f->header->tail_object_offset); + if (p == 0) + p = le64toh(f->header->header_size); + else { + Object *tail; + uint64_t sz; + + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); + if (r < 0) + return r; + + sz = le64toh(READ_NOW(tail->object.size)); + if (sz > UINT64_MAX - sizeof(uint64_t) + 1) + return -EBADMSG; + + sz = ALIGN64(sz); + if (p > UINT64_MAX - sz) + return -EBADMSG; + + p += sz; + } + + *ret_offset = p; + + return 0; +} + int journal_file_set_offline_thread_join(JournalFile *f) { int r; @@ -818,10 +858,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset return 0; } -int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, Object *ret) { - int r; - Object o; +int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t offset, Object *ret) { uint64_t s; + ssize_t n; + Object o; + int r; assert(f); @@ -838,17 +879,22 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O offset); /* This will likely read too much data but it avoids having to call pread() twice. */ - r = pread(f->fd, &o, sizeof(Object), offset); - if (r < 0) - return r; + n = pread(f->fd, &o, sizeof(o), offset); + if (n < 0) + return log_debug_errno(errno, "Failed to read journal file at offset: %" PRIu64, + offset); + + if ((size_t) n < sizeof(o.object)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to read short object at offset: %" PRIu64, + offset); s = le64toh(o.object.size); - if (s == 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read uninitialized object: %" PRIu64, offset); - if (s < sizeof(ObjectHeader)) + if (s < sizeof(o.object)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read overly short object: %" PRIu64, offset); @@ -863,6 +909,11 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O "Attempt to read truncated object: %" PRIu64, offset); + if ((size_t) n < minimum_header_size(&o)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Short read while reading object: %" PRIu64, + offset); + if (type > OBJECT_UNUSED && o.object.type != type) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read object of unexpected type: %" PRIu64, @@ -930,7 +981,7 @@ int journal_file_append_object( if (r < 0) return r; - r = journal_file_tail_end(f, &p); + r = journal_file_tail_end_by_mmap(f, &p); if (r < 0) return r; diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h index f673e05e72..59509deec9 100644 --- a/src/libsystemd/sd-journal/journal-file.h +++ b/src/libsystemd/sd-journal/journal-file.h @@ -185,9 +185,10 @@ static inline bool VALID_EPOCH(uint64_t u) { FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_KEYED_HASH) int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); -int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, Object *ret); +int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t offset, Object *ret); -int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset); +int journal_file_tail_end_by_pread(JournalFile *f, uint64_t *ret_offset); +int journal_file_tail_end_by_mmap(JournalFile *f, uint64_t *ret_offset); uint64_t journal_file_entry_n_items(Object *o) _pure_; uint64_t journal_file_entry_array_n_items(Object *o) _pure_;