From: Arnaldo Carvalho de Melo Date: Sat, 2 May 2026 22:46:21 +0000 (-0300) Subject: perf zstd: Fix multi-iteration decompression and error handling X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3ec93875fda94e7f4c466855825c860a2e0ceac2;p=thirdparty%2Flinux.git perf zstd: Fix multi-iteration decompression and error handling zstd_decompress_stream() has two bugs in its multi-iteration loop: 1. After each ZSTD_decompressStream() call, the code advances output.dst by output.pos but doesn't reset output.pos to 0. ZSTD interprets output.pos relative to output.dst, so the next iteration writes at (dst + pos) + pos = dst + 2*pos, skipping a gap and potentially writing out of bounds. 2. On ZSTD_decompressStream() error, the loop executes break and returns output.pos (which is > 0 if some bytes were decompressed before the error). The caller checks !decomp_size and skips the error, silently accepting truncated or corrupted data. Fix both by removing the output buffer adjustment — ZSTD correctly accumulates output.pos across calls without it. Return 0 on decompression error so the caller detects it. Add a no-progress guard to prevent infinite loops if the output buffer fills before all input is consumed. Note: the compressed event data_size is validated against header.size by a subsequent patch in this series ("perf tools: Harden compressed event processing"). Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo --- diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c index ecda9deb53b73..21a0eb58597c2 100644 --- a/tools/perf/util/zstd.c +++ b/tools/perf/util/zstd.c @@ -123,14 +123,26 @@ size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size } } while (input.pos < input.size) { + size_t prev_in = input.pos; + size_t prev_out = output.pos; + ret = ZSTD_decompressStream(data->dstream, &output, &input); if (ZSTD_isError(ret)) { pr_err("failed to decompress (B): %zd -> %zd, dst_size %zd : %s\n", - src_size, output.size, dst_size, ZSTD_getErrorName(ret)); - break; + src_size, output.pos, dst_size, ZSTD_getErrorName(ret)); + return 0; } - output.dst = dst + output.pos; - output.size = dst_size - output.pos; + /* + * Neither stream advanced — decompression is stuck. + * Return 0 (error) rather than partial output: perf + * uses ZSTD_flushStream (not ZSTD_endStream), so the + * stream is continuous across compressed events. + * Discarding unconsumed input would desynchronize the + * decompressor, causing the next call to produce + * garbage that could be misinterpreted as valid events. + */ + if (input.pos == prev_in && output.pos == prev_out) + return 0; } return output.pos;