]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
compress: gracefully handle a truncated ZSTD frame
authorFrantisek Sumsal <frantisek@sumsal.cz>
Sun, 19 Apr 2026 13:47:11 +0000 (15:47 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 20 Apr 2026 07:37:13 +0000 (09:37 +0200)
If a journal file contains a truncated ZSTD frame (i.e. a frame with
Frame_Content_Size > 0, but with not enough data in Data_Block),
ZSTD_decompressStream() would return a non-zero, non-error value. This
would then skip the error path in the ZSTD_isError() branch and we'd hit
the following assert:

$ build-local/journalctl -o cat --file zstd-truncated.journal
Assertion 'output.pos >= prefix_len + 1' failed at src/basic/compress.c:1236, function decompress_startswith_zstd(). Aborting.
Aborted                    (core dumped) build-local/journalctl -o cat --file zstd-truncated.journal

Let's handle this situation gracefully and return EBADMSG instead.

Also, add another journalctl invocation to the corrupted-journals test
that goes through the sd_journal_get_data() -> decompress_startswith_zstd()
code path which, among other things, covers the issue when run on the
provided journal file.

src/basic/compress.c
test/test-journals/corrupted/zstd-truncated-frame.zst [new file with mode: 0644]
test/units/TEST-04-JOURNAL.corrupted-journals.sh

index 72e336bc68965b776ad5eba8b76f1e2f17a80105..4beaa0f6bd22f930fe738058e2cc2f28067db37f 100644 (file)
@@ -1233,7 +1233,9 @@ static int decompress_startswith_zstd(
                 log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
                 return zstd_ret_to_errno(k);
         }
-        assert(output.pos >= prefix_len + 1);
+
+        if (output.pos < prefix_len + 1)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoded less data than indicated, probably corrupted stream.");
 
         return memcmp(*buffer, prefix, prefix_len) == 0 &&
                 ((const uint8_t*) *buffer)[prefix_len] == extra;
diff --git a/test/test-journals/corrupted/zstd-truncated-frame.zst b/test/test-journals/corrupted/zstd-truncated-frame.zst
new file mode 100644 (file)
index 0000000..66db974
Binary files /dev/null and b/test/test-journals/corrupted/zstd-truncated-frame.zst differ
index 3dd0fc6dde56a1b257e56e979194a533bc19f159..0a8d7e030e86c6cb16cb55536c96f9a4dd4f001b 100755 (executable)
@@ -20,6 +20,7 @@ if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
         timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]]
         timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]]
         timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]]
+        timeout 10 journalctl --file="$file" --grep=. >/dev/null || [[ $? -lt 124 ]]
         timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]]
         timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]]
         if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
@@ -36,6 +37,7 @@ fi
 timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]]
 timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]]
 timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --grep=. >/dev/null || [[ $? -lt 124 ]]
 timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]]
 timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]]
 if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then