From 70fec428c845216da98041b1ae9f16a8f54b3d68 Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Mon, 21 Sep 2026 22:23:15 +0200 Subject: pstore: don't leave an ERR_PTR in the ramoops zone pointer ramoops_init_prz() stores the result of persistent_ram_new() in *prz and returns the error without clearing it when initialization fails. ramoops_probe() then cleans up through ramoops_free_przs(), which hands *prz to persistent_ram_free(); that treats the ERR_PTR as a valid zone and dereferences it, crashing the kernel. Clear *prz before returning so that cleanup sees a NULL zone. Fixes: b5d38e9bf1b0c ("pstore/ram: Add console messages handling") Assisted-by: LLM Signed-off-by: Andrea Parri Link: https://patch.msgid.link/20260921202317.70595-2-parri.andrea@gmail.com Signed-off-by: Kees Cook --- fs/pstore/ram.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 2eb0d4fa2186..aa056d0395a4 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -604,6 +604,7 @@ static int ramoops_init_prz(const char *name, if (IS_ERR(*prz)) { int err = PTR_ERR(*prz); + *prz = NULL; dev_err(dev, "failed to request %s mem region (0x%zx@0x%llx): %d\n", name, sz, (unsigned long long)*paddr, err); return err; -- cgit v1.2.3 From 1b30df8662ac85a6759be4332f3bec7e3cb28efc Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Mon, 21 Sep 2026 22:23:16 +0200 Subject: pstore: reject persistent RAM zones too small for the buffer header persistent_ram_buffer_map() computes prz->buffer_size = size - sizeof(struct persistent_ram_buffer); without checking that size is large enough to hold the header. A ramoops zone configured with a tiny size (e.g. ramoops.pmsg_size=1) underflows buffer_size to a huge value, which defeats every bounds check in persistent_ram_write() and persistent_ram_write_user(); a write to /dev/pmsg0 then copies user data far past the end of the zone and corrupts kernel memory. Reject zones that are not larger than the buffer header. Fixes: 404a6043385de ("staging: android: persistent_ram: handle reserving and mapping memory") Assisted-by: LLM Signed-off-by: Andrea Parri Link: https://patch.msgid.link/20260921202317.70595-3-parri.andrea@gmail.com Signed-off-by: Kees Cook --- fs/pstore/ram_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index b4ec8009b8dc..ae941777037b 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -505,6 +505,12 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, prz->paddr = start; prz->size = size; + if (size <= sizeof(struct persistent_ram_buffer)) { + pr_err("%s: persistent ram zone too small (%#llx bytes)\n", + prz->label, (unsigned long long)size); + return -EINVAL; + } + if (pfn_valid(start >> PAGE_SHIFT)) prz->vaddr = persistent_ram_vmap(start, size, memtype); else -- cgit v1.2.3 From 7c756181175d50200be487affa905e973f6cbaf5 Mon Sep 17 00:00:00 2001 From: Jaidev Shastri Date: Mon, 21 Sep 2026 21:16:54 -0400 Subject: pstore: publish big_oops_buf after max_compressed_size allocate_buf_for_compression() stores big_oops_buf and then max_compressed_size, both with plain stores. pstore_dump() runs from kmsg_dump on any CPU, tests big_oops_buf without psinfo_lock and then reads max_compressed_size. Store the size first and publish the buffer with smp_store_release(), paired with smp_load_acquire() in pstore_dump(). Found with MBCheck, a static herd7-based memory consistency checker. Signed-off-by: Jaidev Shastri Link: https://patch.msgid.link/20260921-mb-pstore-v1-1-46f9a6129d58@vt.edu Signed-off-by: Kees Cook --- fs/pstore/platform.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 1d76c9d92056..1ad6e40f4952 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -240,9 +240,14 @@ static void allocate_buf_for_compression(void) return; } - /* A non-NULL big_oops_buf indicates compression is available. */ - big_oops_buf = buf; max_compressed_size = compressed_size; + /* + * A non-NULL big_oops_buf indicates compression is available. + * pstore_dump() tests it without psinfo_lock and then reads + * max_compressed_size, which is stored above. Publish the buffer + * with release semantics. + */ + smp_store_release(&big_oops_buf, buf); pr_info("Using crash dump compression: %s\n", compress); } @@ -316,7 +321,8 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.part = part; record.buf = psinfo->buf; - dst = big_oops_buf ?: psinfo->buf; + /* Pairs with the smp_store_release() in allocate_buf_for_compression(). */ + dst = smp_load_acquire(&big_oops_buf) ?: psinfo->buf; dst_size = max_compressed_size ?: psinfo->bufsize; /* Write dump header. */ -- cgit v1.2.3