[PATCH] pstore/zone: reject truncated kmsg records
From: Laxman Acharya Padhya
Date: Wed Jul 29 2026 - 14:28:22 EST
A recovered pstore/zone kmsg record may have a valid zone signature
and kmsg magic while advertising a datalen smaller than struct
psz_kmsg_header.
psz_kmsg_recover_meta() reads a full header from storage regardless of
datalen. It can therefore parse stale bytes beyond the advertised
record, update the write and crash counters, and mark the truncated zone
for recovery. psz_kmsg_read() later subtracts the header size from
datalen, turning the payload length negative. That value is then
converted to an allocation size.
Validate datalen before parsing recovery metadata, and retain a
defensive check in the read path before subtracting the header size.
Treat a zero-length zone as erased as before.
Fixes: d26c3321fe18 ("pstore/zone: Introduce common layer to manage storage zones")
Signed-off-by: Laxman Acharya Padhya <acharyalaxman8848@xxxxxxxxx>
---
fs/pstore/zone.c | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/fs/pstore/zone.c b/fs/pstore/zone.c
index a3b003f9a..d4fbe4c32 100644
--- a/fs/pstore/zone.c
+++ b/fs/pstore/zone.c
@@ -372,6 +372,7 @@ static int psz_kmsg_recover_meta(struct psz_context *cxt)
{
struct pstore_zone_info *info = cxt->pstore_zone_info;
struct pstore_zone *zone;
+ int datalen;
ssize_t rcnt, len;
struct psz_buffer *buf;
struct psz_kmsg_header *hdr;
@@ -408,7 +409,22 @@ static int psz_kmsg_recover_meta(struct psz_context *cxt)
continue;
}
- if (zone->buffer_size < atomic_read(&buf->datalen)) {
+ datalen = atomic_read(&buf->datalen);
+ if (!datalen) {
+ pr_debug("found erased zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
+ zone->name, i, zone->off,
+ zone->buffer_size, datalen);
+ continue;
+ }
+
+ if (datalen < (int)sizeof(*hdr)) {
+ pr_info("found truncated zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
+ zone->name, i, zone->off,
+ zone->buffer_size, datalen);
+ continue;
+ }
+
+ if (zone->buffer_size < datalen) {
pr_info("found overtop zone: %s: id %lu, off %lld, size %zu\n",
zone->name, i, zone->off,
zone->buffer_size);
@@ -439,19 +455,11 @@ static int psz_kmsg_recover_meta(struct psz_context *cxt)
cxt->panic_counter =
max(cxt->panic_counter, hdr->counter);
- if (!atomic_read(&buf->datalen)) {
- pr_debug("found erased zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
- zone->name, i, zone->off,
- zone->buffer_size,
- atomic_read(&buf->datalen));
- continue;
- }
-
if (!is_on_panic())
zone->should_recover = true;
pr_debug("found nice zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
zone->name, i, zone->off,
- zone->buffer_size, atomic_read(&buf->datalen));
+ zone->buffer_size, datalen);
}
return 0;
@@ -962,6 +970,12 @@ static ssize_t psz_kmsg_read(struct pstore_zone *zone,
ssize_t size, hlen = 0;
size = buffer_datalen(zone);
+ if (size < (ssize_t)sizeof(struct psz_kmsg_header)) {
+ atomic_set(&zone->buffer->datalen, 0);
+ atomic_set(&zone->dirty, 0);
+ return -ENOMSG;
+ }
+
/* Clear and skip this kmsg dump record if it has no valid header */
if (psz_kmsg_read_hdr(zone, record)) {
atomic_set(&zone->buffer->datalen, 0);
--
2.51.2