Search code examples
cmallocc99flexible-array-member

Odd behaviour using flexible array member


I tried to replace a void* member of a struct with a flexible array member using the more accepted idiom:

typedef struct Entry {
    int counter;
    //void* block2; // This used to be what I had
    unsigned char block[1];
}

I then add entries into a continuous memory block:

void *memPtr = mmap(NULL, someSize*1024, PROT_READ|PROT_WRITE, 
                        MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

as such:

int number = 0;

int AddEntry(void *data) {
   Entry *entry;
   entry = malloc(sizeof(Entry) + ((SECTOR_SIZE-1) * sizeof(unsigned char));
   entry->counter = 1;
   memcpy(entry->block, data, SECTOR_SIZE);

   // make sure number doesn't overflow space, etc...
   memcpy(&memPtr[number], entry, sizeof(Entry) + ((SECTOR_SIZE-1) * sizeof(unsigned char));
   number++;
   return 0;
}

The problem is unpacking this data once I need it. For example, if I do:

void * returnBlock(int i) {
    Entry * entry  = &memPtr[i];
    printf("Entry counter is %d\n", entry->counter); // returns 1, reliably
    return entry->block; // Gives me gibberish but not if I uncomment void* block2.
}

Is there a reason this could be? I don't necessarily think I'm stomping on stuff anywhere, and it used to work with the void* approach. The weird thing is that if I put a dummy void* back into the struct, it works. It doesn't work if I put in a dummy int.

Edit: actually, it also fails if number in AddEntry is not 0. What am I stepping on, if anything?


Solution

  • Your problem is here:

    &memPtr[number]
    

    Since memPtr is a void * pointer, this isn't actually allowed in C at all. Some compilers do allow arithmetic on void * pointers as a language extension - however they treat it as if it were a char * pointer.

    That means that &memPtr[number] is likely indexing only number bytes into your memory block - so the second Entry structure copied in will overlap the first one, and so on.

    Your allocation line appears to be assuming 1024 bytes per Entry (if someSize is a number of Entry structures), so you probably want something like:

    ((char *)memPtr + number * 1024)
    

    (and similar in the returnBlock() function).

    However, if you do this you will notice that there is no point in using the flexible array member - because you're creating a contiguous array of these structures, and don't have a separate index, you have to assume each one is a fixed size. This means that you might as well make each one a fixed size:

    typedef struct Entry {
        int counter;
        unsigned char block[1024 - sizeof counter];
    }