mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] jffs2: reject undersized input in jffs2_dynrubin_decompress
@ 2026-10-22 18:19 Jay Vadayath
  0 siblings, 0 replies; only message in thread
From: Jay Vadayath @ 2026-10-22 18:19 UTC (permalink / raw)
  To: David Woodhouse, Richard Weinberger; +Cc: linux-mtd, linux-kernel, Jay Vadayath

jffs2_dynrubin_decompress() reads an 8-byte probability prefix
(data_in[0..7]) out of a compressed data node and then passes
sourcelen - 8 (csize - 8) as the input length to rubin_do_decompress().
The compressed buffer is a kmalloc(csize) slab filled from the on-disk
node, but nothing checks that csize is at least 8 before the prefix is
read or before the subtraction. When a node declares csize < 8 the read
of data_in[0..7] already runs past the end of the slab, and sourcelen - 8
underflows to roughly 4 GiB, which defeats the pullbit() bound and lets
rubin_do_decompress() keep reading unbounded.

A hostile jffs2 image that carries a data node with
compr == JFFS2_COMPR_DYNRUBIN (on-disk id 0x03 routes to this
decompressor) and a tiny csize therefore causes an out-of-bounds read of
the compressed-input slab. Reachable from any user who can cause the file
to be read on a mounted attacker-supplied jffs2 image (e.g. cat on the
file; the image dd'd to /dev/mtdblock0 via CONFIG_MTD_MTDRAM). The
decompressor stays registered even when compression is disabled, so the
code path is always live.

  BUG: KASAN: slab-out-of-bounds in jffs2_dynrubin_decompress+0x170/0x1c0
  Read of size 1 at addr ffff888006b45a64 by task poc/125
  Call Trace:
   dump_stack_lvl+0x64/0x80
   print_report+0xd0/0x630
   kasan_report+0xe5/0x120
   jffs2_dynrubin_decompress+0x170/0x1c0
   jffs2_decompress
   jffs2_read_dnode
   ...

Reject any node whose compressed size is smaller than the 8-byte prefix
(if (sourcelen < 8) return -1;), and additionally bound pullbit() so that
a stream claiming more output than the input provides is treated as a run
of zero bits instead of walking off the kmalloc(csize) buffer.

This bug was discovered by Artiphishell, which
generated a from-scratch jffs2 image (cleanmarker + dirent + inode node
with CRCs computed via crc32_le) and a mount+cat reproducer that reliably
triggers the KASAN report on an unpatched kernel (csize=4 -> kmalloc(4)
-> reads data_in[0..7]). The fix below was drafted with the Claude coding
assistant; a userspace reproducer is available on request.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jay Vadayath <jay@artiphishell.com>
---
 fs/jffs2/compr_rubin.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
index 9854253d0108..98bbab157c62 100644
--- a/fs/jffs2/compr_rubin.c
+++ b/fs/jffs2/compr_rubin.c
@@ -78,6 +78,12 @@ static inline int pullbit(struct pushpull *pp)
 {
 	int bit;
 
+	/* On decompress buflen bounds the input; treat an overrun as zero bits. */
+	if ((pp->ofs >> 3) >= pp->buflen) {
+		pp->ofs++;
+		return 0;
+	}
+
 	bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
 
 	pp->ofs++;
@@ -392,6 +398,10 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in,
 	int bits[8];
 	int c;
 
+	/* Need the 8-byte prefix; also avoids the sourcelen-8 underflow. */
+	if (sourcelen < 8)
+		return -1;
+
 	for (c=0; c<8; c++)
 		bits[c] = data_in[c];
 
-- 
2.51.2


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-22 18:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 18:19 [PATCH] jffs2: reject undersized input in jffs2_dynrubin_decompress Jay Vadayath

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®