diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index c9ff6abca6..64bf8ef9af 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -3047,7 +3047,7 @@ static int generic_array_bisect( left = 0; right = m - 1; - if (direction == DIRECTION_UP) { + if (direction == DIRECTION_UP && left < right) { /* If we're going upwards, the last entry of the previous array may pass the test, * and the first entry of the current array may not pass. In that case, the last * entry of the previous array must be returned. Hence, we need to test the first @@ -3162,10 +3162,21 @@ previous: if (direction == DIRECTION_DOWN) return 0; - /* Indicate to go to the previous array later. Note, do not move to the previous array here, - * as that may invalidate the current array object in the mmap cache and - * journal_file_entry_array_item() below may read invalid address. */ - i = UINT64_MAX; + /* Get the last entry of the previous array. */ + r = bump_entry_array(f, NULL, a, first, DIRECTION_UP, &a); + if (r <= 0) + return r; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); + if (r < 0) + return r; + + p = journal_file_entry_array_n_items(f, array); + if (p == 0 || t < p) + return -EBADMSG; + + t -= p; + i = p - 1; found: p = journal_file_entry_array_item(f, array, 0); @@ -3175,27 +3186,6 @@ found: /* Let's cache this item for the next invocation */ chain_cache_put(f->chain_cache, ci, first, a, p, t, i); - if (i == UINT64_MAX) { - uint64_t m; - - /* Get the last entry of the previous array. */ - - r = bump_entry_array(f, NULL, a, first, DIRECTION_UP, &a); - if (r <= 0) - return r; - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); - if (r < 0) - return r; - - m = journal_file_entry_array_n_items(f, array); - if (m == 0 || t < m) - return -EBADMSG; - - t -= m; - i = m - 1; - } - p = journal_file_entry_array_item(f, array, i); if (p == 0) return -EBADMSG; @@ -3549,7 +3539,8 @@ int journal_file_next_entry( p, test_object_offset, direction, - ret_object ? &o : NULL, &q, &i); + NULL, &q, &i); /* Here, do not read entry object, as the result object + * may not be the one we want, and it may be broken. */ if (r <= 0) return r; @@ -3559,12 +3550,11 @@ int journal_file_next_entry( * the same offset, and the index needs to be shifted. Otherwise, use the found object as is, * as it is the nearest entry object from the input offset 'p'. */ - if (p != q) - goto found; - - r = bump_array_index(&i, direction, n); - if (r <= 0) - return r; + if (p == q) { + r = bump_array_index(&i, direction, n); + if (r <= 0) + return r; + } /* And jump to it */ r = generic_array_get(f, le64toh(f->header->entry_array_offset), i, direction, ret_object ? &o : NULL, &q); @@ -3576,7 +3566,7 @@ int journal_file_next_entry( return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: entry array not properly ordered at entry index %" PRIu64, f->path, i); -found: + if (ret_object) *ret_object = o; if (ret_offset) diff --git a/src/libsystemd/sd-journal/test-journal-interleaving.c b/src/libsystemd/sd-journal/test-journal-interleaving.c index 8aeef8f607..93c2c4aaca 100644 --- a/src/libsystemd/sd-journal/test-journal-interleaving.c +++ b/src/libsystemd/sd-journal/test-journal-interleaving.c @@ -567,7 +567,41 @@ static int expected_result(uint64_t needle, const uint64_t *candidates, const ui } } -static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offset, size_t n) { +static int expected_result_next(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) { + switch (direction) { + case DIRECTION_DOWN: + for (size_t i = 0; i < n; i++) + if (needle < offset[i]) { + *ret = candidates[i]; + return candidates[i] > 0; + } + *ret = 0; + return 0; + + case DIRECTION_UP: + for (size_t i = 0; i < n; i++) + if (needle <= offset[i]) { + n = i; + break; + } + + for (; n > 0 && candidates[n - 1] == 0; n--) + ; + + if (n == 0) { + *ret = 0; + return 0; + } + + *ret = candidates[n - 1]; + return candidates[n - 1] > 0; + + default: + assert_not_reached(); + } +} + +static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offset_candidates, const uint64_t *offset, size_t n) { uint64_t p, q; int r, e; @@ -664,12 +698,81 @@ static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offse assert_se(r == e); assert_se(p == q); } + + /* by journal_file_next_entry() */ + for (size_t i = 0; i < n; i++) { + p = 0; + r = journal_file_next_entry(f, offset[i] - 2, DIRECTION_DOWN, NULL, &p); + e = expected_result_next(offset[i] - 2, offset_candidates, offset, n, DIRECTION_DOWN, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_DOWN, NULL, &p); + e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_DOWN, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i], DIRECTION_DOWN, NULL, &p); + e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_DOWN, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_DOWN, NULL, &p); + e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_DOWN, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_UP, NULL, &p); + e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_UP, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i], DIRECTION_UP, NULL, &p); + e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_UP, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_UP, NULL, &p); + e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_UP, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + + p = 0; + r = journal_file_next_entry(f, offset[i] + 2, DIRECTION_UP, NULL, &p); + e = expected_result_next(offset[i] + 2, offset_candidates, offset, n, DIRECTION_UP, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + } + for (size_t trial = 0; trial < 3 * n; trial++) { + uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2); + + p = 0; + r = journal_file_next_entry(f, i, DIRECTION_DOWN, NULL, &p); + e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_DOWN, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + } + for (size_t trial = 0; trial < 3 * n; trial++) { + uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2); + + p = 0; + r = journal_file_next_entry(f, i, DIRECTION_UP, NULL, &p); + e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_UP, &q); + assert_se(e == 0 ? r <= 0 : r > 0); + assert_se(p == q); + } } static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; char t[] = "/var/tmp/journal-seq-XXXXXX"; - _cleanup_free_ uint64_t *seqnum = NULL, *offset = NULL; + _cleanup_free_ uint64_t *seqnum = NULL, *offset = NULL, *offset_candidates = NULL; JournalFile *f; log_info("/* %s(%zu, %zu) */", __func__, n, num_corrupted); @@ -695,7 +798,9 @@ static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) { } } - verify(f, seqnum, offset, n); + assert_se(offset_candidates = newdup(uint64_t, offset, n)); + + verify(f, seqnum, offset_candidates, offset, n); /* Reset chain cache. */ assert_se(journal_file_move_to_entry_by_offset(f, offset[0], DIRECTION_DOWN, NULL, NULL) > 0); @@ -708,9 +813,10 @@ static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) { assert_se(o); o->entry.seqnum = 0; seqnum[i] = 0; + offset_candidates[i] = 0; } - verify(f, seqnum, offset, n); + verify(f, seqnum, offset_candidates, offset, n); test_close(f); test_done(t);