diff mbox

mmc: pwrseq: Use kmalloc_array instead of stack VLA

Message ID 1522045994-21913-1-git-send-email-me@tobin.cc (mailing list archive)
State New, archived
Headers show

Commit Message

Tobin Harding March 26, 2018, 6:33 a.m. UTC
The use of stack Variable Length Arrays needs to be avoided, as they
can be a vector for stack exhaustion, which can be both a runtime bug
(kernel Oops) or a security flaw (overwriting memory beyond the
stack). Also, in general, as code evolves it is easy to lose track of
how big a VLA can get. Thus, we can end up having runtime failures
that are hard to debug. As part of the directive[1] to remove all VLAs
from the kernel, and build with -Wvla.

Currently driver is using a VLA declared using the number of descriptors.  This
array is used to store integer values and is later used as an argument to
`gpiod_set_array_value_cansleep()` This can be avoided by using
`kmalloc_array()` to allocate memory for the array of integer values.  Memory is
free'd before return from function.

From the code it appears that it is safe to sleep so we can use GFP_KERNEL
(based _cansleep() suffix of function `gpiod_set_array_value_cansleep()`.

It can be expected that this patch will result in a small increase in overhead
due to the use of `kmalloc_array()`

[1] https://lkml.org/lkml/2018/3/7/621

Signed-off-by: Tobin C. Harding <me@tobin.cc>
---

Patch is untested.  I was not able to fully grok the call chain involved
with this change so was unable to quantify the total degradation
of performance.

Line calling to gpiod_set_array_value_cansleep() is 83 characters long,
patch attempts to leave the code as clean as possible.  Open to
suggestions for improvement.  I tried various forms of while loop and
pointer arithmetic for the setting of `values` but in the end settled
for array indexing, again open to suggestions.

thanks,
Tobin.

 drivers/mmc/core/pwrseq_simple.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

Comments

Ulf Hansson April 4, 2018, 12:45 p.m. UTC | #1
On 26 March 2018 at 08:33, Tobin C. Harding <me@tobin.cc> wrote:
> The use of stack Variable Length Arrays needs to be avoided, as they
> can be a vector for stack exhaustion, which can be both a runtime bug
> (kernel Oops) or a security flaw (overwriting memory beyond the
> stack). Also, in general, as code evolves it is easy to lose track of
> how big a VLA can get. Thus, we can end up having runtime failures
> that are hard to debug. As part of the directive[1] to remove all VLAs
> from the kernel, and build with -Wvla.
>
> Currently driver is using a VLA declared using the number of descriptors.  This
> array is used to store integer values and is later used as an argument to
> `gpiod_set_array_value_cansleep()` This can be avoided by using
> `kmalloc_array()` to allocate memory for the array of integer values.  Memory is
> free'd before return from function.
>
> From the code it appears that it is safe to sleep so we can use GFP_KERNEL
> (based _cansleep() suffix of function `gpiod_set_array_value_cansleep()`.
>
> It can be expected that this patch will result in a small increase in overhead
> due to the use of `kmalloc_array()`
>
> [1] https://lkml.org/lkml/2018/3/7/621
>
> Signed-off-by: Tobin C. Harding <me@tobin.cc>

Thanks, queued for 3.18!

Kind regards
Uffe

> ---
>
> Patch is untested.  I was not able to fully grok the call chain involved
> with this change so was unable to quantify the total degradation
> of performance.
>
> Line calling to gpiod_set_array_value_cansleep() is 83 characters long,
> patch attempts to leave the code as clean as possible.  Open to
> suggestions for improvement.  I tried various forms of while loop and
> pointer arithmetic for the setting of `values` but in the end settled
> for array indexing, again open to suggestions.
>
> thanks,
> Tobin.
>
>  drivers/mmc/core/pwrseq_simple.c | 14 +++++++++-----
>  1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
> index 13ef162cf066..a8b9fee4d62a 100644
> --- a/drivers/mmc/core/pwrseq_simple.c
> +++ b/drivers/mmc/core/pwrseq_simple.c
> @@ -40,14 +40,18 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
>         struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>
>         if (!IS_ERR(reset_gpios)) {
> -               int i;
> -               int values[reset_gpios->ndescs];
> +               int i, *values;
> +               int nvalues = reset_gpios->ndescs;
>
> -               for (i = 0; i < reset_gpios->ndescs; i++)
> +               values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
> +               if (!values)
> +                       return;
> +
> +               for (i = 0; i < nvalues; i++)
>                         values[i] = value;
>
> -               gpiod_set_array_value_cansleep(
> -                       reset_gpios->ndescs, reset_gpios->desc, values);
> +               gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
> +               kfree(values);
>         }
>  }
>
> --
> 2.7.4
>
Kees Cook April 4, 2018, 4:23 p.m. UTC | #2
On Wed, Apr 4, 2018 at 5:45 AM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On 26 March 2018 at 08:33, Tobin C. Harding <me@tobin.cc> wrote:
>> The use of stack Variable Length Arrays needs to be avoided, as they
>> can be a vector for stack exhaustion, which can be both a runtime bug
>> (kernel Oops) or a security flaw (overwriting memory beyond the
>> stack). Also, in general, as code evolves it is easy to lose track of
>> how big a VLA can get. Thus, we can end up having runtime failures
>> that are hard to debug. As part of the directive[1] to remove all VLAs
>> from the kernel, and build with -Wvla.
>>
>> Currently driver is using a VLA declared using the number of descriptors.  This
>> array is used to store integer values and is later used as an argument to
>> `gpiod_set_array_value_cansleep()` This can be avoided by using
>> `kmalloc_array()` to allocate memory for the array of integer values.  Memory is
>> free'd before return from function.
>>
>> From the code it appears that it is safe to sleep so we can use GFP_KERNEL
>> (based _cansleep() suffix of function `gpiod_set_array_value_cansleep()`.
>>
>> It can be expected that this patch will result in a small increase in overhead
>> due to the use of `kmalloc_array()`
>>
>> [1] https://lkml.org/lkml/2018/3/7/621
>>
>> Signed-off-by: Tobin C. Harding <me@tobin.cc>
>
> Thanks, queued for 3.18!

Time travel! ;)

-Kees
diff mbox

Patch

diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 13ef162cf066..a8b9fee4d62a 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,14 +40,18 @@  static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
 	struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
 
 	if (!IS_ERR(reset_gpios)) {
-		int i;
-		int values[reset_gpios->ndescs];
+		int i, *values;
+		int nvalues = reset_gpios->ndescs;
 
-		for (i = 0; i < reset_gpios->ndescs; i++)
+		values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
+		if (!values)
+			return;
+
+		for (i = 0; i < nvalues; i++)
 			values[i] = value;
 
-		gpiod_set_array_value_cansleep(
-			reset_gpios->ndescs, reset_gpios->desc, values);
+		gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
+		kfree(values);
 	}
 }