diff mbox series

[v3] clk: mvebu: Prevent division by zero in clk_double_div_recalc_rate()

Message ID 20240917132201.17513-1-adiupina@astralinux.ru (mailing list archive)
State Changes Requested, archived
Headers show
Series [v3] clk: mvebu: Prevent division by zero in clk_double_div_recalc_rate() | expand

Commit Message

Alexandra Diupina Sept. 17, 2024, 1:22 p.m. UTC
get_div() may return zero, so it is necessary to check
before calling DIV_ROUND_UP_ULL().

Return value of get_div() depends on reg1, reg2, shift1, shift2
fields of clk_double_div structure which are filled using the
PERIPH_DOUBLEDIV macro. This macro is called from the
PERIPH_CLK_FULL_DD and PERIPH_CLK_MUX_DD macros (the last 4 arguments).

It is not known exactly what values can be contained in the registers
at the addresses DIV_SEL0, DIV_SEL1, DIV_SEL2, so the final value of
div can be zero. Print an error message and return 0 in this case.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

Fixes: 8ca4746a78ab ("clk: mvebu: Add the peripheral clock driver for Armada 3700")
Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru>
---
v3: fix indentation
v2: added explanations to the commit message and printing 
of an error message when div==0
 drivers/clk/mvebu/armada-37xx-periph.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Comments

Stephen Boyd Sept. 19, 2024, 10:24 a.m. UTC | #1
Quoting Alexandra Diupina (2024-09-17 06:22:01)
> get_div() may return zero, so it is necessary to check
> before calling DIV_ROUND_UP_ULL().
> 
> Return value of get_div() depends on reg1, reg2, shift1, shift2
> fields of clk_double_div structure which are filled using the
> PERIPH_DOUBLEDIV macro. This macro is called from the
> PERIPH_CLK_FULL_DD and PERIPH_CLK_MUX_DD macros (the last 4 arguments).
> 
> It is not known exactly what values can be contained in the registers
> at the addresses DIV_SEL0, DIV_SEL1, DIV_SEL2, so the final value of
> div can be zero. Print an error message and return 0 in this case.
> 
> Found by Linux Verification Center (linuxtesting.org) with SVACE.
> 
> Fixes: 8ca4746a78ab ("clk: mvebu: Add the peripheral clock driver for Armada 3700")
> Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru>
> ---
> v3: fix indentation
> v2: added explanations to the commit message and printing 
> of an error message when div==0

Please stop sending as replies to previous patches.

>  drivers/clk/mvebu/armada-37xx-periph.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
> index 8701a58a5804..b32c6d4d7ee5 100644
> --- a/drivers/clk/mvebu/armada-37xx-periph.c
> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
> @@ -343,7 +343,12 @@ static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
>         div = get_div(double_div->reg1, double_div->shift1);
>         div *= get_div(double_div->reg2, double_div->shift2);
>  
> -       return DIV_ROUND_UP_ULL((u64)parent_rate, div);
> +       if (!div) {
> +               pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);

hw->init is set to NULL after registration (see clk_register() code). If
div is 0 what does the hardware do?

> +               return 0;
> +       } else {
> +               return DIV_ROUND_UP_ULL((u64)parent_rate, div);
> +       }
>  }
>
Alexandra Diupina Sept. 24, 2024, 1:14 p.m. UTC | #2
Hi


19/09/24 13:24, Stephen Boyd пишет:
> Quoting Alexandra Diupina (2024-09-17 06:22:01)
>> get_div() may return zero, so it is necessary to check
>> before calling DIV_ROUND_UP_ULL().
>>
>> Return value of get_div() depends on reg1, reg2, shift1, shift2
>> fields of clk_double_div structure which are filled using the
>> PERIPH_DOUBLEDIV macro. This macro is called from the
>> PERIPH_CLK_FULL_DD and PERIPH_CLK_MUX_DD macros (the last 4 arguments).
>>
>> It is not known exactly what values can be contained in the registers
>> at the addresses DIV_SEL0, DIV_SEL1, DIV_SEL2, so the final value of
>> div can be zero. Print an error message and return 0 in this case.
>>
>> Found by Linux Verification Center (linuxtesting.org) with SVACE.
>>
>> Fixes: 8ca4746a78ab ("clk: mvebu: Add the peripheral clock driver for Armada 3700")
>> Signed-off-by: Alexandra Diupina <adiupina@astralinux.ru>
>> ---
>> v3: fix indentation
>> v2: added explanations to the commit message and printing
>> of an error message when div==0
> Please stop sending as replies to previous patches.
>
>>   drivers/clk/mvebu/armada-37xx-periph.c | 7 ++++++-
>>   1 file changed, 6 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
>> index 8701a58a5804..b32c6d4d7ee5 100644
>> --- a/drivers/clk/mvebu/armada-37xx-periph.c
>> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
>> @@ -343,7 +343,12 @@ static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
>>          div = get_div(double_div->reg1, double_div->shift1);
>>          div *= get_div(double_div->reg2, double_div->shift2);
>>   
>> -       return DIV_ROUND_UP_ULL((u64)parent_rate, div);
>> +       if (!div) {
>> +               pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);
> hw->init is set to NULL after registration (see clk_register() code). If
> div is 0 what does the hardware do?

Thanks for noticing the error. Yes, hw->init is set to zero,
I will replace that code with clk_hw_get_name(hw).
If the value of div is 0, should I return 0 as stated in the
comment for .recalc_rate (in struct clk_ops) or should I
return parent_rate as in some other similar rate recalculation
functions (in some other drivers)?
>
>> +               return 0;
>> +       } else {
>> +               return DIV_ROUND_UP_ULL((u64)parent_rate, div);
>> +       }
>>   }
>>
Stephen Boyd Oct. 7, 2024, 10:56 p.m. UTC | #3
Quoting Alexandra Diupina (2024-09-24 06:14:44)
> >> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
> >> index 8701a58a5804..b32c6d4d7ee5 100644
> >> --- a/drivers/clk/mvebu/armada-37xx-periph.c
> >> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
> >> @@ -343,7 +343,12 @@ static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
> >>          div = get_div(double_div->reg1, double_div->shift1);
> >>          div *= get_div(double_div->reg2, double_div->shift2);
> >>   
> >> -       return DIV_ROUND_UP_ULL((u64)parent_rate, div);
> >> +       if (!div) {
> >> +               pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);
> > hw->init is set to NULL after registration (see clk_register() code). If
> > div is 0 what does the hardware do?
> 
> Thanks for noticing the error. Yes, hw->init is set to zero,
> I will replace that code with clk_hw_get_name(hw).
> If the value of div is 0, should I return 0 as stated in the
> comment for .recalc_rate (in struct clk_ops) or should I
> return parent_rate as in some other similar rate recalculation
> functions (in some other drivers)?

It depends on what the hardware does. Does the hardware pass on the
parent rate if the divider is zero? If so, then return parent_rate. Or
does it turn off completely? If so, return zero.
Andrew Lunn Oct. 8, 2024, 9:58 p.m. UTC | #4
On Mon, Oct 07, 2024 at 03:56:29PM -0700, Stephen Boyd wrote:
> Quoting Alexandra Diupina (2024-09-24 06:14:44)
> > >> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
> > >> index 8701a58a5804..b32c6d4d7ee5 100644
> > >> --- a/drivers/clk/mvebu/armada-37xx-periph.c
> > >> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
> > >> @@ -343,7 +343,12 @@ static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
> > >>          div = get_div(double_div->reg1, double_div->shift1);
> > >>          div *= get_div(double_div->reg2, double_div->shift2);
> > >>   
> > >> -       return DIV_ROUND_UP_ULL((u64)parent_rate, div);
> > >> +       if (!div) {
> > >> +               pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);
> > > hw->init is set to NULL after registration (see clk_register() code). If
> > > div is 0 what does the hardware do?
> > 
> > Thanks for noticing the error. Yes, hw->init is set to zero,
> > I will replace that code with clk_hw_get_name(hw).
> > If the value of div is 0, should I return 0 as stated in the
> > comment for .recalc_rate (in struct clk_ops) or should I
> > return parent_rate as in some other similar rate recalculation
> > functions (in some other drivers)?
> 
> It depends on what the hardware does. Does the hardware pass on the
> parent rate if the divider is zero? If so, then return parent_rate. Or
> does it turn off completely? If so, return zero.

I don't think anybody knows what the hardware does in this
condition. I also suspect it has never happened, or if it has, nobody
has complained.

I would say, let is divide by 0, so there is an obvious kernel stack
trace and hopefully a report of the issue. It can then be investigated
in a way we can then find out what the hardware actually is doing.

   Andrew
Fedor Pchelkin Oct. 9, 2024, 9:01 a.m. UTC | #5
Hi Andrew,

On Tue, 08. Oct 23:58, Andrew Lunn wrote:
> On Mon, Oct 07, 2024 at 03:56:29PM -0700, Stephen Boyd wrote:
> > Quoting Alexandra Diupina (2024-09-24 06:14:44)
> > > >> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
> > > >> index 8701a58a5804..b32c6d4d7ee5 100644
> > > >> --- a/drivers/clk/mvebu/armada-37xx-periph.c
> > > >> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
> > > >> @@ -343,7 +343,12 @@ static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
> > > >>          div = get_div(double_div->reg1, double_div->shift1);
> > > >>          div *= get_div(double_div->reg2, double_div->shift2);
> > > >>   
> > > >> -       return DIV_ROUND_UP_ULL((u64)parent_rate, div);
> > > >> +       if (!div) {
> > > >> +               pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);
> > > > hw->init is set to NULL after registration (see clk_register() code). If
> > > > div is 0 what does the hardware do?
> > > 
> > > Thanks for noticing the error. Yes, hw->init is set to zero,
> > > I will replace that code with clk_hw_get_name(hw).
> > > If the value of div is 0, should I return 0 as stated in the
> > > comment for .recalc_rate (in struct clk_ops) or should I
> > > return parent_rate as in some other similar rate recalculation
> > > functions (in some other drivers)?
> > 
> > It depends on what the hardware does. Does the hardware pass on the
> > parent rate if the divider is zero? If so, then return parent_rate. Or
> > does it turn off completely? If so, return zero.
> 
> I don't think anybody knows what the hardware does in this
> condition. I also suspect it has never happened, or if it has, nobody
> has complained.
> 
> I would say, let is divide by 0, so there is an obvious kernel stack
> trace and hopefully a report of the issue. It can then be investigated
> in a way we can then find out what the hardware actually is doing.

Is it worth adding some kind of WARN assertions? Or actually just leave it
for now as is?
Andrew Lunn Oct. 9, 2024, 12:23 p.m. UTC | #6
> > I would say, let is divide by 0, so there is an obvious kernel stack
> > trace and hopefully a report of the issue. It can then be investigated
> > in a way we can then find out what the hardware actually is doing.
> 
> Is it worth adding some kind of WARN assertions? Or actually just leave it
> for now as is?

What actually happens on a / 0 on ARM? I assume it triggers an
exception, which will give a stack trace? If so a WARN adds no value.

	Andrew
Fedor Pchelkin Oct. 9, 2024, 1:43 p.m. UTC | #7
On Wed, 09. Oct 14:23, Andrew Lunn wrote:
> > > I would say, let is divide by 0, so there is an obvious kernel stack
> > > trace and hopefully a report of the issue. It can then be investigated
> > > in a way we can then find out what the hardware actually is doing.
> > 
> > Is it worth adding some kind of WARN assertions? Or actually just leave it
> > for now as is?
> 
> What actually happens on a / 0 on ARM? I assume it triggers an
> exception, which will give a stack trace? If so a WARN adds no value.

Oh, I see. I should have better said "adding WARN assertions and bailing
out with a default value if they are violated". Thus avoiding to have a
division by zero exception. Non panic_on_warn systems would at least
survive in this case but still have a valuable trace.

Somehow more importantly, it would state in the codebase that the condition
is very-very unexpected and most probably won't ever happen but not 100%
sure because it depends on hardware behavior (as I perceive reading the
current thread).

That said, if adding such WARN-bail-out pattern seems unnecessary and just
wasteful in this situation, I don't think we have any options other than
keeping the code as is.
diff mbox series

Patch

diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
index 8701a58a5804..b32c6d4d7ee5 100644
--- a/drivers/clk/mvebu/armada-37xx-periph.c
+++ b/drivers/clk/mvebu/armada-37xx-periph.c
@@ -343,7 +343,12 @@  static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
 	div = get_div(double_div->reg1, double_div->shift1);
 	div *= get_div(double_div->reg2, double_div->shift2);
 
-	return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+	if (!div) {
+		pr_err("Can't recalculate the rate of clock %s\n", hw->init->name);
+		return 0;
+	} else {
+		return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+	}
 }
 
 static const struct clk_ops clk_double_div_ops = {