On Wed, Jun 30, 2010 at 09:27:10AM -0500, Phil Staub wrote:
> On 06/29/2010 10:59 PM, Adam Jiang wrote:
> > Hello, list.
> > I'm having a problem with kernel mode stack on my box. It seems that
> > STACKOVERFLOW happened to Linux kernel. However, I can't prove it
> > because the lack of any detection in __do_IRQ() function just like on
> > the other architectures. If you know something about, please help me
> > on following two questions.
> > - Is there any possible to do this on MIPS?
> The mechanisms I know about for detecting stack overflow include:
> 1. Use of the MMU - stack ends at a page boundary, adjacent page is
> either unmapped or mapped read-only and causes an exception if violated.
Kernel stacks are allocated in kseg0, which is direct mapped. This means
conversions from virtual to physical addresses don't use the TLBs, so
this strategy doesn't work on MIPS.
> 2. Hooks inserted into toolchain to cause any stack decrement to be
> first tested against a limit.
I don't know of MIPS hooks for this but there may be some. It would have
to be tuned specifically for the kernel because of the way you need to get
the stack bounds (see below).
> 3. Fill entire stack with a recognizable pattern before first
> use. After suspected stack overflow, check to see if the pattern has
> been disturbed in the area of the stack limit.
This one pretty much always works. The only exception is if you allocate
a big array on the stack and don't use enough of it to alter memory in
the area at which you are looking. Allocating big arrays on the stack
is a kernel no-no, so you should be in good shape here.
To implement this, you have to know the stack bounds. Start by getting
getting current_thread_info(), which is a pointer to a struct
thread_info. The struct thread_info is at the low address part of the
memory allocated for a task stack, which is given by union thread_union.
So, you can cast current_thread_info to a union thread_union* and put
the result in, say, p. Then the valid stack space is from
(char*)(&p->thread_info + 1) to ((char *)p + THREAD_SIZE/sizeof(long)).
> (Disclaimer: I've used all of these in some form on other OSes, but
> not on Linux. Someone else may have a more directly relevant answer.)
> > - or, more simple question, how could I get the address $sp pointed by
> > asm() notation in C?
> How about something like:
> long x;
> asm("move %0,$29":"=g"(x));
Depending on the version of gcc you are using, you should also be able
to use __builtin_frame_address(), though I haven't tried this in the kernel.
> > Best regards,
> > /Adam