7

I'm adding a feature to my Linux debugger (I'm using Ptrace to manipulate the traced process as well as libbfd/libopcodes) to unwind the stack and determine if discrepancies exist between each CALL's allocated stack space and a statically derived local variable size, printing the address and local stack size of each frame along the way.

My general methodology is to take the address in the base pointer (EBP/RBP), increment the pointer to should should contain the stored frame pointer, dereference that address, examine it with PTRACE_PEEKDATA and repeat until I dereference an address occupying an area outside the stack.

I know how to check code/data segment registers, but ideally I'd like a method to check if I'm still inside the callstack even if the segmentation has been changed by W^X memory pages or an otherwise nonexecutable stack.

In short, how can I check (in the general case) when I've moved outside the stack without triggering a general protection fault?

(As as aside, I realize I'm operating on the assumption that checking an address's page segment is the ideal methodology here -- perhaps another simpler method exists to determine if an address is within the current process's stack space)

5
  • 1
    I guess you are working under Linux. Specifying the OS is clearly needed here as it does set the stack base address (with an ASLR offset or not) before starting the process.
    – perror
    Apr 5, 2013 at 15:19
  • Thank you, I've edited the answer to specify above -- It is indeed linux.
    – zetavolt
    Apr 5, 2013 at 15:22
  • Highly recommended reading on unwinding: part 1, part 2 (OS X oriented, but many things still apply).
    – Igor Skochinsky
    Jun 4, 2013 at 18:32
  • Given that you know the stack usage in intel/amd environments you know that the ebp value's would necessarily increase in value for each successive rollback (meaning, accessing the next parent). Also, given you're looking at a Linux environment you can (somewhat safely) assume the 8MB (per thread) stack size limitation. Furthermore, given that (most likely) 4KB pages are in use, you can safely read ebp values that are at a greater address than the current one UNTIL it reaches a different (and obviously higher) page entry. At this point, you can use mmap() to see if the page is there or not...
    – Nick
    Aug 22, 2016 at 18:43
  • Also, you can detect some forms of 'corrupted' EBP/RBP values by examining the lower 2 bits (32-bit) or lower 3 bits (64-bit) because the ESP/RSP and EBP/RSP values are 4-byte or 8-byte aligned, respectively.
    – Nick
    Aug 23, 2016 at 16:10

1 Answer 1

9

So, it is totally untested but here is the result of a few Internet browsing.

First the stack base address is present in /proc/<pid>/maps, then it must be accessible from user-space at some point.

I looked at the code of the pstack command which is printing the content of the stack of a running process. This code is getting the base address from a link_map structure and store it inside the field l_addr. This field is set inside the function readLinkMap():

static void readLinkMap(int pid, ElfN_Addr base, struct link_map *lm, 
                        char *name, unsigned int namelen)
{
  /* base address */
  lm->l_addr = (ElfN_Addr) ptrace(PTRACE_PEEKDATA, pid,
                                  base + offsetof(struct link_map,l_addr), 0);
  /* next element of link map chain */
  if (-1 != (long) lm->l_addr || !errno)
    lm->l_next = (struct link_map *) ptrace(PTRACE_PEEKDATA, pid,
                                            base + offsetof(struct link_map, l_next), 0);
  if ((-1 == (long) lm->l_addr || -1 == (long) lm->l_next) && errno) {
    perror("ptrace");
    quit("can't read target.");
  }

  loadString(pid, base + offsetof(struct link_map, l_name), name, namelen);
}

I guess this is the right way to go. So, I would advise you to take a look at the code of the pstack command (the file is not very long) and to get inspiration from it because it does something extremely similar to what you want (at least if I understand what you said correctly).

Hope this short note will help you a bit.

3
  • Within the context of the users question, this seems adequate. If one wants to implement backtrace functionality, however, you'll probably need to use dwarf cfi if you want it to be reliable.
    – broadway
    Jun 5, 2013 at 16:11
  • @broadway: But, dwarf is not always present on all executable files. You can get some help looking at it, but, in my humble opinion, it would be a mistake to totally rely on it and nothing else.
    – perror
    Jun 5, 2013 at 17:46
  • yeah, you just have to accept the breakage in that case. The frames are often just not going to be there. You can maybe try to heuristically infer the registers in some common cases, though.
    – broadway
    Jun 5, 2013 at 18:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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