[PATCH 5/5] pstore: Use zstd directly by default for compression

From: Kees Cook
Date: Mon Oct 17 2022 - 22:09:18 EST


If compression is desired, use zstd directly to avoid Crypto API
overhead.

Cc: Tony Luck <tony.luck@xxxxxxxxx>
Cc: "Guilherme G. Piccoli" <gpiccoli@xxxxxxxxxx>
Cc: Nick Terrell <terrelln@xxxxxx>
Cc: linux-hardening@xxxxxxxxxxxxxxx
Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
fs/pstore/Kconfig | 11 +++++-
fs/pstore/platform.c | 93 +++++++++++++++++++++++++++++++++++++++++---
2 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index a95b3981cb0e..1f05312c7479 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -25,7 +25,7 @@ config PSTORE_DEFAULT_KMSG_BYTES
choice
prompt "Panic dump compression"
depends on PSTORE
- default PSTORE_COMPRESS_CRYPTO
+ default PSTORE_COMPRESS
help
Choose whether and how to compress the panic dump output. This
is usually only needed for very storage-constrained backends.
@@ -38,6 +38,15 @@ choice
available to from the Crypto API. Note that this may reserve
non-trivial amounts of per-CPU memory.

+ config PSTORE_COMPRESS
+ bool "Use recommended best compression algorithm"
+ select CRYPTO_ZSTD
+ help
+ Use the compression routines currently deemed best suited
+ for panic dump compression. Currently, this is "zstd".
+ As this compression is used directly through its library
+ interface, no per-CPU memory is allocated by the Crypto API.
+
config PSTORE_COMPRESS_NONE
bool "Do not compress panic dumps"
help
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 4d883dc2e8a7..51d2801fc880 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -23,6 +23,7 @@
#include <linux/uaccess.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
+#include <linux/zstd.h>

#include "internal.h"

@@ -72,13 +73,17 @@ module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "specific backend to use");

static char *compress __ro_after_init =
-#ifdef CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT
- CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT;
+#ifdef CONFIG_PSTORE_COMPRESS
+ "zstd";
#else
- NULL;
-#endif
+# ifdef CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT
+ CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT;
module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "compression to use");
+# else
+ NULL;
+# endif
+#endif

/* How much of the kernel log to snapshot */
unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
@@ -88,6 +93,12 @@ MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
/* Compression parameters */
static struct crypto_comp *tfm;

+static zstd_cctx *cctx;
+static zstd_dctx *dctx;
+static void *cwksp;
+static void *dwksp;
+zstd_parameters zparams;
+
static char *big_oops_buf;
static size_t big_oops_buf_sz;

@@ -175,6 +186,14 @@ static int pstore_compress(const void *in, void *out,
return 0;
}

+ if (IS_ENABLED(CONFIG_PSTORE_COMPRESS)) {
+ *outlen = zstd_compress_cctx(cctx, out, *outlen, in, inlen,
+ &zparams);
+ if (zstd_is_error(*outlen))
+ return -EINVAL;
+ return 0;
+ }
+
return -EINVAL;
}

@@ -203,12 +222,56 @@ static int allocate_crypto_buf(void)
return 0;
}

+static int allocate_zstd_buf(void)
+{
+ size_t csize, dsize;
+
+ /* Skip if compression init already done. */
+ if (cctx)
+ return 0;
+
+ zparams = zstd_get_params(3, 0);
+ csize = zstd_cctx_workspace_bound(&zparams.cParams);
+ dsize = zstd_dctx_workspace_bound();
+
+#define init_ctx(dir, name) do { \
+ dir##wksp = kzalloc(dir##size, GFP_KERNEL); \
+ if (!dir##wksp) { \
+ pr_err("Failed %zu byte %s " #name " allocation\n", \
+ dir##size, compress); \
+ return -ENOMEM; \
+ } \
+ dir##ctx = zstd_init_##dir##ctx(dir##wksp, dir##size); \
+ if (!dir##ctx) { \
+ pr_err("Failed %s " #name " context init\n", compress); \
+ return -EINVAL; \
+ } \
+} while (0)
+
+ init_ctx(c, compress);
+ init_ctx(d, decompress);
+
+#undef init_wksp
+
+ pr_info("Using crash dump compression: built-in %s\n", compress);
+ return 0;
+}
+
static void free_buf_for_compression(void)
{
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO) && tfm) {
crypto_free_comp(tfm);
tfm = NULL;
}
+ if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && cctx) {
+ cctx = NULL;
+ dctx = NULL;
+ kfree(cwksp);
+ cwksp = NULL;
+ kfree(dwksp);
+ dwksp = NULL;
+
+ }
kfree(big_oops_buf);
big_oops_buf = NULL;
big_oops_buf_sz = 0;
@@ -228,7 +291,10 @@ static void allocate_buf_for_compression(void)
return;

/* Initialize compression routines. */
- rc = allocate_crypto_buf();
+ if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO))
+ rc = allocate_crypto_buf();
+ else
+ rc = allocate_zstd_buf();
if (rc)
goto fail;

@@ -598,6 +664,16 @@ static int pstore_decompress_crypto(struct pstore_record *record, char *workspac
return 0;
}

+static int pstore_decompress_zstd(struct pstore_record *record,
+ char *workspace, size_t *outlen)
+{
+ *outlen = zstd_decompress_dctx(dctx, workspace, *outlen,
+ record->buf, record->size);
+ if (zstd_is_error(*outlen))
+ return -EINVAL;
+ return 0;
+}
+
static void decompress_record(struct pstore_record *record)
{
size_t unzipped_len;
@@ -626,7 +702,12 @@ static void decompress_record(struct pstore_record *record)
if (!workspace)
return;

- rc = pstore_decompress_crypto(record, workspace, &unzipped_len);
+ if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO))
+ rc = pstore_decompress_crypto(record, workspace,
+ &unzipped_len);
+ else
+ rc = pstore_decompress_zstd(record, workspace,
+ &unzipped_len);
if (rc) {
kfree(workspace);
return;
--
2.34.1