first glance of alloca in ctf

0x1 background

when I was doing zeropts CTF 2022, I found a pwn question called accountant.

There is a line of code that use alloca to allocate memory. However, at that time, I didn't know that alloca allocate the memory on the stack. so I simply thought there is no bug... :(

later, Super Guesser publish their writeup on accountant that point out that alloca allocate space on the stack.

So I could have a chance of looking at alloca.

0x2 basic idea

The alloca() function allocates size bytes of space in the stack frame of the caller. This temporary space is automatically freed when the function that called alloca() returns to its caller.
https://www.mkssoftware.com/docs/man3/alloca.3.asp

Basically, alloca allocate a space on the stack instead of heap.

Lets do some code to find out what alloca do. above is a example program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
void test() {
long * ptr1 = (long *)0x1000;
long * ptr2 = (long *)0x1001;
// just some offset to make it clear
long x = 0x1002;
// lets alloca some space!
// normal alloca
ptr1 = (long *) alloca(sizeof(long));
*ptr1 = 0x2001;
ptr1 = (long *) alloca(sizeof(long));
*ptr1 = 0x2002;
// what if alloca 0
ptr2 = (long *) alloca(0);
*ptr2 = 0x2003;
}

int main() {
test();
return 0;
}

firstly, in assemble code alloca didn't appear to be a function. Instead, it appear with a set of instruction. So, the asm will not have any instruction like call alloca.

Before alloca is called, the stack looks very normal

1
2
3
4
5
6
:> pxr @ rsp
0x7fffe7ddb1d0 0x0000000000001000 ........ @ rsp 4096
0x7fffe7ddb1d8 0x0000000000001001 ........ 4097
0x7fffe7ddb1e0 0x0000000000001002 ........ 4098
0x7fffe7ddb1e8 0x65ba763434aa8f00 ...44v.e
0x7fffe7ddb1f0 0x00007fffe7ddb200 ........ @ rbp [stack] stack R W 0x0

After program execute *ptr = 0x2001. Comparing with previous stack frame, We notice that that stack alloca extra 16 bytes above the initial stack.

1
2
3
4
5
6
7
8
:> pxr @ rsp
0x7fffe7ddb1c0 0x0000000000002001 . ...... @ rax 8193
0x7fffe7ddb1c8 0x000055756136632d -c6auU.. /home/aynakeya/ctf/play/allocaa/a .text sym.__libc_csu_init program ascii ('-') R X 'add rbx, 1' 'a'
0x7fffe7ddb1d0 0x00007fffe7ddb1c0 ........ @ rcx [stack] rsp,rax stack R W 0x2001
0x7fffe7ddb1d8 0x0000000000001001 ........ 4097
0x7fffe7ddb1e0 0x0000000000001002 ........ 4098
0x7fffe7ddb1e8 0x65ba763434aa8f00 ...44v.e
0x7fffe7ddb1f0 0x00007fffe7ddb200 ........ @ rbp [stack] stack R W 0x0

If we continue and execute *ptr = 0x2002. The it will also allocate 16 bytes of memory above the stack. This is because the stack need to be aligned with 0x10. Since we only allocate 8 bytes, there need to be an extra 8 bytes padding in order to make the stack aligned.

Now, lets look what happens if we alloc(0).

After execute *ptr2 = 0x2003. We found the value 0x2002 is replaced by 0x2003. ptr1 and ptr2 are now pointing to a same address. Since

alloc(0) actually return the current rsp address!

1
2
3
4
5
6
7
8
9
10
:> pxr @ rsp
0x7fffe7ddb1b0 0x0000000000002003 . ...... @ rax 8195
0x7fffe7ddb1b8 0x00007fffe7ddb1e7 ........ [stack] stack R W 0xba763434aa8f0000
0x7fffe7ddb1c0 0x0000000000002001 . ...... @ rsi 8193
0x7fffe7ddb1c8 0x000055756136632d -c6auU.. /home/aynakeya/ctf/play/allocaa/a .text sym.__libc_csu_init program ascii ('-') R X 'add rbx, 1' 'a'
0x7fffe7ddb1d0 0x00007fffe7ddb1b0 ........ @ rcx [stack] rsp,rax stack R W 0x2003
0x7fffe7ddb1d8 0x00007fffe7ddb1b0 ........ [stack] rsp,rax stack R W 0x2003
0x7fffe7ddb1e0 0x0000000000001002 ........ 4098
0x7fffe7ddb1e8 0x65ba763434aa8f00 ...44v.e
0x7fffe7ddb1f0 0x00007fffe7ddb200 ........ @ rbp [stack] stack R W 0x0

0x3 Some thoughts

it is pretty interesting that there is a function that allocate memory on the stack. It seems very convenient compare to malloc if we just want use a memory space temporaryly. Since it will automatically freed after function exists, we don't need to worry about dangling pointer or memory leak.

But it is also kind of dangerous to use. For example, If we allocate 0 bytes, it would return current rsp address.

Also, when allocate larger space, we may run out of stack address if we use alloca.