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