Search code examples
linuxprocessheap-memorykillptrace

Stopped Process but heap is still changing


I have stopped a process by tracing it with the ptrace() syscall. Which in effect sends a kill -sigstop <pid> to the process.

Now I inspect the [heap] region, which I got from /proc/<pid>/maps

But Reading out certain addresses in the heap memory range returns different values!

ptrace(PEEKDATA*, <pid>, <addr>, null) => 33  
ptrace(PEEKDATA*, <pid>, <addr>, null) => 34
ptrace(PEEKDATA*, <pid>, <addr>, null) => 34 

The values increase by one over time (not repeated calls) until the byte overflows at 63 and starts at 0 again!

I did a sanity test and read out the values directly from /proc/<pid>/mem which returns the same value. It also was increasing over time.

More background: I'm ptrace()ing a game, called Rogue Legacy, whose heap is about 200 Million addresses in size. The process hierarchie of the game looks like this:

  • SteamChildMonit
    • RogueLegacy
      • RogueLegacy.bin // <- im tracing this process, which stops the window animation and music

UPDATE: ptrace attaches to individual threads. It takes a pid as argument, but a pid can be both a process or thread. I was under the assumption that it is always a process, only now this inaccurate model broke for me. In my example, then, "RogueLegacy.bin" is just a traced thread and there is another thread, which is not stopped, who is causing the heap to change.

Multiple threads sharing the heap as a common resource. This also means that we have to be careful with peekdata in mutlithreaded applications, as the data under scrutiny is subject to change through the sovereign of other running threads.


Solution

  • I think I narrowed it down to a solution:

    Given a multithreaded application with the threads, who all share the same heap address-space

    • foo-thread
    • bar-thread
    • qux-thread

      And the process running our ptrace calls (in this case a single threaded process):

    • tracer-thread

      If we ptrace(PTRACE_ATTACH ..) to foo-thread, we make tracer-thread another parent of foo-thread. In accordance with ptrace' documentation, a SIGSTOP is send, that stops the thread. But this SIGSTOP send by tracer-thread only stops foo-thread not any other thread!

      But if we send a SIGSTOP to foo-thread directly, i.e. not through the means of the attach ptrace call, then the whole process, and its threads foo- and bar-thread are stopped.

      This is an instance where the peculiarity of ptrace becoming a makeshift foster home for threads has to be taken into account.