Why is the variable allocated the same way in both programs?

1

I have following code to showcase stack-based buffer overflow.

int check_authentication(char *password) {
  int auth_flag = 0;
  char password_buffer[16];

  strcpy(password_buffer, password);
  if(strcmp(password_buffer, "Admin") == 0)
    auth_flag = 1;
  return auth_flag;
}

Here when user inputs any string with length greater than 16 will allow access. To show other case of not overflow the auth_flag I have the following code:

int check_authentication(char *password) {
  char password_buffer[16];
  int auth_flag = 0;

  strcpy(password_buffer, password);
  if(strcmp(password_buffer, "Admin") == 0)
    auth_flag = 1;
  return auth_flag;
}

As the stack works as LIFO, auth_flag should have a lower address than password_buffer in the second example. GDB with break point at strcpy looks as follows:

(gdb) x/16xw password_buffer
0x61fefc:       0x696d6441      0x7659006e      0xc9da078f      0xfffffffe
0x61ff0c:       0x00000001      0x76596cad      0x00401990      0x0061ff38
0x61ff1c:       0x00401497      0x00ae1658      0x00000000      0x0028f000
0x61ff2c:       0x00400080      0x0061ff1c      0x0028f000      0x0061ff94
(gdb) x/x &auth_flag
0x61ff0c:       0x00000001

I expected the password_buffer to start from 0x61ff10, right after auth_flag. Where I am wrong?

I am using gcc (gcc version 9.2.0 (MinGW.org GCC Build-20200227-1) and gdb (GNU gdb (GDB) 7.6.1) on windows 10 with no modification to SEHOP or ASLR.

c
memory
gdb
allocation
buffer-overflow
asked on Stack Overflow May 11, 2020 by Vintux • edited May 11, 2020 by RobertS supports Monica Cellio

1 Answer

2

As stated in the comments, local variables are not pushed onto and popped from the stack. Instead, when the function call is executed, the runtime allocates some space on the stack for the local variables. It is called Function Prologue and has a known sequence (in many cases - see the comment)

push ebp
mov ebp, esp
sub esp, N

where N is the space reserved for the local variables.

For some reason, GCC always allocates the memory location [rbp-4] for auth_flag local variable and that's why you do not see any difference (check this vs this). Could be how the compiler is designed...

On the other hand, clang does what you expect the compiler to do, at least when allocating the place on the stack for your auth_flag local variable. No optimisations are used for the compiler

check_authentication:                   # @check_authentication
        push    rbp
        mov     rbp, rsp
        sub     rsp, 48
        lea     rax, [rbp - 32]
        mov     qword ptr [rbp - 8], rdi
        mov     dword ptr [rbp - 12], 0
        mov     rsi, qword ptr [rbp - 8]
        mov     rdi, rax
        mov     qword ptr [rbp - 40], rax # 8-byte Spill
        call    strcpy
        mov     esi, offset .L.str
        mov     rdi, qword ptr [rbp - 40] # 8-byte Reload
        mov     qword ptr [rbp - 48], rax # 8-byte Spill
        call    strcmp
        cmp     eax, 0
        jne     .LBB0_2
        mov     dword ptr [rbp - 12], 1
.LBB0_2:
        mov     eax, dword ptr [rbp - 12]
        add     rsp, 48
        pop     rbp
        ret
.L.str:
        .asciz  "Admin"

compare the above with the below code where password_buffer is declared before the auth_flag local variable.

check_authentication:                   # @check_authentication
        push    rbp
        mov     rbp, rsp
        sub     rsp, 64
        lea     rax, [rbp - 32]
        mov     qword ptr [rbp - 8], rdi
        mov     dword ptr [rbp - 36], 0
        mov     rsi, qword ptr [rbp - 8]
        mov     rdi, rax
        mov     qword ptr [rbp - 48], rax # 8-byte Spill
        call    strcpy
        mov     esi, offset .L.str
        mov     rdi, qword ptr [rbp - 48] # 8-byte Reload
        mov     qword ptr [rbp - 56], rax # 8-byte Spill
        call    strcmp
        cmp     eax, 0
        jne     .LBB0_2
        mov     dword ptr [rbp - 36], 1
.LBB0_2:
        mov     eax, dword ptr [rbp - 36]
        add     rsp, 64
        pop     rbp
        ret
.L.str:
        .asciz  "Admin"

The mov dword ptr [rbp - XXX], 0 line in above code snippets are where your local auth_flag variable is declared and initialised. As you can see, the reserved location on the stack for the local variable changes based on your buffer size. It is worth compiling your code with clang and debugging it with lldb I think.

answered on Stack Overflow May 11, 2020 by fnisi • edited May 12, 2020 by fnisi

User contributions licensed under CC BY-SA 3.0