[PATCH 1/2] erofs-utils: lib: switch to on-heap fitblk_buffer for libzstd

Gao Xiang hsiangkao at linux.alibaba.com
Thu Jul 3 15:34:45 AEST 2025


 - Allocating VLAs on the stack (or using alloca()) for large sizes
   could exceed the stack limit;

 - It's easier to isolate these buffers on the heap for code sanitizers
   to detect potential bugs.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 lib/compressor_libzstd.c | 83 +++++++++++++++++++++++++++++-----------
 1 file changed, 60 insertions(+), 23 deletions(-)

diff --git a/lib/compressor_libzstd.c b/lib/compressor_libzstd.c
index e53b1a63..3233d723 100644
--- a/lib/compressor_libzstd.c
+++ b/lib/compressor_libzstd.c
@@ -4,18 +4,24 @@
 #include "erofs/config.h"
 #include <zstd.h>
 #include <zstd_errors.h>
-#include <alloca.h>
+#include <stdlib.h>
 #include "compressor.h"
 #include "erofs/atomic.h"
 
+struct erofs_libzstd_context {
+	ZSTD_CCtx *cctx;
+	u8 *fitblk_buffer;
+	unsigned int fitblk_bufsiz;
+};
+
 static int libzstd_compress(const struct erofs_compress *c,
 			    const void *src, unsigned int srcsize,
 			    void *dst, unsigned int dstcapacity)
 {
-	ZSTD_CCtx *cctx = c->private_data;
+	struct erofs_libzstd_context *ctx = c->private_data;
 	size_t csize;
 
-	csize = ZSTD_compress2(cctx, dst, dstcapacity, src, srcsize);
+	csize = ZSTD_compress2(ctx->cctx, dst, dstcapacity, src, srcsize);
 	if (ZSTD_isError(csize)) {
 		if (ZSTD_getErrorCode(csize) == ZSTD_error_dstSize_tooSmall)
 			return -ENOSPC;
@@ -29,12 +35,20 @@ static int libzstd_compress_destsize(const struct erofs_compress *c,
 				     const void *src, unsigned int *srcsize,
 				     void *dst, unsigned int dstsize)
 {
-	ZSTD_CCtx *cctx = c->private_data;
+	struct erofs_libzstd_context *ctx = c->private_data;
 	size_t l = 0;		/* largest input that fits so far */
 	size_t l_csize = 0;
 	size_t r = *srcsize + 1; /* smallest input that doesn't fit so far */
 	size_t m;
-	u8 *fitblk_buffer = alloca(dstsize + 32);
+
+	if (dstsize + 32 > ctx->fitblk_bufsiz) {
+		u8 *buf = realloc(ctx->fitblk_buffer, dstsize + 32);
+
+		if (!buf)
+			return -ENOMEM;
+		ctx->fitblk_bufsiz = dstsize + 32;
+		ctx->fitblk_buffer = buf;
+	}
 
 	m = dstsize * 4;
 	for (;;) {
@@ -43,7 +57,7 @@ static int libzstd_compress_destsize(const struct erofs_compress *c,
 		m = max(m, l + 1);
 		m = min(m, r - 1);
 
-		csize = ZSTD_compress2(cctx, fitblk_buffer,
+		csize = ZSTD_compress2(ctx->cctx, ctx->fitblk_buffer,
 				       dstsize + 32, src, m);
 		if (ZSTD_isError(csize)) {
 			if (ZSTD_getErrorCode(csize) == ZSTD_error_dstSize_tooSmall)
@@ -53,7 +67,7 @@ static int libzstd_compress_destsize(const struct erofs_compress *c,
 
 		if (csize > 0 && csize <= dstsize) {
 			/* Fits */
-			memcpy(dst, fitblk_buffer, csize);
+			memcpy(dst, ctx->fitblk_buffer, csize);
 			l = m;
 			l_csize = csize;
 			if (r <= l + 1 || csize + 1 >= dstsize)
@@ -78,9 +92,14 @@ doesnt_fit:
 
 static int compressor_libzstd_exit(struct erofs_compress *c)
 {
-	if (!c->private_data)
+	struct erofs_libzstd_context *ctx = c->private_data;
+
+	if (!ctx)
 		return -EINVAL;
-	ZSTD_freeCCtx(c->private_data);
+
+	free(ctx->fitblk_buffer);
+	ZSTD_freeCCtx(ctx->cctx);
+	free(ctx);
 	return 0;
 }
 
@@ -118,27 +137,41 @@ static int erofs_compressor_libzstd_setdictsize(struct erofs_compress *c,
 
 static int compressor_libzstd_init(struct erofs_compress *c)
 {
+	struct erofs_libzstd_context *ctx = c->private_data;
 	static erofs_atomic_bool_t __warnonce;
-	ZSTD_CCtx *cctx = c->private_data;
-	size_t err;
+	ZSTD_CCtx *cctx;
+	size_t errcode;
+	int err;
 
-	ZSTD_freeCCtx(cctx);
+	if (ctx) {
+		ZSTD_freeCCtx(ctx->cctx);
+		ctx->cctx = NULL;
+		c->private_data = NULL;
+	} else {
+		ctx = calloc(1, sizeof(*ctx));
+		if (!ctx)
+			return -ENOMEM;
+	}
 	cctx = ZSTD_createCCtx();
-	if (!cctx)
-		return -ENOMEM;
+	if (!cctx) {
+		err = -ENOMEM;
+		goto out_err;
+	}
 
-	err = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, c->compression_level);
-	if (ZSTD_isError(err)) {
+	err = -EINVAL;
+	errcode = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, c->compression_level);
+	if (ZSTD_isError(errcode)) {
 		erofs_err("failed to set compression level: %s",
-			  ZSTD_getErrorName(err));
-		return -EINVAL;
+			  ZSTD_getErrorName(errcode));
+		goto out_err;
 	}
-	err = ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, ilog2(c->dict_size));
-	if (ZSTD_isError(err)) {
-		erofs_err("failed to set window log: %s", ZSTD_getErrorName(err));
-		return -EINVAL;
+	errcode = ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, ilog2(c->dict_size));
+	if (ZSTD_isError(errcode)) {
+		erofs_err("failed to set window log: %s", ZSTD_getErrorName(errcode));
+		goto out_err;
 	}
-	c->private_data = cctx;
+	ctx->cctx = cctx;
+	c->private_data = ctx;
 
 	if (!erofs_atomic_test_and_set(&__warnonce)) {
 		erofs_warn("EXPERIMENTAL libzstd compressor in use. Note that `fitblk` isn't supported by upstream zstd for now.");
@@ -146,6 +179,10 @@ static int compressor_libzstd_init(struct erofs_compress *c)
 		erofs_info("You could clarify further needs in zstd repository <https://github.com/facebook/zstd/issues> for reference too.");
 	}
 	return 0;
+out_err:
+	ZSTD_freeCCtx(cctx);
+	free(ctx);
+	return err;
 }
 
 const struct erofs_compressor erofs_compressor_libzstd = {
-- 
2.43.5



More information about the Linux-erofs mailing list