Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer in Struct -- unknown sized array

Tags:

c

struct

I am trying to implement a TLV (Type-Length-Value) in C, however I am having issues with getting the dynamic size of value to work correctly.

My struct looks like this:

typedef struct __attribute__((packed)){
   unsigned char type;
   unsigned char length;
   unsigned char * value;
} TLV;

I am trying to cast an array to the struct so that I can easily access the type, and length. For example an array:

unsigned char test[5] = {(unsigned char)'T', 0x03, 0x01, 0x02, 0x03};

Where the 'T' in the array is the type, the first 0x03 is the length.

I am casting the array to the struct like so:

TLV* tlv = (TLV*)test; 

Yet when I try to access the value array I get a segmentation fault, even when I try to access the first element of the value memory address (which should be the first element in the array after the length).

How can I get around this segmentation fault?

like image 217
Dean Avatar asked Sep 30 '22 00:09

Dean


1 Answers

value is not array, it's a pointer (which is pointing somewhere outside the struct). If you want array (of unknown size), write unsigned char value[1] instead.

typedef struct __attribute__((packed)) {
    unsigned char type;
    unsigned char length;
    unsigned char value[1];
} TLV;

Having an array of size 1 allows you to actually address any number of bytes. That is actually UB, but it is actually used and works properly in all cases I saw.

GCC allows to use arrays of size 0. I am so used to that convention that I forgot that arrays of size 0 are not allowed in C.

Edit:

Long answer

There is a difference between arrays and pointers. While you can use the similar code to work with both, these are still different beasts.

Disclaimer: The following code works in gcc, but it might not be strictly valid. I did not try to make it completely valid.

Let's define two structures:

typedef struct {
    char p[20];
} sa;

typedef struct {
    char *p;
} sp;

And create instances of those:

sa x = { "Hello, world" };
sp y = { "Howdy, world" };

What's the difference between those two?

printf("%s\n", x.p); // prints "Hello, world"
printf("%s\n", y.p); // prints "Howdy, world"

What about addresses of these?

printf("address of x = %p\n", &x); // On my machine it prints 0x7fffacce9b20
printf("address of y = %p\n", &y); // 0x7fffacce9b10

Well.. not really interesting except that these numbers are .. quite similar - both structures are located in about the same spot - in my case it's stack, end of address space, but could be somewhere else.

printf("address of x.p = %p\n", &x.p); // 0x7fffacce9b20
printf("address of y.p = %p\n", &y.p); // 0x7fffacce9b10

Same numbers. As expected actually.

printf("address of x.p[0] = %p\n", &x.p[0]); // 0x7fffacce9b20 - same as before
printf("address of y.p[0] = %p\n", &y.p[0]); // 0x400764 - could be anything

Now these are different. The string "Hello, world" is located in the same spot as structure x, while string "Howdy, world" is located somewhere else - data segment, somewhere in the beginning of address space, but, again could be somewhere else.

So this is the difference: array is some data stored "here", while pointer is only address of the data stored "somewhere".

In your case you want to be able to keep the data somewhere "here" - right after the type and length. This is why you need arrays, not pointers.

I can not find any proof that the TLV implementation above is not UB, but I saw a lot of cases where array of chars was "parsed" by casting it to pointer to some structure. I even wrote a code like this myself.

0-size arrays

As I said before, arrays of size 0 are not allowed by C standard. But they are allowed by GCC and this is handy because it allows you to do the following:

typedef struct {
    unsigned char type;
    unsigned char length;
    unsigned char value[0];
} TLV;

int required_length = 10;
TLV *tlv = (TLV *) malloc(sizeof(TLV) + required_length);

Without 0-size arrays you'd have to add (or subtract? subtract I guess) 1 somewhere in the code above.

like image 195
aragaer Avatar answered Oct 06 '22 20:10

aragaer