enforce exact decompressed length for lz4, gzip, and zstd
checkstyle / checkstyle (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
smatch / smatch (push) Has been cancelled
zfs-arm / ZFS ARM build (push) Has been cancelled
zfs-qemu / Setup (push) Has been cancelled
zloop / zloop (push) Has been cancelled
zfs-qemu / qemu-x86 (push) Has been cancelled
zfs-qemu / Cleanup (push) Has been cancelled

Decompressors must expand a ZFS block to exactly the expected number
of bytes. Treat decompression to an unexpected length as failure, so
truncated or short output is not accepted as valid decompression. This
makes our handling of decompress return values consistent with the
decompression functions' APIs.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com>
Signed-off-by: Alek Pinchuk <Alek.Pinchuk@connectwise.com>
Closes #18599
This commit is contained in:
Alek P
2026-05-29 21:13:39 -04:00
committed by GitHub
parent eafa39fbc3
commit c90dc28089
3 changed files with 26 additions and 6 deletions
+6 -2
View File
@@ -96,13 +96,17 @@ zfs_gzip_decompress_buf(void *s_start, void *d_start, size_t s_len,
/* check if hardware accelerator can be used */
if (qat_dc_use_accel(d_len)) {
if (qat_compress(QAT_DECOMPRESS, s_start, s_len,
d_start, d_len, &dstlen) == CPA_STATUS_SUCCESS)
return (0);
d_start, d_len, &dstlen) == CPA_STATUS_SUCCESS) {
if ((size_t)dstlen == d_len)
return (0);
}
/* if hardware de-compress fail, do it again with software */
}
if (uncompress_func(d_start, &dstlen, s_start, s_len) != Z_OK)
return (-1);
if ((size_t)dstlen != d_len)
return (-1);
return (0);
}
+11 -4
View File
@@ -88,17 +88,24 @@ zfs_lz4_decompress_buf(void *s_start, void *d_start, size_t s_len,
(void) n;
const char *src = s_start;
uint32_t bufsiz = BE_IN32(src);
int decoded;
/* invalid compressed buffer size encoded at start */
if (bufsiz + sizeof (bufsiz) > s_len)
return (1);
/*
* Returns 0 on success (decompression function returned non-negative)
* and non-zero on failure (decompression function returned negative).
* LZ4_uncompress_unknownOutputSize returns the number of bytes decoded
* on success, or a negative value on failure. An OpenZFS block must
* expand to exactly d_len bytes
*/
return (LZ4_uncompress_unknownOutputSize(&src[sizeof (bufsiz)],
d_start, bufsiz, d_len) < 0);
decoded = LZ4_uncompress_unknownOutputSize(&src[sizeof (bufsiz)],
d_start, bufsiz, d_len);
if (decoded < 0)
return (1);
if (d_len != (size_t)decoded)
return (1);
return (0);
}
ZFS_COMPRESS_WRAP_DECL(zfs_lz4_compress)
+9
View File
@@ -682,6 +682,15 @@ zfs_zstd_decompress_level_buf(void *s_start, void *d_start, size_t s_len,
return (1);
}
/*
* An OpenZFS compressed block must expand to exactly d_len bytes.
* ZSTD_decompressDCtx returns the decompressed size on success.
*/
if (result != d_len) {
ZSTDSTAT_BUMP(zstd_stat_dec_fail);
return (1);
}
if (level) {
*level = curlevel;
}