Re: [PATCH] PM: hibernate: Wait for in-flight reads before freeing the read-ahead ring
From: Rafael J. Wysocki (Intel)
Date: Fri Nov 13 2026 - 15:55:11 EST
On Wed, Sep 30, 2026 at 4:09 PM Andrea Parri <parri.andrea@xxxxxxxxx> wrote:
>
> load_compressed_image() reads the image ahead into a ring of pages with
> asynchronous bios, and waits for them only when fewer than CMP_PAGES
> pages are buffered. From the second loop pass on, reads into the rest of
> the ring are thus in flight while buffered data is parsed and
> decompressed.
>
> The exits taken on an invalid compressed length, a failed decompression,
> an invalid uncompressed length or a snapshot_write_next() error jump to
> out_finish without waiting for those reads. The function then frees the
> ring pages and returns, releasing the hib_bio_batch on its stack, while
> the outstanding bios still write into the freed pages and hib_end_io()
> still updates and wakes the stale batch.
>
> A corrupted image is what fails these checks, as the CRC32 is only
> compared once the whole image has been loaded. With such an image, the
> resume fails with "Failed to load image, recovering." and the boot
> continues on top of the resulting memory corruption.
>
> Wait for the batch at out_finish, as save_compressed_image() and
> load_image() already do before releasing their buffers. hib_wait_io()
> returns at once when no reads are outstanding, so exits without
> read-ahead in flight are unaffected.
>
> This was found by code inspection. It has only been build-tested; no
> reproducer was run.
>
> Fixes: 081a9d043c98 ("PM / Hibernate: Improve performance of LZO/plain hibernation, checksum image")
> Assisted-by: LLM
> Signed-off-by: Andrea Parri <parri.andrea@xxxxxxxxx>
> ---
> kernel/power/swap.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> index c78f1593600bb..49b3da89b49d4 100644
> --- a/kernel/power/swap.c
> +++ b/kernel/power/swap.c
> @@ -1198,6 +1198,7 @@ static int load_compressed_image(struct swap_map_handle *handle,
> {
> unsigned int m;
> int ret = 0;
> + int err2;
> int eof = 0;
> struct hib_bio_batch hb;
> ktime_t start;
> @@ -1478,6 +1479,10 @@ static int load_compressed_image(struct swap_map_handle *handle,
> }
>
> out_finish:
> + /* Error exits may leave reads in flight into page[]. */
> + err2 = hib_wait_io(&hb);
> + if (!ret)
> + ret = err2;
> if (crc->run_threads) {
> wait_event(crc->done, atomic_read_acquire(&crc->stop));
> atomic_set(&crc->stop, 0);
> --
Applied as 7.5 material, thanks!