summaryrefslogtreecommitdiff
path: root/fs/pstore/platform.c
diff options
context:
space:
mode:
authorYuxiao Zhang <yuxiaozhang@google.com>2023-06-27 13:25:41 -0700
committerKees Cook <keescook@chromium.org>2023-08-17 15:18:24 -0700
commit104fd0b5e948157f8e8ac88a20b46ba8641d4e95 (patch)
treedd88e938e7515d17ef837a470b750599818143b8 /fs/pstore/platform.c
parentfe8c3623ab06603eb760444a032d426542212021 (diff)
pstore: Support record sizes larger than kmalloc() limit
Currently pstore record buffers are allocated using kmalloc() which has a maximum size based on page size. If a large "pmsg-size" module parameter is specified, pmsg will fail to copy the contents since memdup_user() is limited to kmalloc() allocation sizes. Since we don't need physically contiguous memory for any of the pstore record buffers, use kvzalloc() to avoid such limitations in the core of pstore and in the ram backend, and explicitly read from userspace using vmemdup_user(). This also means that any other backends that want to (or do already) support larger record sizes will Just Work now. Signed-off-by: Yuxiao Zhang <yuxiaozhang@google.com> Link: https://lore.kernel.org/r/20230627202540.881909-2-yuxiaozhang@google.com Co-developed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'fs/pstore/platform.c')
-rw-r--r--fs/pstore/platform.c27
1 files changed, 14 insertions, 13 deletions
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index aaa5e4e66db4..62356d542ef6 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/kmsg_dump.h>
#include <linux/console.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/pstore.h>
#include <linux/string.h>
@@ -215,7 +216,7 @@ static void allocate_buf_for_compression(void)
* uncompressed record size, since any record that would be expanded by
* compression is just stored uncompressed.
*/
- buf = kmalloc(psinfo->bufsize, GFP_KERNEL);
+ buf = kvzalloc(psinfo->bufsize, GFP_KERNEL);
if (!buf) {
pr_err("Failed %zu byte compression buffer allocation for: %s\n",
psinfo->bufsize, compress);
@@ -226,7 +227,7 @@ static void allocate_buf_for_compression(void)
vmalloc(zlib_deflate_workspacesize(MAX_WBITS, DEF_MEM_LEVEL));
if (!compress_workspace) {
pr_err("Failed to allocate zlib deflate workspace\n");
- kfree(buf);
+ kvfree(buf);
return;
}
@@ -243,7 +244,7 @@ static void free_buf_for_compression(void)
compress_workspace = NULL;
}
- kfree(big_oops_buf);
+ kvfree(big_oops_buf);
big_oops_buf = NULL;
}
@@ -421,7 +422,7 @@ static int pstore_write_user_compat(struct pstore_record *record,
if (record->buf)
return -EINVAL;
- record->buf = memdup_user(buf, record->size);
+ record->buf = vmemdup_user(buf, record->size);
if (IS_ERR(record->buf)) {
ret = PTR_ERR(record->buf);
goto out;
@@ -429,7 +430,7 @@ static int pstore_write_user_compat(struct pstore_record *record,
ret = record->psi->write(record);
- kfree(record->buf);
+ kvfree(record->buf);
out:
record->buf = NULL;
@@ -582,8 +583,8 @@ static void decompress_record(struct pstore_record *record,
}
/* Allocate enough space to hold max decompression and ECC. */
- workspace = kmalloc(psinfo->bufsize + record->ecc_notice_size,
- GFP_KERNEL);
+ workspace = kvzalloc(psinfo->bufsize + record->ecc_notice_size,
+ GFP_KERNEL);
if (!workspace)
return;
@@ -595,7 +596,7 @@ static void decompress_record(struct pstore_record *record,
ret = zlib_inflate(zstream, Z_FINISH);
if (ret != Z_STREAM_END) {
pr_err("zlib_inflate() failed, ret = %d!\n", ret);
- kfree(workspace);
+ kvfree(workspace);
return;
}
@@ -606,14 +607,14 @@ static void decompress_record(struct pstore_record *record,
record->ecc_notice_size);
/* Copy decompressed contents into an minimum-sized allocation. */
- unzipped = kmemdup(workspace, unzipped_len + record->ecc_notice_size,
- GFP_KERNEL);
- kfree(workspace);
+ unzipped = kvmemdup(workspace, unzipped_len + record->ecc_notice_size,
+ GFP_KERNEL);
+ kvfree(workspace);
if (!unzipped)
return;
/* Swap out compressed contents with decompressed contents. */
- kfree(record->buf);
+ kvfree(record->buf);
record->buf = unzipped;
record->size = unzipped_len;
record->compressed = false;
@@ -673,7 +674,7 @@ void pstore_get_backend_records(struct pstore_info *psi,
rc = pstore_mkfile(root, record);
if (rc) {
/* pstore_mkfile() did not take record, so free it. */
- kfree(record->buf);
+ kvfree(record->buf);
kfree(record->priv);
kfree(record);
if (rc != -EEXIST || !quiet)