For Programmers: Free Programming Magazines  


Home > Archive > Unix Programming > December 2004 > Non-Root Process Backtrace of Itself?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Non-Root Process Backtrace of Itself?
Michael B Allen

2004-12-22, 9:00 am

When one of my daemon workers faults I would like to automatically
get a backtrace using gdb in Linux. Unfortunately if that process is
not root ptrace returns EPERM because the worker was setuid/setgid to
"nobody". So when the code faults and I run my "backtrace" script in
the signal handler it chokes.

Is there any way for a non-root process to backtrace itself?

Thanks,
Mike
Paul Pluzhnikov

2004-12-22, 4:04 pm

Michael B Allen <mba2000@ioplex.com> writes:

> When one of my daemon workers faults I would like to automatically
> get a backtrace using gdb in Linux. Unfortunately if that process is
> not root ptrace returns EPERM because the worker was setuid/setgid to
> "nobody". So when the code faults and I run my "backtrace" script in
> the signal handler it chokes.


Your description of what you are doing is self-inconsistent:
- if the *parent* is trying to invoke gdb on the child, then EPERM
is expected, but what does this have to do with the signal handler?
- if the child invokes gdb on itself in its signal handler, then
there should be no problem with EPERM (gdb will also run as
"nobody"), and that is how you probably should solve this problem.

Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
Bjorn Reese

2004-12-22, 4:04 pm

Michael B Allen wrote:

> Is there any way for a non-root process to backtrace itself?


You could consider using __builtin_return_address instead.

See the GCC_DumpStack function in stacktrace.c in

http://home1.stofanet.dk/breese/debug/debug.tar.gz

--
mail1dotstofanetdotdk
Michael B Allen

2004-12-22, 9:13 pm

On Wed, 22 Dec 2004 11:25:41 -0500, Paul Pluzhnikov wrote:

>
> Your description of what you are doing is self-inconsistent: - if the
> *parent* is trying to invoke gdb

<nope>
> - if the child invokes gdb on itself in its signal handler, then
> there should be no problem with EPERM (gdb will also run as "nobody"),
> and that is how you probably should solve this problem.


No. This is what I'm doing but I'm seeing ptrace return EPERM. If you
ptrace on a process that started out as "user" then ptraceing as that user
will work. But an exe that started out as "root" and then setuid/setgid
to "nobody" is inherently different and "nobody" cannot ptrace it even
though it is also "nobody".

Mike
Michael B Allen

2004-12-22, 9:13 pm

On Wed, 22 Dec 2004 13:19:31 -0500, Bjorn Reese wrote:

> Michael B Allen wrote:
>
>
> You could consider using __builtin_return_address instead.
>
> See the GCC_DumpStack function in stacktrace.c in
>
> http://home1.stofanet.dk/breese/debug/debug.tar.gz


I just wrote a simple test proggie and it faults:

Program received signal SIGSEGV, Segmentation fault.
0x08048a69 in GCC_DumpStack () at stacktrace.c:257
257 p = __builtin_return_address(6);

Can I use this fn as is or do I need to actually understand what
__builtin_return_address actually is?

Also, I was hoping I could get the backtrace inside the sighandler
after getting SEGV. But won't this just print the stack of the signal
handler? Can I change it to print the stack of the process that faulted?

Mike
Bjorn Reese

2004-12-24, 9:06 am

Michael B Allen wrote:

> Program received signal SIGSEGV, Segmentation fault.
> 0x08048a69 in GCC_DumpStack () at stacktrace.c:257
> 257 p = __builtin_return_address(6);
>
> Can I use this fn as is or do I need to actually understand what
> __builtin_return_address actually is?


You will notice from the boiler-plate that the code is quite old.
It used to work, but apparently __builtin_return_address has changed
semantics in the meantime.

I ran the program and got the same crash as you. I can make it work
if I change the line

p = __builtin_return_address(0);

to

p = __builtin_frame_address(0);
if (p)
p = __builtin_return_address(0);

and similar for all other __builtin_return_address lines.

> Also, I was hoping I could get the backtrace inside the sighandler
> after getting SEGV. But won't this just print the stack of the signal
> handler? Can I change it to print the stack of the process that faulted?


It outputs the entire stack, not just the stack of the signal handler.
I get the following output (CrashHandler is the SIGSEGV signal handler)

[0] 0x08049443 <StackTrace + 0xb> T
[1] 0x0804948d <CrashHandler + 0x1b> T
[2] 0x420275c8 ???
[3] 0x080494ac <Crash + 0x15> T
[4] 0x0804952d <main + 0x7c> T
[5] 0x42015574 ???
[6] 0x08048729 <_start + 0x21> T

--
mail1dotstofanetdotdk
Michael B Allen

2004-12-25, 3:56 am

On Fri, 24 Dec 2004 07:07:01 -0500, Bjorn Reese wrote:

> Michael B Allen wrote:
>
>
> You will notice from the boiler-plate that the code is quite old. It
> used to work, but apparently __builtin_return_address has changed
> semantics in the meantime.
>
> I ran the program and got the same crash as you. I can make it work if I
> change the line
>
> p = __builtin_return_address(0);
>
> to
>
> p = __builtin_frame_address(0);
> if (p)
> p = __builtin_return_address(0);
>
> and similar for all other __builtin_return_address lines.
>


Still faults for me anyway:

Program received signal SIGSEGV, Segmentation fault.
0x08048aaf in GCC_DumpStack () at stacktrace.c:264
264 p = __builtin_return_address(6);
(gdb) bt
#0 0x08048aaf in GCC_DumpStack () at stacktrace.c:264
#1 0x0804962f in fn0 (i=10) at t.c:7
#2 0x0804964c in fn1 (i=11) at t.c:14
#3 0x0804966c in fn2 (i=11) at t.c:19
#4 0x0804969a in main () at t.c:25
#5 0x42017499 in __libc_start_main () from /lib/i686/libc.so.6

If I cut down the ADDRESSLIST_SIZE to 6 it sort of works:

$ ./t
[0] 0x0804937b ???
[1] 0x08049398 ???
[2] 0x080493b8 ???
[3] 0x080493e6 ???
[4] 0x42017499 ???
[5] 0x08048741 ???

I removed 'static' from GCC_DumpStack, added a prototype to stacktrace.h,
and compiled as follows:

$ gcc -Wall -W -g -DUSE_BUILTIN -c -o stacktrace.o stacktrace.c
stacktrace.c: In function `GCC_DumpStack':
stacktrace.c:225: warning: unused variable `pname'
$ gcc -Wall -W -g stacktrace.o -o t t.c

I also tried:

$ gcc -Wall -W -g -DUSE_BUILTIN stacktrace.c -o t t.c
stacktrace.c: In function `GCC_DumpStack':
stacktrace.c:225: warning: unused variable `pname'

No difference.

$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.96/specs
gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-110)

Is there any documentation on this __builtin_return_address thing?

Thanks,
Mike
Bjorn Reese

2004-12-25, 8:55 am

Michael B Allen wrote:

> If I cut down the ADDRESSLIST_SIZE to 6 it sort of works:


Well, the problem is detecting the top of the call stack, so
lowering ADDRESSLIST_SIZE will only avoid the crash if you
know that your call stack is more than 6 frames deep.
__builtin_return_address ought to return 0 in that case, but
instead it simply crashes.

> [0] 0x0804937b ???


It does not resolve the symbols. Is nm in your path?

> gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-110)


Ah, the old version of gcc could be the problem.

> Is there any documentation on this __builtin_return_address thing?


Yes, it is a built-in function of gcc, so you can find the
description in 'info gcc' (at least in later versions of gcc.)

--
mail1dotstofanetdotdk
Heny Townsend

2004-12-26, 3:57 am

Bjorn Reese wrote:
>
>
> Ah, the old version of gcc could be the problem.


According to my readings (just some googling over the last 24 hours or
so) __buildin_return_address() is not guaranteed to return 0 when
reaching stack top due to limitations of certain architectures, and
supposedly x86 is one of those.

--
Henry Townsend
Bjorn Reese

2004-12-26, 3:56 pm

Heny Townsend wrote:

> According to my readings (just some googling over the last 24 hours or
> so) __buildin_return_address() is not guaranteed to return 0 when
> reaching stack top due to limitations of certain architectures, and
> supposedly x86 is one of those.


Yes, this is why I suggested the __builtin_frame_address() workaround.
However, that only works if (according to the documentation) "the
first frame pointer is properly initialized by the startup code";
however one achieves that...

Another workaround could be to iterate through the call stack until
we find an address inside the main() function. The could be done
by adding the following two lines (marked by +) in GCC_DumpStack()

if ((p) && (i < ADDRESSLIST_SIZE))
{
syms[i].realAddress = (unsigned long)p;
syms[i].closestAddress = 0;
syms[i].name[0] = (char)0;
syms[i].type = ' ';
+ if ((p > (void *)main) && (p < (void *)main_end))
+ break; /* for */

This solution requires a pointer to the end of the main function.
I did this by adding an "void main_end(void) {}" immediately after
the main function. Better solutions are most welcome.

--
mail1dotstofanetdotdk
David REsnick

2004-12-30, 3:59 pm

Bjorn Reese wrote:
> Heny Townsend wrote:
>
>
>
> Yes, this is why I suggested the __builtin_frame_address() workaround.
> However, that only works if (according to the documentation) "the
> first frame pointer is properly initialized by the startup code";
> however one achieves that...
>
> Another workaround could be to iterate through the call stack until
> we find an address inside the main() function. The could be done
> by adding the following two lines (marked by +) in GCC_DumpStack()
>
> if ((p) && (i < ADDRESSLIST_SIZE))
> {
> syms[i].realAddress = (unsigned long)p;
> syms[i].closestAddress = 0;
> syms[i].name[0] = (char)0;
> syms[i].type = ' ';
> + if ((p > (void *)main) && (p < (void *)main_end))
> + break; /* for */
>
> This solution requires a pointer to the end of the main function.
> I did this by adding an "void main_end(void) {}" immediately after
> the main function. Better solutions are most welcome.
>


Have you looked at glibc backtrace and such? i.e.
http://www.gnu.org/software/libc/ma...html#Backtraces

Here is an example:
#include <stdio.h>
#include <execinfo.h>
#define MAX_FRAMES 10

static void funca(void);
static void funcb(void);
static void trace(void);

int main()
{
funca();

return 0;
}

static void funcb(void)
{
funca();
}

static void funca(void)
{
static int depth = 0;

if (depth++ > MAX_FRAMES) {
trace();
return;
}
else {
funcb();
}
}

static void trace(void)
{
void *frame_array[MAX_FRAMES];
int frames = backtrace(frame_array, MAX_FRAMES);
backtrace_symbols_fd(&frame_array[1], frames-1, fileno(stdout));
}

dresnick(1659)$ gcc -g -Wall -o bt bt.c
dresnick(1660)$ bt
bt(backtrace_symbols_fd+0x100)[0x8048440
]
bt(backtrace_symbols_fd+0xe3)[0x8048423]

bt(backtrace_symbols_fd+0x107)[0x8048447
]
bt(backtrace_symbols_fd+0xe3)[0x8048423]

bt(backtrace_symbols_fd+0x107)[0x8048447
]
bt(backtrace_symbols_fd+0xe3)[0x8048423]

bt(backtrace_symbols_fd+0x107)[0x8048447
]
bt(backtrace_symbols_fd+0xe3)[0x8048423]

bt(backtrace_symbols_fd+0x107)[0x8048447
]

You only get function addresses (not names, alas), at least
on linux.

But attaching gdb to the program and doing, e.g.
(gdb) l *0x8048423
0x8048423 is in funcb (bt.c:19).
14 }
15
16 static void funcb(void)
17 {
18 funca();
19 }
20
21 static void funca(void)
22 {
23 static int depth = 0;

will get you the code. Hope that is helpful.

-David
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com