6

Let's say we have a struct ending with a flexible array member:

struct foo {
    size_t len;
    uint8_t data[];
};

How to allocate this struct on the stack (ie. memory is automatically released at the end of the scope)? In add, it would be nice if len could contain the size of the field data.

Currently, I do things like:

uint8_t buf[256];
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);

However, it is error prone. Use of alloca() may be slightly better:

struct foo *foo = alloca(256 + sizeof(struct foo));
foo->len = 256;

From there, I could define a macro like this:

#define STACK_ALLOC_FOO(SIZE) ({                          \
    struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
    _tmp->len = SIZE;                                     \
    _tmp;                                                 \
})

And declare it with:

struct foo *foo = STACK_ALLOC_FOO(256);

However, I am not sure of lifetime of the memory allocated with alloca(). Is it the inner scope or the function?

In add, it does not work to allocate a global variable (even if it is not my main concern).

Does someone has good practices in mind to allocate structures with flexible array members on stack?

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Jérôme Pouiller
  • 9,249
  • 5
  • 39
  • 47
  • 1
    memory allocated with `alloca` is killed when the function returns. which means you have no chance of returning `foo` – Raildex Jun 29 '21 at 09:28
  • 4
    @Raildex The question is about a variable on the stack. This already doesn't show any intention to return the address of the variable from the function. Please read: "(ie. memory is automatically released at the end of the scope)" – harper Jun 29 '21 at 09:33
  • 3
    `Does someone has good practices in mind to allocate VLA on stack?` Don't use them. That is not a struct with VLA but a struct with flexible array member. Variable lenght array is not a flexible array member, those are different terms. – KamilCuk Jun 29 '21 at 09:34
  • From the `alloca` man page: "This temporary space is automatically freed when the function that called alloca() returns to its caller." – interjay Jun 29 '21 at 09:34
  • Have you produced the assembly for it? – Ahmed Masud Jun 29 '21 at 09:35
  • @AhmedMasud I have done a few tests and it works. But I think it is not sufficient to guarantee it is correct. – Jérôme Pouiller Jun 29 '21 at 09:38
  • Global variables are allocated before the program start up, so you cannot usefully have a variable-length anything global. – n. m. could be an AI Jun 29 '21 at 09:40
  • What you do currently is UB for more than one reason. alloca is much better. Not portable, but at least an implementation that provides alloca presumably knows what it's doing. Lifetime of memory allocated with alloca is the same as for automatic variables of the function so nothing is lost or gained. – n. m. could be an AI Jun 29 '21 at 09:47

5 Answers5

6

Let's say we have a struct ending with a Variable Length Array (VLA):

Well, you don't. You have a struct ending with a flexible array member. Different thing and mainly used for dynamic memory allocation scenarios.

How to allocate this struct on the stack

It's pretty hard to do that without some non-standard extension. For example an alloca extension that guarantees to return memory which does not have an effective type. Meaning that the memory has not yet been marked internally by the compiler to have a certain type. Otherwise...

struct foo *foo = (struct foo *)buf;

You get strict aliasing violation undefined behavior, like in the above buggy code. What is the strict aliasing rule?

Additionally, you also need to take care of alignment & padding.

However, I am not sure of lifetime of the memory allocated with alloca(). Is it the inner scope or the function?

Yeah probably. It's not a standard function and I'm not sure any lib gives a portable guarantee of its behavior. It's not even a POSIX function. Linux man gives the guarantee that:

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.

I'm assuming this holds for gcc/glibc under *nix, but not other tool chains or systems.


What you could do instead, to get portable and rugged code, is something like this:

struct foo {
    size_t len;
    uint8_t data[];
};

struct bar256 {
  size_t len;
  uint8_t data[256];
};

typedef union
{
  struct foo f;
  struct bar256 b;
} foobar256;

Here bar256 and foobar256 could be defined locally. You can access the data either through the f.data or b.data of a foobar256. This kind of type punning is allowed and well-defined in C.

At this point you might realize that the struct is just more trouble that it's worth and just go with two local variables, one being an actual VLA:

size_t len = ... ;
uint8_t data[len];
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 3
    That `alloca` returns memory without an effective type is no different than the fact that `malloc` returns memory without an effective type. Certainly it is intended for memory returned by `malloc` to be usable for structures with flexible array members, so it would work with `alloca` too. – Eric Postpischil Jun 29 '21 at 11:18
  • @EricPostpischil Since it is not a standard function, there's no guarantees. It could be inlined or a function-like macro resulting into some character array of bytes, for example. Which might then get compiled just as regular application code. – Lundin Jun 29 '21 at 11:22
  • 2
    The rule in 6.5 6 about effective types is not specific to `malloc`; it applies to all memory without a declared type: “If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object…” So, if `alloca` is merely an extension that provides memory, then this rule applies to it. There would be no problem unless `alloca` is specifically documented to override 6.5 6. – Eric Postpischil Jun 29 '21 at 11:44
  • @Lundin Since there is no standard function,. there are no guarantees **in the standard**. Whoever supplies the function must also supply a suitable guarantee to go with it. It's in the consumer protection statute. – n. m. could be an AI Jun 29 '21 at 11:44
1

As alternative, I suggest that:

#define DECLARE_FOO(NAME, SIZE) \
  struct {                      \
      struct foo __foo;         \
      char __data[SIZE];        \
  }  __ ## NAME = {             \
      .__foo.len = SIZE,        \
  };                            \
  struct foo *NAME = &(__ ## NAME).__foo;

So you can do:

DECLARE_FOO(var, 100);

It is not very elegant. However, it works to declare global/static variables and it does not rely on any cast operator.

Jérôme Pouiller
  • 9,249
  • 5
  • 39
  • 47
0

Variable length arrays (as understood in GNU C) are generally not allocated using alloca. In C90 they are not supported.

The typical way is this:

int main() {
    int n;
    struct foo {
        char a;
        int b[n]; // n needs to be in the same scope as the struct definition
    };

    n = 1;
    struct foo a;
    a.a = 'a';
    a.b[0] = 0;
    // writing a.b[1] = 1 will not cause the compiler to complain

    n = 2;
    struct foo b;
    b.a = 'b';
    b.b[0] = 0;
    b.b[1] = 1;
}

Using -fsanitize=undefined with GCC (more specifically -fsanitize=bounds) will trigger a runtime error on accessing an out-of-bounds VLA member.

Simon Doppler
  • 1,918
  • 8
  • 26
  • You can't put a VLA in a struct. – interjay Jun 29 '21 at 09:45
  • What would be the declaration of a function that takes a `struct foo` as parameter? – Jérôme Pouiller Jun 29 '21 at 09:46
  • @interjay you are correct, what I wrote is only true for GNU C, I edited the answer. – Simon Doppler Jun 29 '21 at 09:52
  • @JérômePouiller this will get insanely complex, as you cannot modify the VLA length at file scope, but this also means it is not visible outside of the function scope at which it is defined. I think you are better off looking for *flexible array member*, it will probably better fit your needs. – Simon Doppler Jun 29 '21 at 10:11
0

If your intent is to use it like this:

#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>

struct foo {
    size_t len;
    uint8_t data[];
}; 


#define STACK_ALLOC_FOO(SIZE) ({                          \
    struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
    _tmp->len = SIZE;                                     \
    _tmp;                                                 \
})


void print_foo() {
    struct foo *h = STACK_ALLOC_FOO(sizeof("Hello World"));
    memcpy(h->data, "Hello World", h->len);
    fprintf(stderr, "[%lu]%s\n", h->len, h->data);
}

int main(int argc, char *argv[])
{
    print_foo();
    return 0;
}

Because of this:

The space allocated by alloca() is not automatically deallocated if the pointer that refers to it simply goes out of scope.

it will produce perfectly valid code because the only thing that is going out of scope is the *_tmp and that does NOT deallocate the alloca, you are still within the same stack-frame. it DOES get de-allocated with the return from print_foo.

Actually it's very interesting to see how the compiler deals with the optimization flags and the assembly output. (The alloca related code is fully duplicated in main if you e.g. use -O3)

Hopefully that helps

Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58
-1

How to allocate a struct with a Variable Length Array (VLA) on the stack

You have to make sure your buffer is properly aligned. Use unsinged char or just char to represent "bytes", uint8_t represents an 8-bit number.

#include <stdalign.h>
alignas(struct foo) unsigned char buf[sizeof(struct foo) + 20 * sizeof(uint8_t));
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);

I could define a macro like this:

The ({ is a gcc extension. You could also define a macro to define the variable, like:

// technically UB I believe
#define FOO_DATA_SIZE  sizeof(((struct foo*)0)->data)

struct foo *foo_init(void *buf, size_t bufsize, size_t count) {
    struct foo *t = buf;
    memset(t, 0, bufsize);
    t->size = count;
    return t;
}

#define DEF_struct_foo_pnt(NAME, COUNT) \
    _Alignas(struct foo) unsigned char _foo_##NAME##_buf[sizeof(struct foo) + COUNT * FOO_DATA_SIZE); \
    struct foo *NAME = foo_init(_foo_##NAME##_buf, sizeof(buf), COUNT);

void func() {
   DEF_struct_foo_pnt(foo, 20);
}

Use of alloca() may be slightly better:

Unless you happen to call alloca() in a loop...

I am not sure of lifetime of the memory allocated with alloca(). Is it the inner scope or the function?

Memory allocated with alloca gets freed at end of function or at end of scope?

it does not work to allocate a global variable (even if it is not my main concern).

It will be hard - C does not have constructors. You could use an external tool or experiment with preprocessor magic to generate code, like:

_Alignas(struct foo) unsigned char buf[sizeof(struct foo) + count * sizeof(uint8_t)) = {
     // Big endian with 64-bit size_t?
     20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
struct foo *foo_at_file_scope = (struct foo*)buf;

i.e. you have to initialize the buffer, not the struct. I think I would write a tool in C using the same compiler with same options to generate code for that (for cross-compiling in gcc-ish environment, I would only compile some executable with initialization to an ELF file, and instead of running it, get the initialization from ELF file with objdump, and the process it to generate C source).

Alternatively, you could (ab-)use GCC extension __attrbute__((__constructor__)) - define a function with that attribute in another macro. Something along:

#define DEF_FILE_SCOPE_struct_foo_pnt(NAME, COUNT) \
    _Alignas(struct foo) unsigned char _foo_##NAME##_buf[sizeof(struct foo) + COUNT * FOO_DATA_SIZE); \
    struct foo *NAME = NULL; \
    __attribute__((__constructor__)) \
    void _foo_##NAME##_init(void) { \
        NAME = foo_init(_foo_##NAME##_buf, sizeof(buf), COUNT); \
    }

DEF_FILE_SCOPE_struct_foo_pnt(foo_at_file_scope, 20)

Does someone has good practices in mind to allocate [flexible array members] on stack?

Don't use them. Instead, use pointers and malloc.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • `struct foo *foo = (struct foo *)buf;` results in an aliasing violation when `foo` is used to access the desired structure, even if `buf` is correctly aligned. (Also, the conversion of the `char *` from `buf` to `struct foo *` is not fully defined by the C standard, although that is less of a practical consideration.) – Eric Postpischil Jun 29 '21 at 11:17