45

I'm trying to figure out how alloca() actually works on a memory level. From the linux man page:

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.

Does this mean alloca() will forward the stack pointer by n bytes? Or where exactly is the newly created memory allocated?

And isn't this exactly the same as variable length arrays?

I know the implementation details are probably left to the OS and stuff. But I want to know how in general this is accomplished.

dbush
  • 205,898
  • 23
  • 218
  • 273
glades
  • 3,778
  • 1
  • 12
  • 34
  • 10
    Your understanding is pretty accurate. – Eugene Sh. Oct 01 '21 at 13:49
  • 3
    Most of the time it is done exactly as described by the linux man page, and yes in that case the stack pointer is decreased by n bytes (or maybe a bit more than n for all kinds of reason like memory alignement etc.). And yes the same thing more or less happens when you use VLAs – Jabberwocky Oct 01 '21 at 13:49
  • 1
    @Jabberwocky please use *"automatic VLA"* term – tstanisl Oct 01 '21 at 14:53
  • If someone were inclined, it might be worthwhile to expand into a more detailed explanation of how this is implemented (I'm not sure I'd explain it very well). In a quick test, it looks like gcc inlines the effects of ```alloca()```, which makes sense - the compiler would have to know the stack frame has changed - but it appears to use some of the same mechanisms as thread-local storage, e.g. the use of the ```%fs``` register. – sj95126 Oct 01 '21 at 14:59
  • @sj95126: The `%fs` stuff you're seeing is most likely the [stack canary](https://stackoverflow.com/questions/1345670/stack-smashing-detected/1347464#1347464); the canary value is kept in thread-local storage. It's not really related to alloca itself, so `-fno-stack-protector` might clean things up a bit. – Nate Eldredge Oct 01 '21 at 17:49
  • @NateEldredge: Ah, so it did. Thanks. – sj95126 Oct 01 '21 at 18:00
  • Related: https://stackoverflow.com/questions/714692/alloca-implementation – Nate Eldredge Oct 01 '21 at 22:04
  • You could try reading the source code. I recently studied the source for malloc() because I'm writing a bare-metal memory manager where there is no room for stdlib. Got lots of good ideas from it. – TomServo Oct 02 '21 at 02:12
  • 1
    @NateEldredge one dumb question: If the exact size is not known at compile time, how does assembler generate code such as `subq $xxx, %rsp` to make room for the stack frame where `xxx` needs to be determined during compilation? In other words, compiler does not know how much to move down the stack pointer – torez233 Dec 05 '22 at 04:48
  • 1
    @torez233: Then it doesn't use an immediate instruction, but subtracts a value computed in some other way, most likely in a register. For instance, if you have `size_t n = some_func(); char *p = alloca(n+5);` then the compiler (not the assembler) emits code like `call some_func ; addq $5, %rax ; subq %rax, %rsp`. (There'd be more code in between to round `rax` up to a multiple of 16 or so.) – Nate Eldredge Dec 05 '22 at 15:46

4 Answers4

30

Yes, alloca is functionally equivalent to a local variable length array, i.e. this:

int arr[n];

and this:

int *arr = alloca(n * sizeof(int));

both allocate space for n elements of type int on the stack. The only differences between arr in each case is that 1) one is an actual array and the other is a pointer to the first element of an array, and 2) the array's lifetime ends with its enclosing scope, while the alloca memory's lifetime ends when the function returns. In both cases the array resides on the stack.

As an example, given the following code:

#include <stdio.h>
#include <alloca.h>

void foo(int n)
{
    int a[n];
    int *b=alloca(n*sizeof(int));
    int c[n];
    printf("&a=%p, b=%p, &c=%p\n", (void *)a, (void *)b, (void *)c);
}

int main()
{
    foo(5);
    return 0;
}

When I run this I get:

&a=0x7ffc03af4370, b=0x7ffc03af4340, &c=0x7ffc03af4320

Which shows that the the memory returned from alloca sits between the memory for the two VLAs.

VLAs first appeared in the C standard in C99, but alloca was around well before that. The Linux man page states:

CONFORMING TO

This function is not in POSIX.1-2001.

There is evidence that the alloca() function appeared in 32V, PWB, PWB.2, 3BSD, and 4BSD. There is a man page for it in 4.3BSD. Linux uses the GNU version.

BSD 3 dates back to the late 70's, so alloca was an early nonstandardized attempt at VLAs before they were added to the standard.

Today, unless you're using a compiler that doesn't support VLAs (such as MSVC), there's really no reason to use this function since VLAs are now a standardized way to get the same functionality.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Thanks! >Today there's really no reason to use this function. Why is that? I'm trying to minimize heap allocs during runtime and was hoping alloca would help a lot. Is it because I'm risking a stack overflow when I don't limit the buffer and when I limit the buffer i could just use fixed length arrays? – glades Oct 01 '21 at 14:06
  • 4
    The reason not to use `alloca` is because it is nonstandard while VLAs are. – dbush Oct 01 '21 at 14:08
  • VLAs is far broader term. VLAs refer to **typing**, not the storage. It is possible to have VLAs with dynamic storage. So saying that `VLA` and `alloca()` are equivalent is not true. – tstanisl Oct 01 '21 at 14:48
  • Moreover, this answer does not tell anything about the mechanics of `alloca()` what OP was interested in – tstanisl Oct 01 '21 at 14:49
  • 3
    VLAs are not required to be supported by the C11 and newer standard (e.g.: they are not supported by MSVC) – UnholySheep Oct 01 '21 at 15:43
  • 7
    @UnholySheep, yes, but this optionality feature is a complete failure. The compilers that supported VLAs still support it, those that did not, they still do not, and the value of compliance with C standard got only diluted. – tstanisl Oct 01 '21 at 15:56
  • 4
    Alloca behaves very different in the loops where it can easily exhaust the stack. That is because lifetime of the object acquired with alloca ends when the function returns. While VLA's lifetime ends when its containing block ends. So VLAs are far safer – tstanisl Oct 01 '21 at 21:00
  • 2
    If anyone is curious, [this](https://www.tuhs.org/cgi-bin/utree.pl?file=32V/usr/src/libc/sys/alloca.s) is the source for what may be the earliest `alloca()` implementation, in VAX assembly as part of UNIX/32V (1979). It's just a plain function that's called like any other, not a macro or compiler intrinsic; the compiler didn't have to know anything about it. Apparently the C compiler would reference all local variables relative to the frame pointer, and restore the stack pointer from the frame pointer when returning normally, so a function could safely return with a different stack pointer. – Nate Eldredge Oct 01 '21 at 22:10
  • 3
    @tstanisl In some scenarios, survival until the function returns is a reason to _prefer_ `alloca` over VLAs, for instance if you need to allocate some scratch space conditionally. – zwol Oct 01 '21 at 22:49
  • 1
    @glades: Yes, unchecked VLA or alloca sizes that depend on user input are a big security risk, allowing stack-clash attacks (where the stack pointer ends up inside some other mapping) unless you compile with options that force the compiler to emit stack probes, touching every intervening stack page. The C semantics (and lifetime) differ for VLA and alloca, but the actual asm level implementation is nearly identical, with gcc and clang often actually saving a pointer to the VLA as a separate local var instead of referencing it relative to the new stack pointer. – Peter Cordes Oct 02 '21 at 00:55
  • 1
    VLAs do not give the same functionality. They're a subset of the functionality that the committee deemed less burdensome to require implementors to support. – R.. GitHub STOP HELPING ICE Oct 02 '21 at 03:16
17

The other answer precisely describes mechanics of VLAs and alloca().

However, there is significant functional difference between alloca() and automatic VLA. The lifetime of the objects.

In case of alloca() the lifetime ends when the function returns. For VLAs the object is released when the containing block ends.

char *a;
int n = 10;
{
  char A[n];
  a = A;
}
// a is no longer valid

{
  a = alloca(n);
}
// is still valid

As result, it is possible to easily exhaust the stack in the loop while it is not possible to do it with VLAs.

for (...) {
  char *x = alloca(1000);
  // x is leaking with each iteration consuming stack
}

vs

for (...) {
  int n = 1000;
  char x[n];
  // x is released
}
tstanisl
  • 13,520
  • 2
  • 25
  • 40
  • This makes me wonder what happens if you mix alloca and VLAs... – plugwash Oct 02 '21 at 01:16
  • I'm not sure "a is still valid" is valid :-) a would not be useful because you could (should?) neither read nor write its value because that memory is outside of the current stack "dimentions"/"size", and subject to being clobbered by the next function call. A decent CPU/OS would (should?) not allow accessing stack memory that is "out of scope". – Jaime Guerrero Oct 02 '21 at 02:46
  • "leak" is a bit of an exaggeration. Not a true leak like unfreed mallocs; because assuming you don't exhaust the stack and fault but rather continue executing, upon the next function call or return, the stack pointer is reset and subsequent function calls, variables, or alloca()s will reuse that 'leaked' memory. In other words, it is automatically "freed" by virtue of being on the stack and not the heap. – Jaime Guerrero Oct 02 '21 at 02:49
  • The documentatoin for alloca at least on linux specifically says it is freed when the function call returns, not when you exit the block. – plugwash Oct 02 '21 at 14:30
  • @plugwash that is exactly what I have written in the answer – tstanisl Oct 02 '21 at 14:32
5

Although alloca looks like a function from a syntax point of view, it can't be implemented as a normal function in a modern programming environment*. It must be regarded as a compiler feature with a function-like interface.

Traditionally C compilers maintained two pointer registers, a "stack pointer" and a "frame pointer" (or base pointer). The stack pointer delimits the current extent of the stack. The frame pointer saved the value of the stack pointer on entry to the function and is used to access local variables and to restore the stack pointer on function exit.

Nowadays most compilers do not use a frame pointer by default in normal functions. Modern debug/exception information formats have rendered it unnecessary, but they still understand what it is and can use it where needed.

In particular for functions with alloca or variable length arrays using a frame pointer allows the function to keep track of the location of it's stack frame while dynamically modifying the stack pointer to accommodate the variable length array.

For example I built the following code at O1 for arm

#include <alloca.h>
int bar(void * baz);
void foo(int a) {
    bar(alloca(a));
}

and got (comments mine)

foo(int):
  push {fp, lr}     @ save existing link register and frame pointer
  add fp, sp, #4    @ establish frame pointer for this function
  add r0, r0, #7    @ add 7 to a ...
  bic r0, r0, #7    @ ... and clear the bottom 3 bits, thus rounding a up to the next multiple of 8 for stack alignment 
  sub sp, sp, r0    @ allocate the space on the stack
  mov r0, sp        @ make r0 point to the newly allocated space
  bl bar            @ call bar with the allocated space
  sub sp, fp, #4    @ restore stack pointer from frame pointer 
  pop {fp, pc}      @ restore frame pointer to value at function entry and return.

And yes alloca and variable length arrays are very similar (though as another answer points out not exactly the same). alloca seems to be the older of the two constructoins.


* With a sufficiently dumb/predictable compiler it is posible to implement alloca as a function in assembler. Specifically the compiler needs to.

  • Consistently create a frame pointer for all functions.
  • Consistently use the frame pointer rather than the stack pointer to reference local varaibles.
  • Consistently use the stack pointer rather than the frame pointer when setting up parameters for calls to functions.

This is apparently how it was first implemented ( https://www.tuhs.org/cgi-bin/utree.pl?file=32V/usr/src/libc/sys/alloca.s ).

I guess it's possible one could also have the actual implementation as an assembler function, but have a special case in the compiler that made it go into dumb/predictable mode when it saw alloca, I don't know if any compiler vendors did that.

plugwash
  • 9,724
  • 2
  • 38
  • 51
  • 1
    _"it can't be implemented as a normal function"_ — not always: see [this](https://stackoverflow.com/questions/69406966/how-does-alloca-work-on-a-memory-level#comment122686372_69407054) for a counterexample. – Ruslan Oct 02 '21 at 12:29
  • This is the best answer, because you give some great insight into how `alloca` was actually implemented as a "normal" function with the standard C ABI on the platform where it originated… and that gives us a strong hint about _why_ it was added in this form. – Dan Lenski May 14 '23 at 00:01
-3

The most important difference between alloca and VLAs is the failure case. The following code:

int f(int n) {
    int array[n];
    return array == 0;
}
int g(int n) {
    int *array = alloca(n);
    return array == 0;
}

The VLA has no possibility of detecting an allocation failure; which is a very un-C thing to impose on a language construct. Alloca() is thus much better designed.

mevets
  • 10,070
  • 1
  • 21
  • 33
  • 4
    `man alloca`: *RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behavior is undefined.* – EOF Oct 02 '21 at 20:29
  • Mine says something different: `A pointer to the start of the allocated memory, or NULL if an error occurred (errno is set).` But maybe that is part of why the RTOS's converge on Dinkum libraries instead of gnu. – mevets Oct 03 '21 at 04:43
  • 1
    Or maybe `alloca()` *isn't* "better designed", but rather not very well designed at all (and *very poorly specified*)? – EOF Oct 03 '21 at 04:47
  • Well no. VLA gives no opportunity for error recovery; alloca() does. Pretty much a slam dunk. Sure, some toy implementations of alloca made it out into the wild, but that doesn't prevent good implementations. Unlike VLA, which is pretty much what the standard-du-jour body said by deprecating it. – mevets Oct 03 '21 at 05:02
  • VLA does not give opportunity of recovery in the same was as `int A[10000000];` does not. The lack of resources for any allocation of automatic object is undefined by the C standard. If you want to have VLA with dynamic storage, just use a pointer to VLA and `malloc()`, or even "safe" `alloca()`. And finally. VLAs were **not** made deprecated. They were made optional, the same as complex number, atomics, threads, wide characters. Please update your answer that it is only to a very specific RTOS. – tstanisl Oct 03 '21 at 07:08
  • Compilers going back to the 80s were able to determine the stack size, at least until the points of cycles and uncertainties. For example, aztec C for the PC/8086 did this in at least 1987. So A[ENORMOUS] would be detected and handled or warn the operator. – mevets Oct 03 '21 at 14:31