Skip to main content
Tweeted twitter.com/StackUnix/status/1151461674676830209
added 1062 characters in body
Source Link

EDIT: Some more data makes me even more unsure what's going on. The test is the following:

.text
  .global _start
_start:
  subq $0x7fe000,%rsp
  movq $1,(%rsp)
  mov $0x3c,%eax
  mov $0,%edi
  syscall

I played with different values of the constant 0x7fe000 here to see what happens, and for this value it is nondeterministic whether I get a segfault or not. According to GDB, the subq instruction on its own will expand the size of the mmap, which is mysterious to me (how does linux know what's in my register?), but this program will usually crash GDB on exit for some reason. It can't be ASLR causing the nondeterminism because I'm not using a GOT or any PLT section; the executable is always loaded at the same locations in virtual memory every time. So is this some randomness of the PID or physical memory bleeding through? All in all I'm very confused as to how much stack is actually legally available for random access, and how much is requested on changing RSP or on writing to areas "just out of range" of legal memory.

EDIT: Some more data makes me even more unsure what's going on. The test is the following:

.text
  .global _start
_start:
  subq $0x7fe000,%rsp
  movq $1,(%rsp)
  mov $0x3c,%eax
  mov $0,%edi
  syscall

I played with different values of the constant 0x7fe000 here to see what happens, and for this value it is nondeterministic whether I get a segfault or not. According to GDB, the subq instruction on its own will expand the size of the mmap, which is mysterious to me (how does linux know what's in my register?), but this program will usually crash GDB on exit for some reason. It can't be ASLR causing the nondeterminism because I'm not using a GOT or any PLT section; the executable is always loaded at the same locations in virtual memory every time. So is this some randomness of the PID or physical memory bleeding through? All in all I'm very confused as to how much stack is actually legally available for random access, and how much is requested on changing RSP or on writing to areas "just out of range" of legal memory.

Source Link

How does the ELF loader determine the initial stack size?

I'm studying the ELF specification (http://www.skyfree.org/linux/references/ELF_Format.pdf), and one point that is not clear to me about the program loading process is how the stack is initialized, and what the initial page size is. Here's the test (on Ubuntu x86-64):

$ cat test.s
.text
  .global _start
_start:
  mov $0x3c,%eax
  mov $0,%edi
  syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out 

Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 ~/a.out
      0x7ffff7ffa000     0x7ffff7ffd000     0x3000        0x0 [vvar]
      0x7ffff7ffd000     0x7ffff7fff000     0x2000        0x0 [vdso]
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

The ELF specification has very little to say about how or why this stack page exists in the first place, but I can find references that say that the stack should be initialized with SP pointing to argc, with argv, envp and the auxiliary vector just above that, and I have confirmed this. But how much space is available below SP? On my system there are 0x1FF00 bytes mapped below SP, but presumably this is counting down from the top of the stack at 0x7ffffffff000, and there are 0x21000 bytes in the full mapping. What influences this number?

I am aware that the page just below the stack is a "guard page" that automatically becomes writable and "grows down the stack" if I write to it (presumably so that naive stack handling "just works"), but if I allocate a huge stack frame then I could overshoot the guard page and segfault, so I want to determine how much space is already properly allocated to me right at process start.