diff mbox

[V2,7/8] dm verity fec: Check result of init_rs()

Message ID 20180419100935.340306831@linutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Thomas Gleixner April 19, 2018, 10:04 a.m. UTC
From: Thomas Gleixner <tglx@linutronix.de>

The allocation of the reed solomon control structure can fail, but
fec_alloc_bufs() ignores that and subsequent operations in dm verity use
the potential NULL pointer unconditionally.

Add a proper check and abort if init_rs() fails.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Segher Boessenkool <segher@kernel.crashing.org>
Cc: Kernel Hardening <kernel-hardening@lists.openwall.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Mike Snitzer <snitzer@redhat.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Andrew Morton <akpm@linuxfoundation.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Alasdair Kergon <agk@redhat.com>

---
 drivers/md/dm-verity-fec.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Comments

Mike Snitzer April 19, 2018, 1:46 p.m. UTC | #1
On Thu, Apr 19 2018 at  6:04am -0400,
Thomas Gleixner <tglx@linutronix.de> wrote:

> From: Thomas Gleixner <tglx@linutronix.de>
> 
> The allocation of the reed solomon control structure can fail, but
> fec_alloc_bufs() ignores that and subsequent operations in dm verity use
> the potential NULL pointer unconditionally.
> 
> Add a proper check and abort if init_rs() fails.

This changelog makes little sense: init_rs() isn't in play relative to
this patch.

And it runs counter to this commit's changelog:

commit 34c96507e8f6be497c15497be05f489fb34c5880
Author: NeilBrown <neilb@suse.com>
Date:   Mon Apr 10 12:13:00 2017 +1000

    dm verity fec: fix GFP flags used with mempool_alloc()

    mempool_alloc() cannot fail for GFP_NOIO allocation, so there is no
    point testing for failure.

    One place the code tested for failure was passing "0" as the GFP
    flags.  This is most unusual and is probably meant to be GFP_NOIO,
    so that is changed.

    Also, allocation from ->extra_pool and ->prealloc_pool are repeated
    before releasing the previous allocation.  This can deadlock if the code
    is servicing a write under high memory pressure.  To avoid deadlocks,
    change these to use GFP_NOWAIT and leave the error handling in place.

    Signed-off-by: NeilBrown <neilb@suse.com>
    Signed-off-by: Mike Snitzer <snitzer@redhat.com>

Seems there is no real need for this patch.  Neil, what do you think?

Thanks,
Mike


> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: Tony Luck <tony.luck@intel.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Segher Boessenkool <segher@kernel.crashing.org>
> Cc: Kernel Hardening <kernel-hardening@lists.openwall.com>
> Cc: Richard Weinberger <richard@nod.at>
> Cc: Mike Snitzer <snitzer@redhat.com>
> Cc: Anton Vorontsov <anton@enomsg.org>
> Cc: Colin Cross <ccross@android.com>
> Cc: Andrew Morton <akpm@linuxfoundation.org>
> Cc: David Woodhouse <dwmw2@infradead.org>
> Cc: Alasdair Kergon <agk@redhat.com>
> 
> ---
>  drivers/md/dm-verity-fec.c |    7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> --- a/drivers/md/dm-verity-fec.c
> +++ b/drivers/md/dm-verity-fec.c
> @@ -308,8 +308,13 @@ static int fec_alloc_bufs(struct dm_veri
>  {
>  	unsigned n;
>  
> -	if (!fio->rs)
> +	if (!fio->rs) {
>  		fio->rs = mempool_alloc(v->fec->rs_pool, GFP_NOIO);
> +		if (!fio->rs) {
> +			DMERR("failed to allocate RS control structure");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	fec_for_each_prealloc_buffer(n) {
>  		if (fio->bufs[n])
> 
> 
> 
> 
>
Thomas Gleixner April 19, 2018, 2:08 p.m. UTC | #2
On Thu, 19 Apr 2018, Mike Snitzer wrote:

> On Thu, Apr 19 2018 at  6:04am -0400,
> Thomas Gleixner <tglx@linutronix.de> wrote:
> 
> > From: Thomas Gleixner <tglx@linutronix.de>
> > 
> > The allocation of the reed solomon control structure can fail, but
> > fec_alloc_bufs() ignores that and subsequent operations in dm verity use
> > the potential NULL pointer unconditionally.
> > 
> > Add a proper check and abort if init_rs() fails.
> 
> This changelog makes little sense: init_rs() isn't in play relative to
> this patch.

	fio->rs = mempool_alloc(v->fec->rs_pool, GFP_NOIO);

        f->rs_pool = mempool_create(num_online_cpus(), fec_rs_alloc,
                                    fec_rs_free, (void *) v);

static void *fec_rs_alloc(gfp_t gfp_mask, void *pool_data)
{
	struct dm_verity *v = (struct dm_verity *)pool_data;

        return init_rs(8, 0x11d, 0, 1, v->fec->roots);
}

So init_rs() is part of the chain, right?

Yes. I missed the NOIO part. But....

> And it runs counter to this commit's changelog:
> 
> commit 34c96507e8f6be497c15497be05f489fb34c5880
> Author: NeilBrown <neilb@suse.com>
> Date:   Mon Apr 10 12:13:00 2017 +1000
> 
>     dm verity fec: fix GFP flags used with mempool_alloc()
> 
>     mempool_alloc() cannot fail for GFP_NOIO allocation, so there is no
>     point testing for failure.
> 
>     One place the code tested for failure was passing "0" as the GFP
>     flags.  This is most unusual and is probably meant to be GFP_NOIO,
>     so that is changed.
> 
>     Also, allocation from ->extra_pool and ->prealloc_pool are repeated
>     before releasing the previous allocation.  This can deadlock if the code
>     is servicing a write under high memory pressure.  To avoid deadlocks,
>     change these to use GFP_NOWAIT and leave the error handling in place.
> 
>     Signed-off-by: NeilBrown <neilb@suse.com>
>     Signed-off-by: Mike Snitzer <snitzer@redhat.com>
> 
> Seems there is no real need for this patch.  Neil, what do you think?

The analysis above forgot to look at the mempool->alloc() callback. So yes,
while the NOIO is good at the mempool level, but init_rs() uses GPF_KERNEL
so there might be a different can of wurms lurking.

Thanks,

	tglx
NeilBrown April 19, 2018, 10:16 p.m. UTC | #3
On Thu, Apr 19 2018, Thomas Gleixner wrote:

> On Thu, 19 Apr 2018, Mike Snitzer wrote:
>
>> On Thu, Apr 19 2018 at  6:04am -0400,
>> Thomas Gleixner <tglx@linutronix.de> wrote:
>> 
>> > From: Thomas Gleixner <tglx@linutronix.de>
>> > 
>> > The allocation of the reed solomon control structure can fail, but
>> > fec_alloc_bufs() ignores that and subsequent operations in dm verity use
>> > the potential NULL pointer unconditionally.
>> > 
>> > Add a proper check and abort if init_rs() fails.
>> 
>> This changelog makes little sense: init_rs() isn't in play relative to
>> this patch.
>
> 	fio->rs = mempool_alloc(v->fec->rs_pool, GFP_NOIO);
>
>         f->rs_pool = mempool_create(num_online_cpus(), fec_rs_alloc,
>                                     fec_rs_free, (void *) v);
>
> static void *fec_rs_alloc(gfp_t gfp_mask, void *pool_data)
> {
> 	struct dm_verity *v = (struct dm_verity *)pool_data;
>
>         return init_rs(8, 0x11d, 0, 1, v->fec->roots);
> }
>
> So init_rs() is part of the chain, right?
>
> Yes. I missed the NOIO part. But....
>
>> And it runs counter to this commit's changelog:
>> 
>> commit 34c96507e8f6be497c15497be05f489fb34c5880
>> Author: NeilBrown <neilb@suse.com>
>> Date:   Mon Apr 10 12:13:00 2017 +1000
>> 
>>     dm verity fec: fix GFP flags used with mempool_alloc()
>> 
>>     mempool_alloc() cannot fail for GFP_NOIO allocation, so there is no
>>     point testing for failure.
>> 
>>     One place the code tested for failure was passing "0" as the GFP
>>     flags.  This is most unusual and is probably meant to be GFP_NOIO,
>>     so that is changed.
>> 
>>     Also, allocation from ->extra_pool and ->prealloc_pool are repeated
>>     before releasing the previous allocation.  This can deadlock if the code
>>     is servicing a write under high memory pressure.  To avoid deadlocks,
>>     change these to use GFP_NOWAIT and leave the error handling in place.
>> 
>>     Signed-off-by: NeilBrown <neilb@suse.com>
>>     Signed-off-by: Mike Snitzer <snitzer@redhat.com>
>> 
>> Seems there is no real need for this patch.  Neil, what do you think?

I think the code is correct as-is.

>
> The analysis above forgot to look at the mempool->alloc() callback. So yes,
> while the NOIO is good at the mempool level, but init_rs() uses GPF_KERNEL
> so there might be a different can of wurms lurking.

The ->alloc call back is not relevant to the question of when
mempool_alloc() can return NULL.
If the ->alloc() callback returns a non-NULL value, it will be returned
by mempool_alloc().
If it returns NULL, that will not be returned.

mempool_alloc() *only* returns NULL in one place:

	if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
		spin_unlock_irqrestore(&pool->lock, flags);
		return NULL;
	}

so a NULL return is purely dependent on the GFP flags passed.
GFP_NOIO contains __GFP_DIRECT_RECLAIM, so NULL cannot be returned.

It seems quite broken that init_rs() uses GFP_KERNEL. It should take a
gfp_t arg for the allocation.
If the mempool_alloc() above really needs GFP_NOIO, then it could
theoretically deadlock as it performs a GFP_KERNEL allocation inside
rs_init().  So in that sense, the code is not correct as-is.
It could possibly be fixed by calling memalloc_noio_save() /
memalloc_noio_restore() around the call to init_rs() in fec_rs_alloc().


Thanks,
NeilBrown
Thomas Gleixner April 20, 2018, 8:49 a.m. UTC | #4
On Fri, 20 Apr 2018, NeilBrown wrote:
> On Thu, Apr 19 2018, Thomas Gleixner wrote:
> > The analysis above forgot to look at the mempool->alloc() callback. So yes,
> > while the NOIO is good at the mempool level, but init_rs() uses GPF_KERNEL
> > so there might be a different can of wurms lurking.
> 
> The ->alloc call back is not relevant to the question of when
> mempool_alloc() can return NULL.
> If the ->alloc() callback returns a non-NULL value, it will be returned
> by mempool_alloc().
> If it returns NULL, that will not be returned.

Yes, as I said before, I missed the NOIO flag.

> 
> mempool_alloc() *only* returns NULL in one place:
> 
> 	if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
> 		spin_unlock_irqrestore(&pool->lock, flags);
> 		return NULL;
> 	}
> 
> so a NULL return is purely dependent on the GFP flags passed.
> GFP_NOIO contains __GFP_DIRECT_RECLAIM, so NULL cannot be returned.
> 
> It seems quite broken that init_rs() uses GFP_KERNEL. It should take a
> gfp_t arg for the allocation.

Well, init_rs() was that way before somebody used it in a mempool_alloc()
callback. And all other users are fine with GFP_KERNEL AFAICT.

> If the mempool_alloc() above really needs GFP_NOIO, then it could
> theoretically deadlock as it performs a GFP_KERNEL allocation inside
> rs_init().  So in that sense, the code is not correct as-is.
> It could possibly be fixed by calling memalloc_noio_save() /
> memalloc_noio_restore() around the call to init_rs() in fec_rs_alloc().

No, we surely can add a gfp aware version of init_rs(). It's trivial enough
to do.

Thanks,

	tglx
diff mbox

Patch

--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -308,8 +308,13 @@  static int fec_alloc_bufs(struct dm_veri
 {
 	unsigned n;
 
-	if (!fio->rs)
+	if (!fio->rs) {
 		fio->rs = mempool_alloc(v->fec->rs_pool, GFP_NOIO);
+		if (!fio->rs) {
+			DMERR("failed to allocate RS control structure");
+			return -ENOMEM;
+		}
+	}
 
 	fec_for_each_prealloc_buffer(n) {
 		if (fio->bufs[n])