diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-09-02 10:45:17 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-09-02 10:45:17 -0700 |
commit | 82c5561b57f8e871939e7fed02104c2572e8f48d (patch) | |
tree | ed86f33556a01a8ff35d00c040f9e37a0d649159 | |
parent | fdde6187804782dce49c78d00e04bac6535628c7 (diff) | |
parent | 94160062396d7e7cff4ed69320ffc5e22d51a0ab (diff) |
Merge tag 'pstore-v6.6-rc1-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull pstore fix from Kees Cook:
- Adjust sizes of buffers just avoid uncompress failures (Ard
Biesheuvel)
* tag 'pstore-v6.6-rc1-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
pstore: Base compression input buffer size on estimated compressed size
-rw-r--r-- | fs/pstore/platform.c | 34 |
1 files changed, 27 insertions, 7 deletions
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 62356d542ef6..e5bca9a004cc 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -98,7 +98,14 @@ MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)"); static void *compress_workspace; +/* + * Compression is only used for dmesg output, which consists of low-entropy + * ASCII text, and so we can assume worst-case 60%. + */ +#define DMESG_COMP_PERCENT 60 + static char *big_oops_buf; +static size_t max_compressed_size; void pstore_set_kmsg_bytes(int bytes) { @@ -196,6 +203,7 @@ static int pstore_compress(const void *in, void *out, static void allocate_buf_for_compression(void) { + size_t compressed_size; char *buf; /* Skip if not built-in or compression disabled. */ @@ -216,7 +224,8 @@ static void allocate_buf_for_compression(void) * uncompressed record size, since any record that would be expanded by * compression is just stored uncompressed. */ - buf = kvzalloc(psinfo->bufsize, GFP_KERNEL); + compressed_size = (psinfo->bufsize * 100) / DMESG_COMP_PERCENT; + buf = kvzalloc(compressed_size, GFP_KERNEL); if (!buf) { pr_err("Failed %zu byte compression buffer allocation for: %s\n", psinfo->bufsize, compress); @@ -233,6 +242,7 @@ static void allocate_buf_for_compression(void) /* A non-NULL big_oops_buf indicates compression is available. */ big_oops_buf = buf; + max_compressed_size = compressed_size; pr_info("Using crash dump compression: %s\n", compress); } @@ -246,6 +256,7 @@ static void free_buf_for_compression(void) kvfree(big_oops_buf); big_oops_buf = NULL; + max_compressed_size = 0; } void pstore_record_init(struct pstore_record *record, @@ -305,7 +316,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.buf = psinfo->buf; dst = big_oops_buf ?: psinfo->buf; - dst_size = psinfo->bufsize; + dst_size = max_compressed_size ?: psinfo->bufsize; /* Write dump header. */ header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, @@ -326,8 +337,15 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.compressed = true; record.size = zipped_len; } else { - record.size = header_size + dump_size; - memcpy(psinfo->buf, dst, record.size); + /* + * Compression failed, so the buffer is most + * likely filled with binary data that does not + * compress as well as ASCII text. Copy as much + * of the uncompressed data as possible into + * the pstore record, and discard the rest. + */ + record.size = psinfo->bufsize; + memcpy(psinfo->buf, dst, psinfo->bufsize); } } else { record.size = header_size + dump_size; @@ -560,6 +578,7 @@ static void decompress_record(struct pstore_record *record, int ret; int unzipped_len; char *unzipped, *workspace; + size_t max_uncompressed_size; if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed) return; @@ -583,7 +602,8 @@ static void decompress_record(struct pstore_record *record, } /* Allocate enough space to hold max decompression and ECC. */ - workspace = kvzalloc(psinfo->bufsize + record->ecc_notice_size, + max_uncompressed_size = 3 * psinfo->bufsize; + workspace = kvzalloc(max_uncompressed_size + record->ecc_notice_size, GFP_KERNEL); if (!workspace) return; @@ -591,11 +611,11 @@ static void decompress_record(struct pstore_record *record, zstream->next_in = record->buf; zstream->avail_in = record->size; zstream->next_out = workspace; - zstream->avail_out = psinfo->bufsize; + zstream->avail_out = max_uncompressed_size; ret = zlib_inflate(zstream, Z_FINISH); if (ret != Z_STREAM_END) { - pr_err("zlib_inflate() failed, ret = %d!\n", ret); + pr_err_ratelimited("zlib_inflate() failed, ret = %d!\n", ret); kvfree(workspace); return; } |