How does C99 handle being unable to create a variable length array at run time?

439 views Asked by At

I program embedded systems. One golden rule is that we never call malloc(); all data must be statically allocated at compile time.

Hence, I am not really familiar with Variable Length Arrays, which were introduced with C99.

The concept seems clear enough, and I don't need it explaining. My question is what happens at run time if there isn't enough free memory for such an array?

I would imagine that it is o/s dependent, maybe compiler dependent, to what would GCC/Linux do, and MS visual Studio C on Windows? Any X99 or Posix definitions?

2

There are 2 answers

13
supercat On BEST ANSWER

From the point of view of the Standard, an attempt to allocate a VLA with a size the implementation cannot accommodate invokes Undefined Behavior. Because the Standard provides no means of discovering what size array an implementation could safely create, and does not mandate that implementations allow any particular size, any attempt to create a VLA object with a size greater than 1 should be regarded as invoking Undefined Behavior except in cases where one happens to know enough about implementation's inner workings to determine the size of VLA it will be able to handle.

If malloc() is unavailable, one's best bet may be to define a large array of whatever type has the coarsest alignment requirement, store its address into a volatile-qualified pointer [the storage in which the pointer itself resides should be thus qualified] read it back, and interpret that as the start of a memory pool. No other use should be made of the original array object. While the Standard wouldn't guarantee that a compiler wouldn't decide that it should generate code that checks whether the pointer still identifies the original object and, if it does, skipping any code that would use that pointer to access anything other than the original object's type, the use of volatile on the pointer should make that really unlikely.

Once a memory pool is created, you can write your own memory-management functions to use it, though any time a pointer is returned to the pool it may be necessary to use the volatile-pointer-laundering hack to prevent compilers from using type-based aliasing to justify treating the last uses of storage as its old type as unsequenced relative to the first uses of storage as a new type.

0
plugwash On

Variable length arrays are typically allocated on the stack. Like any other variable allocated on the stack this is normally done by subtracting from the stack pointer (or adding to it for an upwards-growing stack). A frame pointer is likely to be used so that the function can keep track of it's stack frame in the face of dynamically determined changes to the stack pointer. As with other stack allocations there is typically no error checking in this process.

This brings a couple of dangers.

  1. The space allocated to the stack may be overflowed. Depending on the platform this may result in some kind of memory error from the kernel, it may result in the platform dynamically allocating more stack space or it may result in overwriting of other memory.
  2. On platforms that use protection pages beyond the stack for automatic stack growth and/or detecting stack overflows a sufficiently large stack allocation may "skip over" those pages. This may lead to memory protection errors or worse memory corruption in cases where the stack overflow would normally have been caught.

Neither of these risks is specific to variable length arrays but variable length arrays make them far more likely to lie hidden until the code is invoked with particular parameters.