-3

So I have these two structs. Only need to worry about these three variables; name_size, name and xattrs[0].

typedef struct dxattr {
    unsigned int name_size;        /* length of name string in bytes */
    unsigned int value_offset;     /* offset of value in value blocks */
    unsigned int value_size;       /* length of value string in bytes */
    char name[0];                  /* reference to the name string */
} dxattr_t;

typedef struct xcb {
    unsigned int value_blocks[5];  /* blocks for xattr values */
    unsigned int no_xattrs;            /* the number of xattrs in the block */
    unsigned int size;                 /* this is the end of the value list in bytes */
    dxattr_t xattrs[0];                /* then a list of xattr structs (names and value refs) */
} xcb_t;

First I update index 0 in xattrs

xcb_t *xcb = (xcb_t*)malloc( sizeof(xcb_t) );

xcb->xattrs[0].name_size = 5;

memcpy( xcb->xattrs[ 0 ].name, "keith", 5 );
xcb->xattrs[ 0 ].name[ 5 ] = 0; //null terminator

printf("xcb->xattrs[0].name = %s\n", xcb->xattrs[0].name );
printf("xcb->xattrs[0].name_size = %d\n", xcb->xattrs[0].name_size );

The output is;

xcb->xattrs[0].name = keith                                                                                             
xcb->xattrs[0].name_size = 5 

Then I try updating the 2nd index.

memcpy( xcb->xattrs[ 1 ].name, "david", 5 );
xcb->xattrs[ 1 ].name[ 5 ] = 0; //null terminator

printf("xcb->xattrs[0].name = %s\n", xcb->xattrs[0].name );

xcb->xattrs[1].name_size = 5;

printf("xcb->xattrs[0].name = %s\n", xcb->xattrs[0].name );

Immediately after updating the "name_size" variable, the "name" value in previous index gets erased.

xcb->xattrs[0].name = keith                                                                                             
xcb->xattrs[0].name = 

Any idea why this happens?

Keith
  • 35
  • 8
  • For one you are only allocating memory for one xcb_t structure. And if you want to use zero size arrays in a data structure you should read up about how to use them before. You can start here https://stackoverflow.com/questions/14643406/whats-the-need-of-array-with-zero-elements – hko Dec 05 '19 at 18:20
  • This code is located within a region that has already been malloc'd. This means I can't malloc again and update the size. I'm assuming there's no space to store the names in each index, but I'm not sure how to increase the size of each index based on the # of additional bytes that I need to write the "name" value. – Keith Dec 05 '19 at 18:23
  • Then you should give us more context. Seeing this line: `xcb_t *xcb = (xcb_t*)malloc( sizeof(xcb_t) );` and afterwards this line: `memcpy( xcb->xattrs[ 0 ].name, "keith", 5 );` or `xcb->xattrs[1].name_size = 5;` means you are accesiing memory beyond what you allocated. – hko Dec 05 '19 at 18:32
  • The code is part of a file with thousands of lines, I only posted the relevant parts cause its unrealistic to post thousands of lines of code on here. Anyways, are you saying I have to malloc space for dxattr_t struct? For each index in xattrs? – Keith Dec 05 '19 at 18:40
  • These : `char name[0];` and `dxattr_t xattrs[0];` are both meaningless declarations. As has been pointed out in other comments. Why are they there? What do you ever intend to do with them – ryyker Dec 05 '19 at 18:44
  • Their values are updated during runtime. A string is copied into name[0]. Structs of type dxattr_t are allocated in xattrs and used. – Keith Dec 05 '19 at 18:49

2 Answers2

1

As I understand from your code, you are trying to "hack the struct". To do so, it is better to use a standard feature of C11 called "Flexible array member". The syntax is rather to be like this:

typedef struct dxattr {
    unsigned int name_size;        /* length of name string in bytes */
    unsigned int value_offset;     /* offset of value in value blocks */
    unsigned int value_size;       /* length of value string in bytes */
    char name[ ];                  /* reference to the name string */
} dxattr_t;

typedef struct xcb {
    unsigned int value_blocks[5];  /* blocks for xattr values */
    unsigned int no_xattrs;            /* the number of xattrs in the block */
    unsigned int size;                 /* this is the end of the value list in bytes */
    dxattr_t xattrs[ ];                /* then a list of xattr structs (names and value refs) */
} xcb_t;

Note that the last member array is declared without constant size 0 as it is an incomplete type. I am new to Stack Overflow, so maybe your question is something deeper that my shared knowledge maybe not sufficient.

Gor Stepanyan
  • 326
  • 2
  • 5
  • I made the edit and re-ran the code but the same problem still persists. Thanks for the attempt though. – Keith Dec 05 '19 at 18:35
  • Then, maybe a helpful thing will be to allocate memory for each index in the dxattr_t array in xcb struct. – Gor Stepanyan Dec 05 '19 at 18:43
  • Tried that but each index is a variable and malloc returns a pointer. – Keith Dec 05 '19 at 18:44
  • I mean that, first of all, the size of your struct array may increase or even decrease, so you need to malloc the array at first, then each element of that array is also a struct which also needs to be malloced. Remember to free all the malloced staff to prevent memory leaks. – Gor Stepanyan Dec 05 '19 at 18:52
1

It is up to you to allocate enough memory for the number of xattrs and the sizes of name in the xattrs. When there is just 1 indeterminate array size indicated by [ ] or by [ 0 ] it is not so difficult, but where there are multiple dimensions it is very painful.

In fact you can't then use the xattrs member as an array to index higher than 1. It is up to you to write byte address calculation code to determine the address of the second instance of xattrs based on the size of the name filed of the first instance, and then do all that work again if there is a third instance of xattrs.

If you can't simply declare avery large buffer and read it all in 1 go, and you insist on preserving this structure, then the best you can do is something like:

keep a note of total size
malloc the minimum size => total size (as you have done)
read only the minimum size
for the number of attrs
    next totalsize+= 1 minimum xattr
    realloc for next total size
    generate an xattr pointer based on the current total size
    read the minimal attr to that pointer
    next totalsize+= name size
    realloc for next total size
    generate an xattr pointer based on the current total size
    read the name
    current = next size

These sorts of data structures are sometimes used to describe binary files on disk, or a blob to transmit through RPC, as a schematic. But they are not actually easy to manipulate in reality.

Note also that on some platforms (including x86 if you enable sse optimisations) you will have issues with the alignment of the int members of these data structures.

If you are just trying to stream this structure from disk into memory, declare yourself a new data structure which uses pointers - a pointer to name and either an array of pointers to attr or a pointer to an array of pointers to attr, that way you can malloc each thing as you need it and build an equivalent datastructure.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
  • Can you point me to a resource with instructions on how to allocate memory for such a data structure? I tried allocating memory for each struct in each index but malloc returns a pointer and each index is not a pointer. Also, what do you by having to perform byte address calculation? Can't I just use indexing to find the location of each struct within the array? – Keith Dec 05 '19 at 19:04
  • No you simply cant do simple indexing, because the xattr contains the name inline. Where are the characters of that name going to live? They have to be before the next xatrr instance, so you can't know that address. – Gem Taylor Dec 05 '19 at 19:09
  • I'm currently trying to understand the pseudocode you wrote. Could you explain why "name" variable is being erased when I update name_size in adjacent index? And how you're pseudocode fixes this issue? I think understanding that part will go a long way in understanding your code. I suspected the reason for corruption was related to how they were located in heap. No space was allocated for name so when I copied data into it, it wrote into the memory location for the next struct. In particular, name_size data in that next struct. – Keith Dec 05 '19 at 19:20
  • So I am saying the next attr object occurs at offset totalsize, which we increased enough to make room for the previous attr and name. Note that we have to use offsets here because the realloc may return a new address for the start of the structure. – Gem Taylor Dec 05 '19 at 19:31
  • Oh okay. So essentially, we're increasing the space allocated to each index everytime we add a new index? So don't we only have to increase the space allocated to the former last index, before we add a new index? Since the rest have already been adjusted. – Keith Dec 05 '19 at 20:15
  • I really appreciate your help. But I'm having trouble understanding your pseudocode, is it possible to write c code? – Keith Dec 05 '19 at 20:17
  • Sorry, the pseudocode is as much effort as I can put in. If you have grasped the issue that the strings need space, then you are best placed to solve it. I would personally recommend using a different equivalent data structure that uses ponters instead of array. And probably some sort of preallocated buffer to stream the data into and out of. – Gem Taylor Dec 06 '19 at 11:24