Search code examples
csegmentation-faultvoid-pointersstrdup

Segmentation fault when strdupa void pointer


I'm fairly new to pointers, and void pointers is still black art to me. In the following code I get a segfault when tmp->item = strdup(item); I'm not sure how to fix.

int springmap_add(SpringMap *sm, char *key, void *item) {
    SpringMap *tmp = sm;
.
.
.
while (tmp) {

        if (!tmp->next) {

            tmp->next = springmap_init();
            tmp->next->key = strdup(key);
            tmp->next->item = strdup(item); // Segfault here
            return 1;
        }
        tmp = tmp->next;
    }
    return 0
}
 
int main(int argc, char* argv[]) {
    char* key[ESTS] = {"alpha"};
    void* ptr[ESTS] = {(void*)0xdeadbeef};

    SpringMap* map = springmap_init();
       
    for(int i = 0; i < TESTS; i++) {
    int status = springmap_add(map, key[i], ptr[i]);
    }
    springmap_free(map);
    return 0;

I'm not up to speed on void pointers.


Solution

  • The function name already tells: strdup composes of string duplicate, and it only is able to duplicate null-terminated C-strings (well, admittedly any data as long as it contains a null byte somewhere, though it would get cut off too early unless this null byte was the very last byte within the data).

    void pointers in C have the unfortunate nature of implicitly converting to any other pointer type, happening in your code as well. However these pointers do not point to null-terminated C-strings, actually, they aren't even valid at all (most of most likely, at least)! Thus trying to read from them yields undefined behaviour.

    So at first make sure that your void pointers point to valid memory. To use strdup they should point to C-strings, otherwise memcpy is the way to go, though you need to malloc storage space as target first. For both, you need the size of the object available, though. However you cannot get that back from the void pointer any more, thus you need yet another parameter.

    You could write your own objdup function covering the duplication:

    void* objdup(size_t size, void* object)
    {
        void* copy = malloc(size);
        if(copy)
        {
            memcpy(copy, object, size);
        }
        return copy;
    }
    

    Still your pointers need to be valid! Some possible example might look like:

    int main()
    {
        SomeDataType o1;
        AnotherDataType o2;
        AnotherDatatType* ptr = &o2; // create a valid pointer
                                     // (could be done by malloc'ing memory, too)
    
        void* c1 = objdup(sizeof(o1), &o1);
        //                            ^ take the address of a REAL object!
        if(c1)
        {
            void* c2 = objdup(sizeof(*o2), o2); // or via pointer to REAL object
            if(c2)
            {
                // ...
    
                free(c2);
            }
            free(c1);
        }
        return 0;
    }