Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pstore: Replace crypto API compression with zlib_deflate library calls

Pstore supports compression using a variety of algorithms exposed by the
crypto API. This uses the deprecated comp (as opposed to scomp/acomp)
API, and so we should stop using that, and either move to the new API,
or switch to a different approach entirely.

Given that we only compress ASCII text in pstore, and considering that
this happens when the system is likely to be in an unstable state, the
flexibility that the complex crypto API provides does not outweigh its
impact on the risk that we might encounter additional problems when
trying to commit the kernel log contents to the pstore backend.

So let's switch [back] to the zlib deflate library API, and remove all
the complexity that really has no place in a low-level diagnostic
facility. Note that, while more modern compression algorithms have been
added to the kernel in recent years, the code size of zlib deflate is
substantially smaller than, e.g., zstd, while its performance in terms
of compression ratio is comparable for ASCII text, and speed is deemed
irrelevant in this context.

Note that this means that compressed pstore records may no longer be
accessible after a kernel upgrade, but this has never been part of the
contract. (The choice of compression algorithm is not stored in the
pstore records either)

Tested-by: "Guilherme G. Piccoli" <gpiccoli@igalia.com> # Steam Deck
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20230712162332.2670437-3-ardb@kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>

authored by

Ard Biesheuvel and committed by
Kees Cook
438b8050 1756ddea

+97 -139
+9 -91
fs/pstore/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 config PSTORE 3 3 tristate "Persistent store support" 4 - select CRYPTO if PSTORE_COMPRESS 5 4 default n 6 5 help 7 6 This option enables generic access to platform level ··· 21 22 Defines default size of pstore kernel log storage. 22 23 Can be enlarged if needed, not recommended to shrink it. 23 24 24 - config PSTORE_DEFLATE_COMPRESS 25 - tristate "DEFLATE (ZLIB) compression" 26 - default y 27 - depends on PSTORE 28 - select CRYPTO_DEFLATE 29 - help 30 - This option enables DEFLATE (also known as ZLIB) compression 31 - algorithm support. 32 - 33 - config PSTORE_LZO_COMPRESS 34 - tristate "LZO compression" 35 - depends on PSTORE 36 - select CRYPTO_LZO 37 - help 38 - This option enables LZO compression algorithm support. 39 - 40 - config PSTORE_LZ4_COMPRESS 41 - tristate "LZ4 compression" 42 - depends on PSTORE 43 - select CRYPTO_LZ4 44 - help 45 - This option enables LZ4 compression algorithm support. 46 - 47 - config PSTORE_LZ4HC_COMPRESS 48 - tristate "LZ4HC compression" 49 - depends on PSTORE 50 - select CRYPTO_LZ4HC 51 - help 52 - This option enables LZ4HC (high compression) mode algorithm. 53 - 54 - config PSTORE_842_COMPRESS 55 - bool "842 compression" 56 - depends on PSTORE 57 - select CRYPTO_842 58 - help 59 - This option enables 842 compression algorithm support. 60 - 61 - config PSTORE_ZSTD_COMPRESS 62 - bool "zstd compression" 63 - depends on PSTORE 64 - select CRYPTO_ZSTD 65 - help 66 - This option enables zstd compression algorithm support. 67 - 68 25 config PSTORE_COMPRESS 69 - def_bool y 26 + bool "Pstore compression (deflate)" 70 27 depends on PSTORE 71 - depends on PSTORE_DEFLATE_COMPRESS || PSTORE_LZO_COMPRESS || \ 72 - PSTORE_LZ4_COMPRESS || PSTORE_LZ4HC_COMPRESS || \ 73 - PSTORE_842_COMPRESS || PSTORE_ZSTD_COMPRESS 74 - 75 - choice 76 - prompt "Default pstore compression algorithm" 77 - depends on PSTORE_COMPRESS 28 + select ZLIB_INFLATE 29 + select ZLIB_DEFLATE 30 + default y 78 31 help 79 - This option chooses the default active compression algorithm. 80 - This change be changed at boot with "pstore.compress=..." on 81 - the kernel command line. 82 - 83 - Currently, pstore has support for 6 compression algorithms: 84 - deflate, lzo, lz4, lz4hc, 842 and zstd. 85 - 86 - The default compression algorithm is deflate. 87 - 88 - config PSTORE_DEFLATE_COMPRESS_DEFAULT 89 - bool "deflate" if PSTORE_DEFLATE_COMPRESS 90 - 91 - config PSTORE_LZO_COMPRESS_DEFAULT 92 - bool "lzo" if PSTORE_LZO_COMPRESS 93 - 94 - config PSTORE_LZ4_COMPRESS_DEFAULT 95 - bool "lz4" if PSTORE_LZ4_COMPRESS 96 - 97 - config PSTORE_LZ4HC_COMPRESS_DEFAULT 98 - bool "lz4hc" if PSTORE_LZ4HC_COMPRESS 99 - 100 - config PSTORE_842_COMPRESS_DEFAULT 101 - bool "842" if PSTORE_842_COMPRESS 102 - 103 - config PSTORE_ZSTD_COMPRESS_DEFAULT 104 - bool "zstd" if PSTORE_ZSTD_COMPRESS 105 - 106 - endchoice 107 - 108 - config PSTORE_COMPRESS_DEFAULT 109 - string 110 - depends on PSTORE_COMPRESS 111 - default "deflate" if PSTORE_DEFLATE_COMPRESS_DEFAULT 112 - default "lzo" if PSTORE_LZO_COMPRESS_DEFAULT 113 - default "lz4" if PSTORE_LZ4_COMPRESS_DEFAULT 114 - default "lz4hc" if PSTORE_LZ4HC_COMPRESS_DEFAULT 115 - default "842" if PSTORE_842_COMPRESS_DEFAULT 116 - default "zstd" if PSTORE_ZSTD_COMPRESS_DEFAULT 32 + Whether pstore records should be compressed before being written to 33 + the backing store. This is implemented using the zlib 'deflate' 34 + algorithm, using the library implementation instead of using the full 35 + blown crypto API. This reduces the risk of secondary oopses or other 36 + problems while pstore is recording panic metadata. 117 37 118 38 config PSTORE_CONSOLE 119 39 bool "Log kernel console messages"
+88 -48
fs/pstore/platform.c
··· 16 16 #include <linux/console.h> 17 17 #include <linux/module.h> 18 18 #include <linux/pstore.h> 19 - #include <linux/crypto.h> 20 19 #include <linux/string.h> 21 20 #include <linux/timer.h> 22 21 #include <linux/slab.h> 23 22 #include <linux/uaccess.h> 24 23 #include <linux/jiffies.h> 24 + #include <linux/vmalloc.h> 25 25 #include <linux/workqueue.h> 26 + #include <linux/zlib.h> 26 27 27 28 #include "internal.h" 28 29 ··· 72 71 module_param(backend, charp, 0444); 73 72 MODULE_PARM_DESC(backend, "specific backend to use"); 74 73 75 - static char *compress = 76 - #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT 77 - CONFIG_PSTORE_COMPRESS_DEFAULT; 78 - #else 79 - NULL; 80 - #endif 74 + /* 75 + * pstore no longer implements compression via the crypto API, and only 76 + * supports zlib deflate compression implemented using the zlib library 77 + * interface. This removes additional complexity which is hard to justify for a 78 + * diagnostic facility that has to operate in conditions where the system may 79 + * have become unstable. Zlib deflate is comparatively small in terms of code 80 + * size, and compresses ASCII text comparatively well. In terms of compression 81 + * speed, deflate is not the best performer but for recording the log output on 82 + * a kernel panic, this is not considered critical. 83 + * 84 + * The only remaining arguments supported by the compress= module parameter are 85 + * 'deflate' and 'none'. To retain compatibility with existing installations, 86 + * all other values are logged and replaced with 'deflate'. 87 + */ 88 + static char *compress = "deflate"; 81 89 module_param(compress, charp, 0444); 82 90 MODULE_PARM_DESC(compress, "compression to use"); 83 91 ··· 95 85 module_param(kmsg_bytes, ulong, 0444); 96 86 MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)"); 97 87 98 - /* Compression parameters */ 99 - static struct crypto_comp *tfm; 88 + static void *compress_workspace; 100 89 101 90 static char *big_oops_buf; 102 91 ··· 165 156 static int pstore_compress(const void *in, void *out, 166 157 unsigned int inlen, unsigned int outlen) 167 158 { 159 + struct z_stream_s zstream = { 160 + .next_in = in, 161 + .avail_in = inlen, 162 + .next_out = out, 163 + .avail_out = outlen, 164 + .workspace = compress_workspace, 165 + }; 168 166 int ret; 169 167 170 168 if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS)) 171 169 return -EINVAL; 172 170 173 - ret = crypto_comp_compress(tfm, in, inlen, out, &outlen); 174 - if (ret) { 175 - pr_err("crypto_comp_compress failed, ret = %d!\n", ret); 176 - return ret; 177 - } 171 + ret = zlib_deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 172 + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); 173 + if (ret != Z_OK) 174 + return -EINVAL; 178 175 179 - return outlen; 176 + ret = zlib_deflate(&zstream, Z_FINISH); 177 + if (ret != Z_STREAM_END) 178 + return -EINVAL; 179 + 180 + ret = zlib_deflateEnd(&zstream); 181 + if (ret != Z_OK) 182 + pr_warn_once("zlib_deflateEnd() failed: %d\n", ret); 183 + 184 + return zstream.total_out; 180 185 } 181 186 182 187 static void allocate_buf_for_compression(void) 183 188 { 184 - struct crypto_comp *ctx; 185 189 char *buf; 186 190 187 - /* Skip if not built-in or compression backend not selected yet. */ 188 - if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !compress) 191 + /* Skip if not built-in or compression disabled. */ 192 + if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !compress || 193 + !strcmp(compress, "none")) { 194 + compress = NULL; 189 195 return; 196 + } 190 197 191 - /* Skip if no pstore backend yet or compression init already done. */ 192 - if (!psinfo || tfm) 193 - return; 194 - 195 - if (!crypto_has_comp(compress, 0, 0)) { 196 - pr_err("Unknown compression: %s\n", compress); 197 - return; 198 + if (strcmp(compress, "deflate")) { 199 + pr_err("Unsupported compression '%s', falling back to deflate\n", 200 + compress); 201 + compress = "deflate"; 198 202 } 199 203 200 204 /* ··· 222 200 return; 223 201 } 224 202 225 - ctx = crypto_alloc_comp(compress, 0, 0); 226 - if (IS_ERR_OR_NULL(ctx)) { 203 + compress_workspace = 204 + vmalloc(zlib_deflate_workspacesize(MAX_WBITS, DEF_MEM_LEVEL)); 205 + if (!compress_workspace) { 206 + pr_err("Failed to allocate zlib deflate workspace\n"); 227 207 kfree(buf); 228 - pr_err("crypto_alloc_comp('%s') failed: %ld\n", compress, 229 - PTR_ERR(ctx)); 230 208 return; 231 209 } 232 210 233 211 /* A non-NULL big_oops_buf indicates compression is available. */ 234 - tfm = ctx; 235 212 big_oops_buf = buf; 236 213 237 214 pr_info("Using crash dump compression: %s\n", compress); ··· 238 217 239 218 static void free_buf_for_compression(void) 240 219 { 241 - if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && tfm) { 242 - crypto_free_comp(tfm); 243 - tfm = NULL; 220 + if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress_workspace) { 221 + vfree(compress_workspace); 222 + compress_workspace = NULL; 244 223 } 224 + 245 225 kfree(big_oops_buf); 246 226 big_oops_buf = NULL; 247 227 } ··· 553 531 } 554 532 EXPORT_SYMBOL_GPL(pstore_unregister); 555 533 556 - static void decompress_record(struct pstore_record *record) 534 + static void decompress_record(struct pstore_record *record, 535 + struct z_stream_s *zstream) 557 536 { 558 537 int ret; 559 538 int unzipped_len; ··· 570 547 } 571 548 572 549 /* Missing compression buffer means compression was not initialized. */ 573 - if (!big_oops_buf) { 550 + if (!zstream->workspace) { 574 551 pr_warn("no decompression method initialized!\n"); 552 + return; 553 + } 554 + 555 + ret = zlib_inflateReset(zstream); 556 + if (ret != Z_OK) { 557 + pr_err("zlib_inflateReset() failed, ret = %d!\n", ret); 575 558 return; 576 559 } 577 560 ··· 587 558 if (!workspace) 588 559 return; 589 560 590 - /* After decompression "unzipped_len" is almost certainly smaller. */ 591 - ret = crypto_comp_decompress(tfm, record->buf, record->size, 592 - workspace, &unzipped_len); 593 - if (ret) { 594 - pr_err("crypto_comp_decompress failed, ret = %d!\n", ret); 561 + zstream->next_in = record->buf; 562 + zstream->avail_in = record->size; 563 + zstream->next_out = workspace; 564 + zstream->avail_out = psinfo->bufsize; 565 + 566 + ret = zlib_inflate(zstream, Z_FINISH); 567 + if (ret != Z_STREAM_END) { 568 + pr_err("zlib_inflate() failed, ret = %d!\n", ret); 595 569 kfree(workspace); 596 570 return; 597 571 } 572 + 573 + unzipped_len = zstream->total_out; 598 574 599 575 /* Append ECC notice to decompressed buffer. */ 600 576 memcpy(workspace + unzipped_len, record->buf + record->size, ··· 630 596 { 631 597 int failed = 0; 632 598 unsigned int stop_loop = 65536; 599 + struct z_stream_s zstream = {}; 633 600 634 601 if (!psi || !root) 635 602 return; 603 + 604 + if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) { 605 + zstream.workspace = kvmalloc(zlib_inflate_workspacesize(), 606 + GFP_KERNEL); 607 + zlib_inflateInit2(&zstream, -DEF_WBITS); 608 + } 636 609 637 610 mutex_lock(&psi->read_mutex); 638 611 if (psi->open && psi->open(psi)) ··· 669 628 break; 670 629 } 671 630 672 - decompress_record(record); 631 + decompress_record(record, &zstream); 673 632 rc = pstore_mkfile(root, record); 674 633 if (rc) { 675 634 /* pstore_mkfile() did not take record, so free it. */ ··· 684 643 psi->close(psi); 685 644 out: 686 645 mutex_unlock(&psi->read_mutex); 646 + 647 + if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) { 648 + if (zlib_inflateEnd(&zstream) != Z_OK) 649 + pr_warn("zlib_inflateEnd() failed\n"); 650 + kvfree(zstream.workspace); 651 + } 687 652 688 653 if (failed) 689 654 pr_warn("failed to create %d record(s) from '%s'\n", ··· 717 670 static int __init pstore_init(void) 718 671 { 719 672 int ret; 720 - 721 - /* 722 - * Check if any pstore backends registered earlier but did not 723 - * initialize compression because crypto was not ready. If so, 724 - * initialize compression now. 725 - */ 726 - allocate_buf_for_compression(); 727 673 728 674 ret = pstore_init_fs(); 729 675 if (ret)