lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 30 Mar 2020 14:07:36 -0400
From:   Steven Rostedt <rostedt@...dmis.org>
To:     David Laight <David.Laight@...LAB.COM>
Cc:     Linux Kernel Mailing List <linux-kernel@...r.kernel.org>
Subject: Re: ftrace not showing the process names for all processes on
 syscall events

On Mon, 30 Mar 2020 15:34:08 +0000
David Laight <David.Laight@...LAB.COM> wrote:

> Oh, does the 'function_graph' code ignore tail calls?

Yes and no ;-)  It works by dumb luck. As it was a year after function
graph tracing was live (some time in 2010 I believe) that someone brought
up tail calls, and I had to take a look at how it never crashed, and was
surprised that it "just worked". Here's a summary:


The function graph tracer works via the following:

start of function being traced:

  call fgraph trampoline

      Replace return code on stack with jump to return trampoline
      save original return address in shadow stack
      return back to start of function

end of function:

  pop return address
  jump to return address which is now a jump to the return trampoline:

      Return trampoline:
        trace return of function
        pop the shadow stack to get the original return address
        jump to original return address



Now let's take a look at a tail function call:


first_func() {
	some_stuff;
	tail_func();
	return;
}

tail_func() {
	some_other_stuff;
	return;
}


The above would look like in assembly something like this:


first_func:

1:    call fgaph_tramp
2:    asm some_stuff
3:    jmp tail_func


tail_func:

4:   call fgraph_trapm
5:   asm some_other_stuff
6:   pop
7:   ret

At 1 above, we call fgraph_tramp which will replace the return address on
the stack (what called first_func) with the address of the fgraph return
trampoline. And store the original address onto the shadow stack.

The stack looks like this:


  main stack:
  -----------
  fgraph return trampoline


  shadow stack:
  -------------
  original return address


At 3, we just jump directly to tail_func, without doing anything to the
stack.

At 4, fgraph_tramp is called for tail_func this time, and it will replace
its return address with a call to the fgraph return trampoline. HERE IS THE
TRICK!  The return address that was replaced, was actually the address of
the fgraph return trampoline and not the original return address. And this
is now stored on the shadow stack. Thus we have this:

  main stack:
  -----------
  fgraph return trampoline

  shadow stack:
  -------------
  original return address
  fgraph return trampoline

At 6, the fgraph return trampoline is popped off the stack, and called.

The fgraph return trampoline, will trace the end of the function, pop off
the shadow stack, and jump to that. It just happen that it pops off the
address of the fgraph return trampoline and jumps to itself again.

The second call to fgraph return trampline will trace the end of
first_func, pop the shadow stack, which this time is the original return
address, and then jump to that.

Thus, it "just works" and we can all stay happy ;-)

-- Steve

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ