Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable length arrays in struct

I'm writing an application in C (as a beginner) and I'm struggling with getting corrupted data inside a struct that contains a variable length array. I found similar issues described in forum posts on cprogramming.com and also on cert.og/secure-coding. I thought I'd had found the right solution, but it seems not.

The struct looks like this;

typedef struct {
    int a;
    int b;
} pair;

typedef struct {
    CommandType name;
    pair class;
    pair instr;
    pair p1;
    pair p2;
    pair p3;
    CommandType expected_next;
    char* desc;
    int size;
    pair sw1;
    pair sw2;
    pair* data;
} command;

With the problematic one being "command". For any given instance (or whatever the correct phrase would be) of "command" different fields would be set, although in most cases the same fields are set albeit in different instances.

The problem I have is when trying to set the expected_next, name, sw1, sw2, size and data fields. And it's the data field that's getting corrupt. I'm allocating memory for the struct like this;

void *command_malloc(int desc_size,int data_size)
{
    return malloc(sizeof(command) +
                  desc_size*sizeof(char) +
                  data_size*sizeof(pair));
}

command *cmd;
cmd = command_malloc(0, file_size);

But when I (pretty) print the resulting cmd, the middle of the data field appears to be random garbage. I've stepped through with gdb and can see that the correct data is getting loaded into the the field. It appears that it's only when the command gets passed to a different function that it gets corrupted. This code is called inside a function such as;

command* parse(char *line, command *context)

And the pretty-print happens in another function;

void pretty_print(char* line, command* cmd)

I had thought I was doing things correctly, but apparently not. As far as I can tell, I construct other instances of the struct okay (and I duplicated those approaches for this one) but they don't contain any variable length array in them and their pretty-prints looks fine - which concerns me because they might also be broken, but the breakage is less obvious.

What I'm writing is actually a parser, so a command gets passed into the parse function (which describes the current state, giving hints to the parser what to expect next) and the next command (derived from the input "line") is returned. "context" is free-d at the end of the parse function, which the new command getting returned - which would then be passed back into "parse" with the next "line" of input.

Can anyone suggest anything as to why this might be happening?

Many thanks.

like image 379
Tom Avatar asked Mar 04 '13 21:03

Tom


People also ask

Can a struct variable be an array?

A structure may contain different data types – char, int, double, float, etc. It may also include an array as its member. The struct keyword is used to create a structure.

Can you store an array in a struct?

A structure may contain elements of different data types – int, char, float, double, etc. It may also contain an array as its member. Such an array is called an array within a structure. An array within a structure is a member of the structure and can be accessed just as we access other elements of the structure.

What is variable length structure?

In computer programming, a variable-length array (VLA), also called variable-sized or runtime-sized, is an array data structure whose length is determined at run time (instead of at compile time). In C, the VLA is said to have a variably modified type that depends on a value (see Dependent type).

Can we declare array inside structure?

For the structures in C programming language from C99 standard onwards, we can declare an array without a dimension and whose size is flexible in nature. Such an array inside the structure should preferably be declared as the last member of structure and its size is variable(can be changed be at runtime).


1 Answers

When you allocate memory to structure, only a pointer size gets allocated to *desc. You must allocate memory to the space (array contents) desc points to, as someone already pointed out. Purpose of my answer is to show slightly different way of doing that. Since having a pointer *desc increases structure size by a word (sizeof pointer), you can safely have a variable length array hack in you structure to reduce structure size.

Here's how your structure should look like, notice that desc[] has been pulled down to the end of structure :

typedef struct {
    CommandType name;
    pair class;
    pair instr;
    pair p1;
    pair p2;
    pair p3;
    CommandType expected_next;
    int size;
    pair sw1;
    pair sw2;
    pair* data;
    char desc[];
} command;

Now, 1. Allocate memory for command which includes array size also :

 command *cmd = malloc(sizeof(command) + desc_length);
  1. Use desc :

    cmd->desc[desc_length -1] = '\0';

This hack works only if member is at the end of structure, saves structure size, saves pointer indirection, can be used if array length is structure instance specific.

like image 77
NeilBlue Avatar answered Sep 20 '22 12:09

NeilBlue