[PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads

Kartik Nair posted 1 patch 1 month ago
mm/zsmalloc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
[PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Kartik Nair 1 month ago
Pages allocated via alloc_zpdesc() use alloc_pages_node() without
__GFP_ZERO, leaving physical memory uninitialized. When a compressed
object spans two physical pages in a zspage, zs_obj_read_sg_begin()
sets up a scatterlist pointing directly at the raw second page. If the
second page was freshly allocated and never written beyond the object
boundary, KMSAN detects reads of uninitialized memory downstream in
the decompressor (e.g. sw842_decompress reading the CRC trailer).

Fix this by passing __GFP_ZERO to alloc_zpdesc() in alloc_zspage() so
all pages backing a zspage are zero-initialized at allocation time.

Reported-by: syzbot+8f77ff6144a73f0cf71b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=8f77ff6144a73f0cf71b
Signed-off-by: Kartik Nair <contact.kartikn@gmail.com>
---
 mm/zsmalloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 63128ddb7..5bbd417d3 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -951,7 +951,7 @@ static struct zspage *alloc_zspage(struct zs_pool *pool,
 	for (i = 0; i < class->pages_per_zspage; i++) {
 		struct zpdesc *zpdesc;
 
-		zpdesc = alloc_zpdesc(gfp, nid);
+		zpdesc = alloc_zpdesc(gfp | __GFP_ZERO, nid);
 		if (!zpdesc) {
 			while (--i >= 0) {
 				zpdesc_dec_zone_page_state(zpdescs[i]);
-- 
2.39.5 (Apple Git-154)
Re: [PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Andrew Morton 1 month ago
On Tue, 12 May 2026 03:06:58 +0530 Kartik Nair <contact.kartikn@gmail.com> wrote:

> Pages allocated via alloc_zpdesc() use alloc_pages_node() without
> __GFP_ZERO, leaving physical memory uninitialized. When a compressed
> object spans two physical pages in a zspage, zs_obj_read_sg_begin()
> sets up a scatterlist pointing directly at the raw second page. If the
> second page was freshly allocated and never written beyond the object
> boundary, KMSAN detects reads of uninitialized memory downstream in
> the decompressor (e.g. sw842_decompress reading the CRC trailer).
> 
> Fix this by passing __GFP_ZERO to alloc_zpdesc() in alloc_zspage() so
> all pages backing a zspage are zero-initialized at allocation time.
> 
> Reported-by: syzbot+8f77ff6144a73f0cf71b@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=8f77ff6144a73f0cf71b
> Signed-off-by: Kartik Nair <contact.kartikn@gmail.com>

Thanks.

> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -951,7 +951,7 @@ static struct zspage *alloc_zspage(struct zs_pool *pool,
>  	for (i = 0; i < class->pages_per_zspage; i++) {
>  		struct zpdesc *zpdesc;
>  
> -		zpdesc = alloc_zpdesc(gfp, nid);
> +		zpdesc = alloc_zpdesc(gfp | __GFP_ZERO, nid);
>  		if (!zpdesc) {
>  			while (--i >= 0) {
>  				zpdesc_dec_zone_page_state(zpdescs[i]);

Decompressing uninitialized memory sounds rather bad, so I'll add a
cc:stable to this.

I think the Fixes: target is 56e5a103a721 ("zsmalloc: prefer the the
original page's node for compressed data").  Can people please check
this when reviewing?
Re: [PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Sergey Senozhatsky 1 month ago
Adding Yosry and Herbert,

On (26/05/12 14:47), Andrew Morton wrote:
> > Pages allocated via alloc_zpdesc() use alloc_pages_node() without
> > __GFP_ZERO, leaving physical memory uninitialized. When a compressed
> > object spans two physical pages in a zspage, zs_obj_read_sg_begin()
> > sets up a scatterlist pointing directly at the raw second page. If the
> > second page was freshly allocated and never written beyond the object
> > boundary, KMSAN detects reads of uninitialized memory downstream in
> > the decompressor (e.g. sw842_decompress reading the CRC trailer).

I don't get this.  How can sw842_decompress() read more bytes than
it's told to decompress.  We first compress and store the object,
before we load and decompress, reading past the known compressed
object size (which we pass to decompress function) should not happen.
Yosry, Herbert, any ideas?

> > Fix this by passing __GFP_ZERO to alloc_zpdesc() in alloc_zspage() so
> > all pages backing a zspage are zero-initialized at allocation time.
> > 
> > Reported-by: syzbot+8f77ff6144a73f0cf71b@syzkaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=8f77ff6144a73f0cf71b
> > Signed-off-by: Kartik Nair <contact.kartikn@gmail.com>
> 
> Thanks.
> 
> > --- a/mm/zsmalloc.c
> > +++ b/mm/zsmalloc.c
> > @@ -951,7 +951,7 @@ static struct zspage *alloc_zspage(struct zs_pool *pool,
> >  	for (i = 0; i < class->pages_per_zspage; i++) {
> >  		struct zpdesc *zpdesc;
> >  
> > -		zpdesc = alloc_zpdesc(gfp, nid);
> > +		zpdesc = alloc_zpdesc(gfp | __GFP_ZERO, nid);
> >  		if (!zpdesc) {
> >  			while (--i >= 0) {
> >  				zpdesc_dec_zone_page_state(zpdescs[i]);
> 
> Decompressing uninitialized memory sounds rather bad, so I'll add a
> cc:stable to this.
[..]
> I think the Fixes: target is 56e5a103a721 ("zsmalloc: prefer the the
> original page's node for compressed data").  Can people please check
> this when reviewing?

If we need to pick a zsmalloc commit, then this probably can be related
to zs_obj_read_sg_begin() API (which is used only by zswap at the moemnt),
so probably the Fixes: tag be dc2e4982cb018 (zsmalloc: introduce SG-list
based object read API).
Re: [PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Yosry Ahmed 1 month ago
On Tue, May 12, 2026 at 7:47 PM Sergey Senozhatsky
<senozhatsky@chromium.org> wrote:
>
> Adding Yosry and Herbert,
>
> On (26/05/12 14:47), Andrew Morton wrote:
> > > Pages allocated via alloc_zpdesc() use alloc_pages_node() without
> > > __GFP_ZERO, leaving physical memory uninitialized. When a compressed
> > > object spans two physical pages in a zspage, zs_obj_read_sg_begin()
> > > sets up a scatterlist pointing directly at the raw second page. If the
> > > second page was freshly allocated and never written beyond the object
> > > boundary, KMSAN detects reads of uninitialized memory downstream in
> > > the decompressor (e.g. sw842_decompress reading the CRC trailer).
>
> I don't get this.  How can sw842_decompress() read more bytes than
> it's told to decompress.  We first compress and store the object,
> before we load and decompress, reading past the known compressed
> object size (which we pass to decompress function) should not happen.
> Yosry, Herbert, any ideas?

I agree it's weird that sw842_decompress() is reading past the
compressed length. I didn't look at the code but maybe it's ready in
chunks of N bytes (e.g. 8 bytes) so it's reading a few bytes past the
end of the compressed length?

Not sure if we should add __GFP_ZERO in zsmalloc just in case, or if
that can hide other problems.

>
> > > Fix this by passing __GFP_ZERO to alloc_zpdesc() in alloc_zspage() so
> > > all pages backing a zspage are zero-initialized at allocation time.
> > >
> > > Reported-by: syzbot+8f77ff6144a73f0cf71b@syzkaller.appspotmail.com
> > > Closes: https://syzkaller.appspot.com/bug?extid=8f77ff6144a73f0cf71b
> > > Signed-off-by: Kartik Nair <contact.kartikn@gmail.com>
> >
> > Thanks.
> >
> > > --- a/mm/zsmalloc.c
> > > +++ b/mm/zsmalloc.c
> > > @@ -951,7 +951,7 @@ static struct zspage *alloc_zspage(struct zs_pool *pool,
> > >     for (i = 0; i < class->pages_per_zspage; i++) {
> > >             struct zpdesc *zpdesc;
> > >
> > > -           zpdesc = alloc_zpdesc(gfp, nid);
> > > +           zpdesc = alloc_zpdesc(gfp | __GFP_ZERO, nid);
> > >             if (!zpdesc) {
> > >                     while (--i >= 0) {
> > >                             zpdesc_dec_zone_page_state(zpdescs[i]);
> >
> > Decompressing uninitialized memory sounds rather bad, so I'll add a
> > cc:stable to this.
> [..]
> > I think the Fixes: target is 56e5a103a721 ("zsmalloc: prefer the the
> > original page's node for compressed data").  Can people please check
> > this when reviewing?
>
> If we need to pick a zsmalloc commit, then this probably can be related
> to zs_obj_read_sg_begin() API (which is used only by zswap at the moemnt),
> so probably the Fixes: tag be dc2e4982cb018 (zsmalloc: introduce SG-list
> based object read API).

Agreed.
Re: [PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Herbert Xu 1 month ago
On Wed, May 13, 2026 at 11:47:41AM +0900, Sergey Senozhatsky wrote:
> Adding Yosry and Herbert,
> 
> On (26/05/12 14:47), Andrew Morton wrote:
> > > Pages allocated via alloc_zpdesc() use alloc_pages_node() without
> > > __GFP_ZERO, leaving physical memory uninitialized. When a compressed
> > > object spans two physical pages in a zspage, zs_obj_read_sg_begin()
> > > sets up a scatterlist pointing directly at the raw second page. If the
> > > second page was freshly allocated and never written beyond the object
> > > boundary, KMSAN detects reads of uninitialized memory downstream in
> > > the decompressor (e.g. sw842_decompress reading the CRC trailer).
> 
> I don't get this.  How can sw842_decompress() read more bytes than
> it's told to decompress.  We first compress and store the object,
> before we load and decompress, reading past the known compressed
> object size (which we pass to decompress function) should not happen.
> Yosry, Herbert, any ideas?

It sounds like a bug in 842.  I'll look into it.

Thanks for pointer.
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Re: [PATCH] zsmalloc: zero-initialize zspage memory to prevent KMSAN uninit reads
Posted by Sergey Senozhatsky 1 month ago
On (26/05/13 13:43), Herbert Xu wrote:
> On Wed, May 13, 2026 at 11:47:41AM +0900, Sergey Senozhatsky wrote:
> > Adding Yosry and Herbert,
> > 
> > On (26/05/12 14:47), Andrew Morton wrote:
> > > > Pages allocated via alloc_zpdesc() use alloc_pages_node() without
> > > > __GFP_ZERO, leaving physical memory uninitialized. When a compressed
> > > > object spans two physical pages in a zspage, zs_obj_read_sg_begin()
> > > > sets up a scatterlist pointing directly at the raw second page. If the
> > > > second page was freshly allocated and never written beyond the object
> > > > boundary, KMSAN detects reads of uninitialized memory downstream in
> > > > the decompressor (e.g. sw842_decompress reading the CRC trailer).
> > 
> > I don't get this.  How can sw842_decompress() read more bytes than
> > it's told to decompress.  We first compress and store the object,
> > before we load and decompress, reading past the known compressed
> > object size (which we pass to decompress function) should not happen.
> > Yosry, Herbert, any ideas?
> 
> It sounds like a bug in 842.  I'll look into it.

Thanks!  The report doesn't have a reproducer, that is always alarming,
a big red flag.  Might be entirely unrelated to 842/zsmalloc/zswap.