0

Thanks for the reply of In pt_regs why the value of bp and sp differ so greatly and why bp is smaller than sp

I have found that the ecx stores the esp of stack when sysenter occurs.Then I write the code below

#include<stdio.h>
#include<unistd.h>

pid_t f0() {
    int myEsp;
    int myEbp;

    pid_t pid = fork();
    if (pid != 0) {
        printf("\n");

        asm("movl %%esp, %0\n" : "=rm"(myEsp));
        printf("esp is 0x%x\n", myEsp);

        asm("movl %%ebp, %0\n" : "=rm"(myEbp));
        printf("ebp is 0x%x\n", myEbp);
    }
    return pid;
}

pid_t f1() {
    return f0();
}

pid_t f2() {
    return f1();
}

int main(int argc, char* argv[]) {
    sleep(5);
    pid_t pid = f2();
    int myEsp;
    int myEbp;

    if (pid != 0) {
        printf("\n");

        asm("movl %%esp, %0\n" : "=rm"(myEsp));
        printf("esp is 0x%x\n", myEsp);

        asm("movl %%ebp, %0\n" : "=rm"(myEbp));
        printf("ebp is 0x%x\n", myEbp);
    }

    return 0;
}

then set breakpoint at sysexit in entry_SYSENTER_32 of arch/x86/entry/entry_32.S. When I run the above code in debuggable kernel, it stops at sysexit. then I run 'x/128x $ecx', the stack content is printed such as below. The stack content is what I expect as user space data.

(gdb) p/x $ebp
$18 = 0xb7ee5000

(gdb) x/128x $ecx
0xbf827f0c: 0xb7ee5000  0x00000000  0x00000000  0xb7d9bc33
0xbf827f1c: 0x00000001  0xbf828104  0xb7ee5000  0xbf827ff8
0xbf827f2c: 0xb7d9b7e2  0x00000000  0x00000000  0x00000000
0xbf827f3c: 0x00000000  0x00000000  0x00000000  0xbf827fa0
0xbf827f4c: 0x4d0d2200  0x00000080  0xb7ef065c  0xbf827fd4
0xbf827f5c: 0xb7ee5000  0xbf827fe4  0xffffffb4  0xbf827fe4
0xbf827f6c: 0xb7d9b72d  0x00000000  0x00000000  0xbf827f8c
0xbf827f7c: 0xbf827f9c  0x00000000  0x00000001  0xb7ef0490
0xbf827f8c: 0x00000005  0x00000000  0x00000000  0x00000000
0xbf827f9c: 0x00000000  0xb7f2f000  0x00000020  0x00000000
0xbf827fac: 0x4d0d2200  0xb7d9b6db  0x00000000  0x7fffffff
0xbf827fbc: 0x4d0d2200  0xbf827fe4  0xbf827fe4  0x00000000
0xbf827fcc: 0x004d2fcc  0xbf828104  0xb7f2eb80  0xbf827ff8(bp)fork?
0xbf827fdc: 0x004d01e4  0xb7f2f608  0x00000005  0x00000000
0xbf827fec: 0x4d0d2200  0x00000000  0x004d2fcc  0xbf828008(bp)f0
0xbf827ffc: 0x004d024b  0xbf828104  0xb7f2eb80  0xbf828018(bp)f1
0xbf82800c: 0x004d0262  0x00000005  0xb7f08f10  0xbf828038(bp)f2
0xbf82801c: 0x004d0293  0xbf828060  0xb7ef065c  0xb7ef0b00
0xbf82802c: 0x00000001  0xbf828050  0xb7ee5000  0xb7f2f020(bp)main
0xbf82803c: 0xb7ce0519  0xbf829f80  0x00000070  0xb7f2f000
0xbf82804c: 0xb7ce0519  0x00000001  0xbf828104  0xbf82810c
0xbf82805c: 0xbf828070  0xb7ee5000  0x004d0264  0x00000001
0xbf82806c: 0xbf828104  0xb7ee5000  0xbf828104  0xb7f2eb80
0xbf82807c: 0xb7f2f020  0x7614d39c  0xef1dd98c  0x00000000
0xbf82808c: 0x00000000  0x00000000  0xb7f2eb80  0xb7f2f020
0xbf82809c: 0x4d0d2200  0xb7f2fa40  0xb7ce04a6  0xb7ee5000
0xbf8280ac: 0xb7ce05f3  0x00000000  0x004d2ed0  0xbf82810c
0xbf8280bc: 0xb7f2f020  0x00000000  0x00000000  0xb7ce056d
0xbf8280cc: 0x004d2fcc  0x00000001  0x004d00a0  0x00000000
0xbf8280dc: 0x004d00cb  0x004d0264  0x00000001  0xbf828104
0xbf8280ec: 0x00000000  0x00000000  0xb7efca90  0xbf8280fc
0xbf8280fc: 0xb7f2fa40  0x00000001  0xbf829f80  0x00000000

In user space the function call order is main->f2->f1->f0->fork. I have found each ebp's position corresponding to each function, and tags them in above data.But the question is why there is too much stack content data above address 0xbf827fd8, the address which I supposed function fork's ebp is at. and the other question is where is __libc_fork's stack frame since I know fork called it in glibc. The last question is how the ebp get back on the right track as ebp and esp represents the range of each stack frame since the ebp's value is 0xb7ee5000 when I print it. Is there some storys in __kernel_vsyscall or vdso which leads to above result?

ps. the debuggable linux's version is linux-5.12.10

I have debugged a program, use 'stepi' to step into the deepest part of 'fork' routine,here is the code:

   │<__kernel_vsyscall+1>        push   %edx                       
   │<__kernel_vsyscall+2>        push   %ebp                      
   │<__kernel_vsyscall+3>        mov    %esp,%ebp                  
  >│<__kernel_vsyscall+5>        sysenter                          
   │<__kernel_vsyscall+7>        int    $0x80                      
   │<__kernel_vsyscall+9>        pop    %ebp                       
   │<__kernel_vsyscall+10>       pop    %edx                        
   │<__kernel_vsyscall+11>       pop    %ecx                        
   │<__kernel_vsyscall+12>       ret 
sunhang
  • 367
  • 2
  • 11
  • You did not show what `fork()` does, it may very well call other functions or have some locals which explains the extra stuff on the stack. What is at `0xb7d9bc33` as that seems to be the return address of the vdso? There seems to be another stack frame at `0xbf827f24` with return to `0xb7d9b7e2` and more frames follow. – Jester Jul 12 '23 at 12:51
  • A quick glance at glibc shows `__libc_fork` calls `_Fork` which calls `arch_fork` so there is plenty of stuff happening. – Jester Jul 12 '23 at 12:57
  • Possibly your glibc was built with `-fomit-frame-pointer` by default, and then it wouldn't be using EBP as a frame pointer internally. GCC sets `-fomit-frame-pointer` by default at optimization level 1 and above. – Ian Abbott Jul 12 '23 at 13:02
  • 2
    `__kernel_vsyscall` pushes ECX, EDX and EBP before the `mov %esp,%ebp; sysenter` sequence and pops them in the reverse order before the `ret` instruction, so the four values (after the address offset) on the first line of the stack trace will be EBP, EDX, ECX and the EIP return address back into the bowels of glibc. – Ian Abbott Jul 12 '23 at 13:47
  • @IanAbbott Yes,I have use 'stepi' to step into __kernel_vsyscall, found that 'sysenter' is followd by "int $0x80\n pop %ebp\n pop %edx\n pop %ecx\n ret". The ebp's value is 0xb7ee5000 in this way. And I don't know what does 0xb7ee5000 meaning, it seemly is not some address of stack. – sunhang Jul 13 '23 at 03:45
  • @IanAbbott thanks for the mention of the -fomit-frame-pointer. here is an explanation https://stackoverflow.com/questions/14666665/trying-to-understand-gcc-option-fomit-frame-pointer – sunhang Jul 13 '23 at 04:00
  • @sunhang The `regs->ip = landing_pad;` statement in `do_fast_syscall_32()` sets `regs->ip` to point just past that `int $0x80` instruction. `regs->ip` is copied to EDX before the `sysexit` instruction, and `sysexit` will set EIP to the value in EDX, so that `int $0x80` instruction will be skipped over. (I should probably mention that in my answer to your earlier question.) – Ian Abbott Jul 13 '23 at 09:55

0 Answers0