mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [BUG] erofs: LZMA stream state NULL deref after failed dict grow
@ 2026-09-02  2:04 Qingyu Zhang
  2026-09-02  2:20 ` Gao Xiang
  0 siblings, 1 reply; 2+ messages in thread
From: Qingyu Zhang @ 2026-09-02  2:04 UTC (permalink / raw)
  To: Gao Xiang, Chao Yu; +Cc: linux-erofs, linux-kernel

Hello,

z_erofs_load_lzma_config() can leave strm->state == NULL while still
publishing z_erofs_lzma_max_dictsize. The next mount of the same
image skips realloc and decompress NULL-derefs.

Type: null-pointer dereference

* Summary

       for (strm = head; strm; strm = strm->next) {
               if (strm->state)
                       xz_dec_microlzma_end(strm->state);
               strm->state = xz_dec_microlzma_alloc(..., dict_size);
               if (!strm->state)
                       err = -ENOMEM;
       }
       ...
       z_erofs_lzma_max_dictsize = dict_size;  /* even if err */

On alloc failure the old state is already gone. The next mount sees
max_dictsize >= dict_size and returns 0 without retrying. Then:

       xz_dec_microlzma_reset(strm->state, ...);  /* state is NULL */

* Affected

622ceaddb764. Needs CONFIG_EROFS_FS_ZIP_LZMA, a microlzma image,
and a failed xz_dec_microlzma_alloc (fault injection or memory
pressure). KASAN recommended.

* Reproduction

1. mkfs.erofs -z lzma,dictsize=4096 lzma.img dir/
2. Force xz_dec_microlzma_alloc() to fail on first mount
  (poc/fail_lzma.c: kprobe sets dict_size=0).
3. First mount returns -ENOMEM.
4. Disarm the probe, mount the same image again (succeeds via the
  max_dictsize early-out).
5. cat /mnt/payload

KASAN: null-ptr-deref in xz_dec_microlzma_reset from
z_erofs_lzma_decompress.

PoC: poc/run.sh. Use lzma_streams=1 to keep the pool to one stream.

* Expected

Failed grow keeps the previous xz state (or retries next mount) and
does not bump z_erofs_lzma_max_dictsize.

* Actual

NULL state + published max_dictsize -> NPD on the next read.

Please consider the suggested patch.

Thanks.

Suggested patch
```
diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c
index 6b0cdb446c6a..3719c6f83f8a 100644
--- a/fs/erofs/decompressor_lzma.c
+++ b/fs/erofs/decompressor_lzma.c
@@ -128,11 +128,16 @@ static int z_erofs_load_lzma_config(struct
super_block *sb,
    err = 0;
    /* 2. walk each isolated stream and grow max dict_size if needed */
    for (strm = head; strm; strm = strm->next) {
+        struct xz_dec_microlzma *state;
+
+        state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
+        if (!state) {
+            err = -ENOMEM;
+            continue;
+        }
        if (strm->state)
            xz_dec_microlzma_end(strm->state);
-        strm->state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
-        if (!strm->state)
-            err = -ENOMEM;
+        strm->state = state;
    }

    /* 3. push back all to the global list and update max dict_size */
@@ -142,7 +147,8 @@ static int z_erofs_load_lzma_config(struct super_block *sb,
    spin_unlock(&z_erofs_lzma_lock);
    wake_up_all(&z_erofs_lzma_wq);

-    z_erofs_lzma_max_dictsize = dict_size;
+    if (!err)
+        z_erofs_lzma_max_dictsize = dict_size;
    mutex_unlock(&lzma_resize_mutex);
    return err;
}
```

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-02  2:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-02  2:04 [BUG] erofs: LZMA stream state NULL deref after failed dict grow Qingyu Zhang
2026-09-02  2:20 ` Gao Xiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®