Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to read a child process's stack but with no luck. I know it is possible using ptrace, but ptrace's interface allows you to read only one word at a time, and I'm trying to scan a larger portions of the stack.

I've also tried reading the /proc/$pid/mem from the boundries of the stack as extracted from the /proc/$pid/maps file after first using ptrace to attach to it (as suggested here) but the read keeps failing (even when running as root) although the same code succeeds when tried reading from different parts of the process (e.g. heap).

What am I doing wrong? Is there any other option?

share|improve this question
    
Did you call waitpid between ptrace(PTRACE_ATTACH,…) and read (otherwise there's a possible race condition)? What error does read return? Is the child doing anything peculiar with its memory mapping — can you try your code with a simple child like sleep? –  Gilles Feb 20 '11 at 19:11
    
I did use wait after ptrace, and I've put a scanf in the child to force it to wait. –  user4537 Feb 20 '11 at 21:51
    
Is this only on Linux? Solaris also has a /proc filesystem, but it's completely different from Linux, eve philosophically. Lots of "binary files". –  Bruce Ediger Feb 23 '11 at 12:55
    
just do a system("pstack pid") and parse the output .. –  Vardhan Mar 2 '11 at 10:11
add comment

4 Answers

ptrace's interface allows you to read only one word at a time, and I'm trying to scan a larger portions of the stack

Well, just use a loop, then. I honestly don't see how that constitutes a problem with ptrace, I use it all the time to remotely access processes.

I use something like this:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;

    while (n)
    {
        size_t todo = MIN(n, sizeof(long) - (src & align));
        long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
        if (errno)
        {
            perror("ptrace_peektext (memcpy_from_target)");
            return -1;
        }
        memcpy(dest, (char *)&data + (src & align), todo);

        dest += todo; src += todo; n -= todo;
    }

    return 0;
}
share|improve this answer
    
Hi Sam, while your code will do (and actually that's what I'm currently doing), it has a large performance overhead. –  user4537 Mar 13 '11 at 9:30
    
@user4536: I see. I have another strategy in mind that I'll post about when I have time writing it down. What are your typical stack sizes? –  Sam Hocevar Mar 13 '11 at 11:37
    
It's hard to say really as my research does not assume a specific stack size, but for the sake of this argument let's say at least a couple of pages long Could you drop a hint regarding your strategy? Thanks for the help anyway! –  user4537 Mar 13 '11 at 12:21
add comment

Here is another strategy that might need tweaking but should be more efficient with large chunks of data. The idea is to execute syscalls in the remote process in order to retrieve the stack contents. It will need specific architecture code but if you only target x86 / x86_64 it shouldn't be too much hassle.

  1. Create a named pipe such as "/tmp/fifo" in your calling process.
  2. Step into the traced process until it returns from a syscall, using PTRACE_SYSCALL to step, waitpid() to wait and PTRACE_GETREGS/PTRACE_PEEKTEXT to check the currently executed opcode.
  3. Backup the remote process’s registers and a small area of its stack.
  4. Execute syscalls on the remote process by overriding its stack with your own data: open("/tmp/fifo"), write() the stack contents, close() the descriptor.
  5. Restore the remote process’s state.
  6. Read the fifo data from your calling process.

There might be more elegant alternatives to the named pipe, but I can’t think of any right now. The reason I only use syscalls is because remote code injection is pretty unreliable on modern systems due to various security protections. The drawback is that it will hang until the remote process does a syscall (which can be a problem for some programs that mostly do computations).

You can see some free code implementing most of the work in this source file. Feedback on the code is welcome!

share|improve this answer
add comment

Another suggestion.

When/if it gets accepted in the main Linux kernel tree, you will be able to use Christopher Yeoh's Cross Memory Attach patch. See the documentation for process_vm_readv for instance.

share|improve this answer
add comment

You could try lsstack. It uses ptrace, just like every other successful "read another process's stack" program. I could not get a program using /proc/$pid/mem reading to work. I believe that you can't do it that way, although, logically, you should.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.