Vaibhav
Vaibhav

Reputation: 5947

Why is the use of alloca() not considered good practice?

alloca() allocates memory on the stack rather than on the heap, as in the case of malloc(). So, when I return from the routine the memory is freed. So, actually this solves my problem of freeing up dynamically allocated memory. Freeing of memory allocated through malloc() is a major headache and if somehow missed leads to all sorts of memory problems.

Why is the use of alloca() discouraged in spite of the above features?

Upvotes: 512

Views: 214389

Answers (26)

Luis Colorado
Luis Colorado

Reputation: 12708

just to add one simple case in which you will run into trouble:

Imagine you allocate pointers in a loop, inside the function. All those chunks of memory will start growing your stack frame (as if you had a lot of temporary variables to store partial results from expressions) and soon will make the function stack populated with all those memory chunks that cannot be free()d until you return; from the function, and will lead to a most than probable stack overflow.

I don't know (and have not digged up) if the alloca() code checks for stack overflow and returns NULL or it just jumps and let's the process crash. At least the manual page says it doesn't check it, so you are probably condemned to an Undefined Behavior.

Alloca is not implemented as a function, because it would need to access to the parent's stack frame. So don't think on it as a function like malloc. Follow all the guidelines stated for it, and more important, use it with care (sometimes it is useful, but not as a general case)

Upvotes: 0

Deon McClung
Deon McClung

Reputation: 331

My comment relates to the relatively small, unknown allocation quantities that would be a candidate for alloca(). Larger allocations should of course always go through the malloc() family of functions.

One reason alloca() may be discouraged for these relatively small, unknown quantities is because it leaves the depth of your stack a direct product of an unbounded quantity; maybe even subject to a variable input from an outside source. This could potentially create a security risk or just an opening for some really nasty bugs on event of a stack overflow.

One remedy to this would be to clamp this input value to an upper bound. However, if you are already bounding your allocation size, why not then just work within a fixed-sized array? You already need a stack size that would account for your worst-case scenario for memory usage. A fixed-sized array accounts for your worst-case stack consumption without exposing your code to vulnerabilities.

I can understand the allure of alloca() and maybe there are some embedded, deep-stack situations where not allocating the whole worst-case amount at each stack frame can make all the difference. However, that is pretty shaky ground.

If you are not terribly stack constrained, you should probably prefer working within a fixed-sized array.

Upvotes: 1

Igor Zevaka
Igor Zevaka

Reputation: 76610

One of the most memorable bugs I had was to do with an inline function that used alloca. It manifested itself as a stack overflow (because it allocates on the stack) at random points of the program's execution.

In the header file:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

In the implementation file:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

So what happened was the compiler (Microsoft VC++ 6) inlined DoSomething function and all the stack allocations were happening inside Process() function and thus blowing the stack up. In my defence (and I wasn't the one who found the issue; I had to go and cry to one of the senior developers when I couldn't fix it), it wasn't straight alloca, it was one of ATL string conversion macros.

So the lesson is - do not use alloca in functions that you think might be inlined.

Upvotes: 290

rustyx
rustyx

Reputation: 85541

alloca is not worse than a variable-length array (VLA), but it's riskier than allocating on the heap.

On x86 (and most often on ARM), the stack grows downwards, and that brings with it a certain amount of risk: if you accidentally write beyond the block allocated with alloca (due to a buffer overflow for example), then you will overwrite the return address of your function, because that one is located "above" on the stack, i.e. after your allocated block.

_alloca block on the stack

The consequence of this is two-fold:

  1. The program will crash spectacularly and it will be impossible to tell why or where it crashed (stack will most likely unwind to a random address due to the overwritten frame pointer).

  2. It makes buffer overflow many times more dangerous, since a malicious user can craft a special payload which would be put on the stack and can therefore end up executed.

In contrast, if you write beyond a block on the heap you "just" get heap corruption. The program will probably terminate unexpectedly but will unwind the stack properly, thereby reducing the chance of malicious code execution.

Upvotes: 8

plugwash
plugwash

Reputation: 10523

IMO the biggest risk with alloca and variable length arrays is it can fail in a very dangerous manner if the allocation size is unexpectedly large.

Allocations on the stack typically have no checking in user code.

Modern operating systems will generally put a guard page in place below* to detect stack overflow. When the stack overflows the kernel may either expand the stack or kill the process. Linux expanded this guard region in 2017 to be significantly large than a page, but it's still finite in size.

So as a rule it's best to avoid allocating more than a page on the stack before making use of the previous allocations. With alloca or variable length arrays it's easy to end up allowing an attacker to make arbitrary size allocations on the stack and hence skip over any guard page and access arbitrary memory.

* on most widespread systems today the stack grows downwards.

Upvotes: 1

Rick
Rick

Reputation: 7516

Why no one mentions this example introduced by GNU documention?

https://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.html

Nonlocal exits done with longjmp (see Non-Local Exits) automatically free the space allocated with alloca when they exit through the function that called alloca. This is the most important reason to use alloca

Suggest reading order 1->2->3->1:

  1. https://www.gnu.org/software/libc/manual/html_node/Advantages-of-Alloca.html
  2. Intro and Details from Non-Local Exits
  3. Alloca Example

Upvotes: 3

Kaz
Kaz

Reputation: 58667

One pitfall with alloca is that longjmp rewinds it.

That is to say, if you save a context with setjmp, then alloca some memory, then longjmp to the context, you may lose the alloca memory. The stack pointer is back where it was and so the memory is no longer reserved; if you call a function or do another alloca, you will clobber the original alloca.

To clarify, what I'm specifically referring to here is a situation whereby longjmp does not return out of the function where the alloca took place! Rather, a function saves context with setjmp; then allocates memory with alloca and finally a longjmp takes place to that context. That function's alloca memory is not all freed; just all the memory that it allocated since the setjmp. Of course, I'm speaking about an observed behavior; no such requirement is documented of any alloca that I know.

The focus in the documentation is usually on the concept that alloca memory is associated with a function activation, not with any block; that multiple invocations of alloca just grab more stack memory which is all released when the function terminates. Not so; the memory is actually associated with the procedure context. When the context is restored with longjmp, so is the prior alloca state. It's a consequence of the stack pointer register itself being used for allocation, and also (necessarily) saved and restored in the jmp_buf.

Incidentally, this, if it works that way, provides a plausible mechanism for deliberately freeing memory that was allocated with alloca.

I have run into this as the root cause of a bug.

Upvotes: 13

greggo
greggo

Reputation: 3249

I don't think anyone has mentioned this: Use of alloca in a function will hinder or disable some optimizations that could otherwise be applied in the function, since the compiler cannot know the size of the function's stack frame.

For instance, a common optimization by C compilers is to eliminate use of the frame pointer within a function, frame accesses are made relative to the stack pointer instead; so there's one more register for general use. But if alloca is called within the function, the difference between sp and fp will be unknown for part of the function, so this optimization cannot be done.

Given the rarity of its use, and its shady status as a standard function, compiler designers quite possibly disable any optimization that might cause trouble with alloca, if would take more than a little effort to make it work with alloca.

UPDATE: Since variable-length local arrays have been added to C, and since these present very similar code-generation issues to the compiler as alloca, I see that 'rarity of use and shady status' does not apply to the underlying mechanism; but I would still suspect that use of either alloca or VLA tends to compromise code generation within a function that uses them. I would welcome any feedback from compiler designers.

Upvotes: 20

Sean Bright
Sean Bright

Reputation: 120714

The answer is right there in the man page (at least on Linux):

RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behaviour is undefined.

Which isn't to say it should never be used. One of the OSS projects I work on uses it extensively, and as long as you're not abusing it (alloca'ing huge values), it's fine. Once you go past the "few hundred bytes" mark, it's time to use malloc and friends, instead. You may still get allocation failures, but at least you'll have some indication of the failure instead of just blowing out the stack.

Upvotes: 349

Dyllon Gagnier
Dyllon Gagnier

Reputation: 381

I don't think that anybody has mentioned this, but alloca also has some serious security issues not necessarily present with malloc (though these issues also arise with any stack based arrays, dynamic or not). Since the memory is allocated on the stack, buffer overflows/underflows have much more serious consequences than with just malloc.

In particular, the return address for a function is stored on the stack. If this value gets corrupted, your code could be made to go to any executable region of memory. Compilers go to great lengths to make this difficult (in particular by randomizing address layout). However, this is clearly worse than just a stack overflow since the best case is a SEGFAULT if the return value is corrupted, but it could also start executing a random piece of memory or in the worst case some region of memory which compromises your program's security.

Upvotes: 1

alecov
alecov

Reputation: 5171

Most answers here largely miss the point: there's a reason why using _alloca() is potentially worse than merely storing large objects in the stack.

The main difference between automatic storage and _alloca() is that the latter suffers from an additional (serious) problem: the allocated block is not controlled by the compiler, so there's no way for the compiler to optimize or recycle it.

Compare:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

with:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

The problem with the latter should be obvious.

Upvotes: 0

zagam
zagam

Reputation: 87

Sadly the truly awesome alloca() is missing from the almost awesome tcc. Gcc does have alloca().

  1. It sows the seed of its own destruction. With return as the destructor.

  2. Like malloc() it returns an invalid pointer on fail which will segfault on modern systems with a MMU (and hopefully restart those without).

  3. Unlike auto variables you can specify the size at run time.

It works well with recursion. You can use static variables to achieve something similar to tail recursion and use just a few others pass info to each iteration.

If you push too deep you are assured of a segfault (if you have an MMU).

Note that malloc() offers no more as it returns NULL (which will also segfault if assigned) when the system is out of memory. I.e. all you can do is bail or just try to assign it any way.

To use malloc() I use globals and assign them NULL. If the pointer is not NULL I free it before I use malloc().

You can also use realloc() as general case if want copy any existing data. You need to check pointer before to work out if you are going to copy or concatenate after the realloc().

3.2.5.2 Advantages of alloca

Upvotes: 5

user7491277
user7491277

Reputation: 76

Actually, alloca is not guaranteed to use the stack. Indeed, the gcc-2.95 implementation of alloca allocates memory from the heap using malloc itself. Also that implementation is buggy, it may lead to a memory leak and to some unexpected behavior if you call it inside a block with a further use of goto. Not, to say that you should never use it, but some times alloca leads to more overhead than it releaves frome.

Upvotes: 3

Daniel Glasser
Daniel Glasser

Reputation: 21

In my opinion, alloca(), where available, should be used only in a constrained manner. Very much like the use of "goto", quite a large number of otherwise reasonable people have strong aversion not just to the use of, but also the existence of, alloca().

For embedded use, where the stack size is known and limits can be imposed via convention and analysis on the size of the allocation, and where the compiler cannot be upgraded to support C99+, use of alloca() is fine, and I've been known to use it.

When available, VLAs may have some advantages over alloca(): The compiler can generate stack limit checks that will catch out-of-bounds access when array style access is used (I don't know if any compilers do this, but it can be done), and analysis of the code can determine whether the array access expressions are properly bounded. Note that, in some programming environments, such as automotive, medical equipment, and avionics, this analysis has to be done even for fixed size arrays, both automatic (on the stack) and static allocation (global or local).

On architectures that store both data and return addresses/frame pointers on the stack (from what I know, that's all of them), any stack allocated variable can be dangerous because the address of the variable can be taken, and unchecked input values might permit all sorts of mischief.

Portability is less of a concern in the embedded space, however it is a good argument against use of alloca() outside of carefully controlled circumstances.

Outside of the embedded space, I've used alloca() mostly inside logging and formatting functions for efficiency, and in a non-recursive lexical scanner, where temporary structures (allocated using alloca() are created during tokenization and classification, then a persistent object (allocated via malloc()) is populated before the function returns. The use of alloca() for the smaller temporary structures greatly reduces fragmentation when the persistent object is allocated.

Upvotes: 2

phonetagger
phonetagger

Reputation: 7883

Lots of interesting answers to this "old" question, even some relatively new answers, but I didn't find any that mention this....

When used properly and with care, consistent use of alloca() (perhaps application-wide) to handle small variable-length allocations (or C99 VLAs, where available) can lead to lower overall stack growth than an otherwise equivalent implementation using oversized local arrays of fixed length. So alloca() may be good for your stack if you use it carefully.

I found that quote in.... OK, I made that quote up. But really, think about it....

@j_random_hacker is very right in his comments under other answers: Avoiding the use of alloca() in favor of oversized local arrays does not make your program safer from stack overflows (unless your compiler is old enough to allow inlining of functions that use alloca() in which case you should upgrade, or unless you use alloca() inside loops, in which case you should... not use alloca() inside loops).

I've worked on desktop/server environments and embedded systems. A lot of embedded systems don't use a heap at all (they don't even link in support for it), for reasons that include the perception that dynamically allocated memory is evil due to the risks of memory leaks on an application that never ever reboots for years at a time, or the more reasonable justification that dynamic memory is dangerous because it can't be known for certain that an application will never fragment its heap to the point of false memory exhaustion. So embedded programmers are left with few alternatives.

alloca() (or VLAs) may be just the right tool for the job.

I've seen time & time again where a programmer makes a stack-allocated buffer "big enough to handle any possible case". In a deeply nested call tree, repeated use of that (anti-?)pattern leads to exaggerated stack use. (Imagine a call tree 20 levels deep, where at each level for different reasons, the function blindly over-allocates a buffer of 1024 bytes "just to be safe" when generally it will only use 16 or less of them, and only in very rare cases may use more.) An alternative is to use alloca() or VLAs and allocate only as much stack space as your function needs, to avoid unnecessarily burdening the stack. Hopefully when one function in the call tree needs a larger-than-normal allocation, others in the call tree are still using their normal small allocations, and the overall application stack usage is significantly less than if every function blindly over-allocated a local buffer.

But if you choose to use alloca()...

Based on other answers on this page, it seems that VLAs should be safe (they don't compound stack allocations if called from within a loop), but if you're using alloca(), be careful not to use it inside a loop, and make sure your function can't be inlined if there's any chance it might be called within another function's loop.

Upvotes: 24

FreeMemory
FreeMemory

Reputation: 8634

As noted in this newsgroup posting, there are a few reasons why using alloca can be considered difficult and dangerous:

  • Not all compilers support alloca.
  • Some compilers interpret the intended behaviour of alloca differently, so portability is not guaranteed even between compilers that support it.
  • Some implementations are buggy.

Upvotes: 48

kriss
kriss

Reputation: 24207

alloca () is nice and efficient... but it is also deeply broken.

  • broken scope behavior (function scope instead of block scope)
  • use inconsistant with malloc (alloca()-ted pointer shouldn't be freed, henceforth you have to track where you pointers are coming from to free() only those you got with malloc())
  • bad behavior when you also use inlining (scope sometimes goes to the caller function depending if callee is inlined or not).
  • no stack boundary check
  • undefined behavior in case of failure (does not return NULL like malloc... and what does failure means as it does not check stack boundaries anyway...)
  • not ansi standard

In most cases you can replace it using local variables and majorant size. If it's used for large objects, putting them on the heap is usually a safer idea.

If you really need it C you can use VLA (no vla in C++, too bad). They are much better than alloca() regarding scope behavior and consistency. As I see it VLA are a kind of alloca() made right.

Of course a local structure or array using a majorant of the needed space is still better, and if you don't have such majorant heap allocation using plain malloc() is probably sane. I see no sane use case where you really really need either alloca() or VLA.

Upvotes: 11

Tony Delroy
Tony Delroy

Reputation: 106244

still alloca use is discouraged, why?

I don't perceive such a consensus. Lots of strong pros; a few cons:

  • C99 provides variable length arrays, which would often be used preferentially as the notation's more consistent with fixed-length arrays and intuitive overall
  • many systems have less overall memory/address-space available for the stack than they do for the heap, which makes the program slightly more susceptible to memory exhaustion (through stack overflow): this may be seen as a good or a bad thing - one of the reasons the stack doesn't automatically grow the way heap does is to prevent out-of-control programs from having as much adverse impact on the entire machine
  • when used in a more local scope (such as a while or for loop) or in several scopes, the memory accumulates per iteration/scope and is not released until the function exits: this contrasts with normal variables defined in the scope of a control structure (e.g. for {int i = 0; i < 2; ++i) { X } would accumulate alloca-ed memory requested at X, but memory for a fixed-sized array would be recycled per iteration).
  • modern compilers typically do not inline functions that call alloca, but if you force them then the alloca will happen in the callers' context (i.e. the stack won't be released until the caller returns)
  • a long time ago alloca transitioned from a non-portable feature/hack to a Standardised extension, but some negative perception may persist
  • the lifetime is bound to the function scope, which may or may not suit the programmer better than malloc's explicit control
  • having to use malloc encourages thinking about the deallocation - if that's managed through a wrapper function (e.g. WonderfulObject_DestructorFree(ptr)), then the function provides a point for implementation clean up operations (like closing file descriptors, freeing internal pointers or doing some logging) without explicit changes to client code: sometimes it's a nice model to adopt consistently
    • in this pseudo-OO style of programming, it's natural to want something like WonderfulObject* p = WonderfulObject_AllocConstructor(); - that's possible when the "constructor" is a function returning malloc-ed memory (as the memory remains allocated after the function returns the value to be stored in p), but not if the "constructor" uses alloca
      • a macro version of WonderfulObject_AllocConstructor could achieve this, but "macros are evil" in that they can conflict with each other and non-macro code and create unintended substitutions and consequent difficult-to-diagnose problems
    • missing free operations can be detected by ValGrind, Purify etc. but missing "destructor" calls can't always be detected at all - one very tenuous benefit in terms of enforcement of intended usage; some alloca() implementations (such as GCC's) use an inlined macro for alloca(), so runtime substitution of a memory-usage diagnostic library isn't possible the way it is for malloc/realloc/free (e.g. electric fence)
  • some implementations have subtle issues: for example, from the Linux manpage:

    On many systems alloca() cannot be used inside the list of arguments of a function call, because the stack space reserved by alloca() would appear on the stack in the middle of the space for the function arguments.


I know this question is tagged C, but as a C++ programmer I thought I'd use C++ to illustrate the potential utility of alloca: the code below (and here at ideone) creates a vector tracking differently sized polymorphic types that are stack allocated (with lifetime tied to function return) rather than heap allocated.

#include <alloca.h>
#include <iostream>
#include <vector>

struct Base
{
    virtual ~Base() { }
    virtual int to_int() const = 0;
};

struct Integer : Base
{
    Integer(int n) : n_(n) { }
    int to_int() const { return n_; }
    int n_;
};

struct Double : Base
{
    Double(double n) : n_(n) { }
    int to_int() const { return -n_; }
    double n_;
};

inline Base* factory(double d) __attribute__((always_inline));

inline Base* factory(double d)
{
    if ((double)(int)d != d)
        return new (alloca(sizeof(Double))) Double(d);
    else
        return new (alloca(sizeof(Integer))) Integer(d);
}

int main()
{
    std::vector<Base*> numbers;
    numbers.push_back(factory(29.3));
    numbers.push_back(factory(29));
    numbers.push_back(factory(7.1));
    numbers.push_back(factory(2));
    numbers.push_back(factory(231.0));
    for (std::vector<Base*>::const_iterator i = numbers.begin();
         i != numbers.end(); ++i)
    {
        std::cout << *i << ' ' << (*i)->to_int() << '\n';
        (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                         // program depends on side effects of destructor
    }
}

Upvotes: 30

Patrick Schl&#252;ter
Patrick Schl&#252;ter

Reputation: 11871

Old question but nobody mentioned that it should be replaced by variable length arrays.

char arr[size];

instead of

char *arr=alloca(size);

It's in the standard C99 and existed as compiler extension in many compilers.

Upvotes: 114

Sondhi Chakraborty
Sondhi Chakraborty

Reputation: 71

A place where alloca() is especially dangerous than malloc() is the kernel - kernel of a typical operating system has a fixed sized stack space hard-coded into one of its header; it is not as flexible as the stack of an application. Making a call to alloca() with an unwarranted size may cause the kernel to crash. Certain compilers warn usage of alloca() (and even VLAs for that matter) under certain options that ought to be turned on while compiling a kernel code - here, it is better to allocate memory in the heap that is not fixed by a hard-coded limit.

Upvotes: 7

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215617

Here's why:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

Not that anyone would write this code, but the size argument you're passing to alloca almost certainly comes from some sort of input, which could maliciously aim to get your program to alloca something huge like that. After all, if the size isn't based on input or doesn't have the possibility to be large, why didn't you just declare a small, fixed-size local buffer?

Virtually all code using alloca and/or C99 vlas has serious bugs which will lead to crashes (if you're lucky) or privilege compromise (if you're not so lucky).

Upvotes: 11

SilentDirge
SilentDirge

Reputation: 835

Everyone has already pointed out the big thing which is potential undefined behavior from a stack overflow but I should mention that the Windows environment has a great mechanism to catch this using structured exceptions (SEH) and guard pages. Since the stack only grows as needed, these guard pages reside in areas that are unallocated. If you allocate into them (by overflowing the stack) an exception is thrown.

You can catch this SEH exception and call _resetstkoflw to reset the stack and continue on your merry way. Its not ideal but it's another mechanism to at least know something has gone wrong when the stuff hits the fan. *nix might have something similar that I'm not aware of.

I recommend capping your max allocation size by wrapping alloca and tracking it internally. If you were really hardcore about it you could throw some scope sentries at the top of your function to track any alloca allocations in the function scope and sanity check this against the max amount allowed for your project.

Also, in addition to not allowing for memory leaks alloca does not cause memory fragmentation which is pretty important. I don't think alloca is bad practice if you use it intelligently, which is basically true for everything. :-)

Upvotes: 14

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91617

alloca() is very useful if you can't use a standard local variable because its size would need to be determined at runtime and you can absolutely guarantee that the pointer you get from alloca() will NEVER be used after this function returns.

You can be fairly safe if you

  • do not return the pointer, or anything that contains it.
  • do not store the pointer in any structure allocated on the heap
  • do not let any other thread use the pointer

The real danger comes from the chance that someone else will violate these conditions sometime later. With that in mind it's great for passing buffers to functions that format text into them :)

Upvotes: 79

David Thornley
David Thornley

Reputation: 57076

One issue is that it isn't standard, although it's widely supported. Other things being equal, I'd always use a standard function rather than a common compiler extension.

Upvotes: 32

JSBձոգչ
JSBձոգչ

Reputation: 41388

All of the other answers are correct. However, if the thing you want to alloc using alloca() is reasonably small, I think that it's a good technique that's faster and more convenient than using malloc() or otherwise.

In other words, alloca( 0x00ffffff ) is dangerous and likely to cause overflow, exactly as much as char hugeArray[ 0x00ffffff ]; is. Be cautious and reasonable and you'll be fine.

Upvotes: 19

RichieHindle
RichieHindle

Reputation: 281855

Processes only have a limited amount of stack space available - far less than the amount of memory available to malloc().

By using alloca() you dramatically increase your chances of getting a Stack Overflow error (if you're lucky, or an inexplicable crash if you're not).

Upvotes: 7

Related Questions