diff mbox

powerpc: Avoid panic during boot due to divide by zero in init_cache_info()

Message ID 1488671674-20833-1-git-send-email-anton@ozlabs.org (mailing list archive)
State Accepted
Headers show

Commit Message

Anton Blanchard March 4, 2017, 11:54 p.m. UTC
From: Anton Blanchard <anton@samba.org>

I see a panic in early boot when building with a recent gcc toolchain.
The issue is a divide by zero, which is undefined. Older toolchains
let us get away with it:

int foo(int a) { return a / 0; }

foo:
	li 9,0
	divw 3,3,9
	extsw 3,3
	blr

But newer ones catch it:

foo:
	trap

Add a check to avoid the divide by zero.

Fixes: bd067f83b084 ("powerpc/64: Fix naming of cache block vs. cache line")
Signed-off-by: Anton Blanchard <anton@samba.org>
---
 arch/powerpc/kernel/setup_64.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Benjamin Herrenschmidt March 5, 2017, 12:25 a.m. UTC | #1
On Sun, 2017-03-05 at 10:54 +1100, Anton Blanchard wrote:
> From: Anton Blanchard <anton@samba.org>
> 
> I see a panic in early boot when building with a recent gcc
> toolchain.
> The issue is a divide by zero, which is undefined. Older toolchains
> let us get away with it:

Maybe we should panic though ... not having a valid cache block size is
going to be fatal in other areas...

> int foo(int a) { return a / 0; }
> 
> foo:
> 	li 9,0
> 	divw 3,3,9
> 	extsw 3,3
> 	blr
> 
> But newer ones catch it:
> 
> foo:
> 	trap
> 
> Add a check to avoid the divide by zero.
> 
> Fixes: bd067f83b084 ("powerpc/64: Fix naming of cache block vs. cache
> line")
> Signed-off-by: Anton Blanchard <anton@samba.org>
> ---
>  arch/powerpc/kernel/setup_64.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/setup_64.c
> b/arch/powerpc/kernel/setup_64.c
> index adf2084..afd1c26 100644
> --- a/arch/powerpc/kernel/setup_64.c
> +++ b/arch/powerpc/kernel/setup_64.c
> @@ -408,7 +408,8 @@ static void init_cache_info(struct ppc_cache_info
> *info, u32 size, u32 lsize,
>  	info->line_size = lsize;
>  	info->block_size = bsize;
>  	info->log_block_size = __ilog2(bsize);
> -	info->blocks_per_page = PAGE_SIZE / bsize;
> +	if (bsize)
> +		info->blocks_per_page = PAGE_SIZE / bsize;
>  
>  	if (sets == 0)
>  		info->assoc = 0xffff;
Benjamin Herrenschmidt March 5, 2017, 12:27 a.m. UTC | #2
On Sun, 2017-03-05 at 11:25 +1100, Benjamin Herrenschmidt wrote:
> On Sun, 2017-03-05 at 10:54 +1100, Anton Blanchard wrote:
> > From: Anton Blanchard <anton@samba.org>
> > 
> > I see a panic in early boot when building with a recent gcc
> > toolchain.
> > The issue is a divide by zero, which is undefined. Older toolchains
> > let us get away with it:
> 
> Maybe we should panic though ... not having a valid cache block size
> is going to be fatal in other areas...

 ... Unless it's for L2/L3 caches. Of course...

Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

> > int foo(int a) { return a / 0; }
> > 
> > foo:
> > 	li 9,0
> > 	divw 3,3,9
> > 	extsw 3,3
> > 	blr
> > 
> > But newer ones catch it:
> > 
> > foo:
> > 	trap
> > 
> > Add a check to avoid the divide by zero.
> > 
> > Fixes: bd067f83b084 ("powerpc/64: Fix naming of cache block vs.
> > cache
> > line")
> > Signed-off-by: Anton Blanchard <anton@samba.org>
> > ---
> >  arch/powerpc/kernel/setup_64.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/powerpc/kernel/setup_64.c
> > b/arch/powerpc/kernel/setup_64.c
> > index adf2084..afd1c26 100644
> > --- a/arch/powerpc/kernel/setup_64.c
> > +++ b/arch/powerpc/kernel/setup_64.c
> > @@ -408,7 +408,8 @@ static void init_cache_info(struct
> > ppc_cache_info
> > *info, u32 size, u32 lsize,
> >  	info->line_size = lsize;
> >  	info->block_size = bsize;
> >  	info->log_block_size = __ilog2(bsize);
> > -	info->blocks_per_page = PAGE_SIZE / bsize;
> > +	if (bsize)
> > +		info->blocks_per_page = PAGE_SIZE / bsize;
> >  
> >  	if (sets == 0)
> >  		info->assoc = 0xffff;
Michael Ellerman March 5, 2017, 10:26 a.m. UTC | #3
Anton Blanchard <anton@ozlabs.org> writes:

> From: Anton Blanchard <anton@samba.org>
>
> I see a panic in early boot when building with a recent gcc toolchain.
> The issue is a divide by zero, which is undefined. Older toolchains
> let us get away with it:
>
> int foo(int a) { return a / 0; }
>
> foo:
> 	li 9,0
> 	divw 3,3,9
> 	extsw 3,3
> 	blr
>
> But newer ones catch it:
>
> foo:
> 	trap
>
> Add a check to avoid the divide by zero.

Erk sorry. One of the static checkers spotted it, but I hadn't got
around to fixing it because it seemed to not actually blow up, guess
not.

cheers
Segher Boessenkool March 5, 2017, 12:37 p.m. UTC | #4
On Sun, Mar 05, 2017 at 09:26:47PM +1100, Michael Ellerman wrote:
> > I see a panic in early boot when building with a recent gcc toolchain.
> > The issue is a divide by zero, which is undefined. Older toolchains
> > let us get away with it:
> >
> > int foo(int a) { return a / 0; }
> >
> > foo:
> > 	li 9,0
> > 	divw 3,3,9
> > 	extsw 3,3
> > 	blr
> >
> > But newer ones catch it:
> >
> > foo:
> > 	trap
> >
> > Add a check to avoid the divide by zero.
> 
> Erk sorry. One of the static checkers spotted it, but I hadn't got
> around to fixing it because it seemed to not actually blow up, guess
> not.

The PowerPC divw etc. instructions do not trap by themselves, but recent
GCC inserts trap instructions on code paths that are always undefined
behaviour (like, dividing by zero).


Segher
Gabriel Paubert March 5, 2017, 4:58 p.m. UTC | #5
On Sun, Mar 05, 2017 at 06:37:37AM -0600, Segher Boessenkool wrote:
> On Sun, Mar 05, 2017 at 09:26:47PM +1100, Michael Ellerman wrote:
> > > I see a panic in early boot when building with a recent gcc toolchain.
> > > The issue is a divide by zero, which is undefined. Older toolchains
> > > let us get away with it:
> > >
> > > int foo(int a) { return a / 0; }
> > >
> > > foo:
> > > 	li 9,0
> > > 	divw 3,3,9
> > > 	extsw 3,3
> > > 	blr
> > >
> > > But newer ones catch it:
> > >
> > > foo:
> > > 	trap
> > >
> > > Add a check to avoid the divide by zero.
> > 
> > Erk sorry. One of the static checkers spotted it, but I hadn't got
> > around to fixing it because it seemed to not actually blow up, guess
> > not.
> 
> The PowerPC divw etc. instructions do not trap by themselves, but recent
> GCC inserts trap instructions on code paths that are always undefined
> behaviour (like, dividing by zero).


Is it systematic or does it depend from, e.g., optimization levels?

Is there anything in the standards about this feature?

    Gabriel
Segher Boessenkool March 5, 2017, 5:24 p.m. UTC | #6
On Sun, Mar 05, 2017 at 05:58:37PM +0100, Gabriel Paubert wrote:
> > > Erk sorry. One of the static checkers spotted it, but I hadn't got
> > > around to fixing it because it seemed to not actually blow up, guess
> > > not.
> > 
> > The PowerPC divw etc. instructions do not trap by themselves, but recent
> > GCC inserts trap instructions on code paths that are always undefined
> > behaviour (like, dividing by zero).
> 
> Is it systematic or does it depend from, e.g., optimization levels?

In this case it needs -fisolate-erroneous-paths-dereference which is
default at -O2 and higher.

> Is there anything in the standards about this feature?

The compiler can do whatever it likes with code that has undefined
behaviour.  With this optimisation it a) can compile the conforming
code to something better; and b) undefined behaviour will trap instead
of doing something random (which often is exploitable).


Segher
Benjamin Herrenschmidt March 5, 2017, 11:09 p.m. UTC | #7
On Sun, 2017-03-05 at 11:24 -0600, Segher Boessenkool wrote:
> On Sun, Mar 05, 2017 at 05:58:37PM +0100, Gabriel Paubert wrote:
> > > > Erk sorry. One of the static checkers spotted it, but I hadn't got
> > > > around to fixing it because it seemed to not actually blow up, guess
> > > > not.
> > > 
> > > The PowerPC divw etc. instructions do not trap by themselves, but recent
> > > GCC inserts trap instructions on code paths that are always undefined
> > > behaviour (like, dividing by zero).
> > 
> > Is it systematic or does it depend from, e.g., optimization levels?
> 
> In this case it needs -fisolate-erroneous-paths-dereference which is
> default at -O2 and higher.
> 
> > Is there anything in the standards about this feature?
> 
> The compiler can do whatever it likes with code that has undefined
> behaviour.  With this optimisation it a) can compile the conforming
> code to something better; and b) undefined behaviour will trap instead
> of doing something random (which often is exploitable).

I actually like that feature, except it did bite me once or twice in the past
adding traps to intentional NULL dereferences ;-) Ah the joys of writing
a firmware where you poke at stuff at fixed addresses in low memory :-)

Cheers,
Ben.
Segher Boessenkool March 6, 2017, 12:10 a.m. UTC | #8
On Mon, Mar 06, 2017 at 10:09:01AM +1100, Benjamin Herrenschmidt wrote:
> > The compiler can do whatever it likes with code that has undefined
> > behaviour.  With this optimisation it a) can compile the conforming
> > code to something better; and b) undefined behaviour will trap instead
> > of doing something random (which often is exploitable).
> 
> I actually like that feature,

Yeah, me too -- it also (currently) makes *smaller* code than it would
without it.  Win-win-win.

> except it did bite me once or twice in the past
> adding traps to intentional NULL dereferences ;-) Ah the joys of writing
> a firmware where you poke at stuff at fixed addresses in low memory :-)

You cannot really have something at address 0, the way NULL pointers
are represented in GCC.  0 in firmware, so *fun*, especially before the
CFAR was invented.  "Something jumped to 0, CTR is 0 so it's probably
a BCTR, but which one of the 6000?"

What do you have at 0?  Not anything you need often I hope?


Segher
Benjamin Herrenschmidt March 6, 2017, 12:20 a.m. UTC | #9
On Sun, 2017-03-05 at 18:10 -0600, Segher Boessenkool wrote:
> You cannot really have something at address 0, the way NULL pointers
> are represented in GCC.  0 in firmware, so *fun*, especially before
> the
> CFAR was invented.  "Something jumped to 0, CTR is 0 so it's probably
> a BCTR, but which one of the 6000?"
> 
> What do you have at 0?  Not anything you need often I hope?

I think it was some kind of boot flag. I've had cases also of
copying the 0..0x100 region over with the kexec gunk etc...

Ben.
Gabriel Paubert March 6, 2017, 12:03 p.m. UTC | #10
On Sun, Mar 05, 2017 at 11:24:56AM -0600, Segher Boessenkool wrote:
> On Sun, Mar 05, 2017 at 05:58:37PM +0100, Gabriel Paubert wrote:
> > > > Erk sorry. One of the static checkers spotted it, but I hadn't got
> > > > around to fixing it because it seemed to not actually blow up, guess
> > > > not.
> > > 
> > > The PowerPC divw etc. instructions do not trap by themselves, but recent
> > > GCC inserts trap instructions on code paths that are always undefined
> > > behaviour (like, dividing by zero).
> > 
> > Is it systematic or does it depend from, e.g., optimization levels?
> 
> In this case it needs -fisolate-erroneous-paths-dereference which is
> default at -O2 and higher.

Great, another optimization-dependent behaviour. :-(

But this is not the most serious issue: on PPC, when you #include
<limits>, the numeric_limits<any_integer_type>::traps is false on PPC,
and on no other architecture that I know of (in practice this trap
reflects the hardware behaviour on division by zero).

By generating a trap in this case, I believe that the compiler violates
a contract given by <limits>, and the standard.

I'd certainly prefer a compile time warning, easily convertible to an
error.

> 
> > Is there anything in the standards about this feature?
> 
> The compiler can do whatever it likes with code that has undefined
> behaviour.  With this optimisation it a) can compile the conforming
> code to something better; and b) undefined behaviour will trap instead
> of doing something random (which often is exploitable).

It may be undefined, but I believe that the numeric_limits<>::traps
value clearly prohibits generating a trap in this case.

    Gabriel
Segher Boessenkool March 6, 2017, 2:17 p.m. UTC | #11
On Mon, Mar 06, 2017 at 01:03:19PM +0100, Gabriel Paubert wrote:
> > > > The PowerPC divw etc. instructions do not trap by themselves, but recent
> > > > GCC inserts trap instructions on code paths that are always undefined
> > > > behaviour (like, dividing by zero).
> > > 
> > > Is it systematic or does it depend from, e.g., optimization levels?
> > 
> > In this case it needs -fisolate-erroneous-paths-dereference which is
> > default at -O2 and higher.
> 
> Great, another optimization-dependent behaviour. :-(

It makes the "behaviour" for undefined behaviour *less* surprising.
It does not change anything else: malformed programs stay malformed,
correct programs do exactly what they did before, too.

> But this is not the most serious issue: on PPC, when you #include
> <limits>, the numeric_limits<any_integer_type>::traps is false on PPC,
> and on no other architecture that I know of (in practice this trap
> reflects the hardware behaviour on division by zero).
> 
> By generating a trap in this case, I believe that the compiler violates
> a contract given by <limits>, and the standard.

[ snip ]

I have no idea why you are bringing C++ into this.  Please open a PR
if you think there is a bug in the C++ library.

I'll note that this cannot violate the standard, see the "terms and
definitions":

[defns.undefined]
undefined behavior
behavior for which this International Standard imposes no requirements


Segher
David Laight March 6, 2017, 3:18 p.m. UTC | #12
From: Segher Boessenkool
> Sent: 06 March 2017 14:18
> On Mon, Mar 06, 2017 at 01:03:19PM +0100, Gabriel Paubert wrote:
> > > > > The PowerPC divw etc. instructions do not trap by themselves, but recent
> > > > > GCC inserts trap instructions on code paths that are always undefined
> > > > > behaviour (like, dividing by zero).
> > > >
> > > > Is it systematic or does it depend from, e.g., optimization levels?
> > >
> > > In this case it needs -fisolate-erroneous-paths-dereference which is
> > > default at -O2 and higher.
> >
> > Great, another optimization-dependent behaviour. :-(
> 
> It makes the "behaviour" for undefined behaviour *less* surprising.
> It does not change anything else: malformed programs stay malformed,
> correct programs do exactly what they did before, too.

Yep, 'undefined behaviour' is exactly that.
It doesn't mean 'undefined result', or 'maybe a signal'.
Wiping the disk and targeting the user with an ICBM are both valid.

	David
Michael Ellerman March 8, 2017, 7:25 a.m. UTC | #13
On Sat, 2017-03-04 at 23:54:34 UTC, Anton Blanchard wrote:
> From: Anton Blanchard <anton@samba.org>
> 
> I see a panic in early boot when building with a recent gcc toolchain.
> The issue is a divide by zero, which is undefined. Older toolchains
> let us get away with it:
> 
> int foo(int a) { return a / 0; }
> 
> foo:
> 	li 9,0
> 	divw 3,3,9
> 	extsw 3,3
> 	blr
> 
> But newer ones catch it:
> 
> foo:
> 	trap
> 
> Add a check to avoid the divide by zero.
> 
> Fixes: bd067f83b084 ("powerpc/64: Fix naming of cache block vs. cache line")
> Signed-off-by: Anton Blanchard <anton@samba.org>
> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Applied to powerpc fixes, thanks.

https://git.kernel.org/powerpc/c/6ba422c75facb1b1e0e206c464ee12

cheers
diff mbox

Patch

diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index adf2084..afd1c26 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -408,7 +408,8 @@  static void init_cache_info(struct ppc_cache_info *info, u32 size, u32 lsize,
 	info->line_size = lsize;
 	info->block_size = bsize;
 	info->log_block_size = __ilog2(bsize);
-	info->blocks_per_page = PAGE_SIZE / bsize;
+	if (bsize)
+		info->blocks_per_page = PAGE_SIZE / bsize;
 
 	if (sets == 0)
 		info->assoc = 0xffff;