User Stack Trace

Stone, Joshua I joshua.i.stone@intel.com
Fri Apr 20 18:57:00 GMT 2007


I'm sorry to keep beating on this, but I'm still not convinced of what 
you're saying...

grundy wrote:
> On Thu, Apr 19, 2007 at 09:04:34AM -0700, Stone, Joshua I wrote:
>> What do you mean by current function?  The topmost in the stack?  A loop 
>> anywhere in the stack walk will still cause problems.
> Current function meaning whatever function made the syscall that gets
> probed. If you mess with the return address the syscall will use
> (regardless of method) the program will only have one shot at messing
> with us. I think most malicious users would prefer multiple shots before
> the program crashes.

It's not just the topmost that matters for a stack walk.  If I have a 
call sequence something like main->A->B->C->sys_open, suppose I corrupt 
the return address from A and create a loop.  Then I just have to make 
sure that I don't return from A, or that I restore the right return info 
in A before I return.  In the meantime I can repeat my calls to sys_open 
hoping that you'll probe it.

Even if the program did crash, there's nothing stopping said user from 
wrapping that program in a looping shell script to get multiple shots.

 >> [...]
>> I'm just pointing out that your first access to the value from 
>> nregs->ebp also needs to be protected by _stp_copy_from_user.
> 
> ok, I misread that the first time through. The first access to the "value
> from nregs->ebp" doesn't leave kernel space. Head actually points
> into the regs structure, so it's really printing out the value of
> nregs->eax, since it just prints the value it doesn't matter if someone
> puts their mom's phone number in there because the first access to the
> actual address is through _stp_copy_from_user. 

On the first time through, head is the *value* of ebp, not anything 
pointing into the nregs structure.

 > [...]
 >         head = (struct frame_head *)nregs->ebp;
 >
 >         if (user_mode_vm(nregs)) {
 >                 do {
 >                         _stp_printf ("[user] <%p>\n", head->ret);
 > [...]

The value of nregs->ebp will be the last value that the user had in 
there, right?  So in your print statement you're dereferencing %ebp to 
get ret.

> It's a little weird the way it looks with the frame structure in there,
> if you look at arch/i386/oprofile/backtrace.c, you'll see where I got
> the ideas from.  They use a recursive function to do the backtrace. I'm
> probably going to go back to that method, I was just having some brain
> fade while embedding the code, so I made it an ugly loop instead.

It's not really recursion, but a while loop that calls 
dump_user_backtrace.  Even the first time through that loop will be 
checked with access_ok, which is what your loop is missing in the 
translation.

There's also a nice comment in there:

     /* frame pointers should strictly progress back up the stack
      * (towards higher addresses) */

... which is a more deterministic way to prevent malicious looping.

> If you look in the runtime code in stack.c at the end in the old
> _stp_ustack_print, they basically tried the same thing, getting the two
> quick values available in kernel space. The side effect of using
> sysenter is the eip value points to the vdso and has limited value from
> an application programmer's point of view.

Yes, and that function is '#if 0'ed.  It also makes an unprotected 
dereference to esp to get the second address, which is bad.


Josh



More information about the Systemtap mailing list