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?
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With