Search code examples
cmemory-managementstructvalgrindcircular-buffer

Getting data from pointer in struct "Invalid read/write"


I am trying to do a implementation of circular buffer in array. I keep my data in structure and manage it by few methods as push, pop, etc. The program is more or less functional and behave as expected, however I run into errors in my valgrind test. And I am not capable of finding out what is wrong with my code. Although it seems like managing data via pointers in my struct is the crucial problem. I would be very grateful if anyone could point me in the right direction coz I am really lost at this point.

This is how my struct looks like:

typedef struct queue_t{
    int* data;
    int* end;
    int* head;
    int* tail;
    int max_length;
    int cur_length;
} queue_t;

Here are my methods to manage buffer operations:
(Commented code produces pretty much the same errors as memcpy)

int* increase(int* point, queue_t* queue){
    if(point != queue->end){
        point = point + sizeof(int*);
        return point;
    }else{
        return queue->data;
    }
}

    queue_t* create_queue(int capacity){
        queue_t* fifo;
        fifo = malloc(sizeof(queue_t));
        fifo->data = malloc((capacity) * sizeof(int*));
        fifo->end = fifo->data + (capacity*sizeof(int*));
        fifo->head = fifo->data;
        fifo->tail = fifo->data;
        fifo->cur_length = 0;
        fifo->max_length = capacity;
        return fifo;
    }

    void delete_queue(queue_t *queue){
        free(queue->data);
        free(queue);
    }

    bool push_to_queue(queue_t *queue, void *data){
        int *temp = (int*) data;
        //*(queue->tail) = *temp;
        memcpy(queue->tail, temp, sizeof(int));
        free(data);
        if(queue->max_length != queue->cur_length){
            queue->cur_length++;
        }

        queue->tail = increase(queue->tail, queue);

        if(queue->tail == queue->head){
            queue->head = increase(queue->head, queue);
        }
        return true;
    }

    void* pop_from_queue(queue_t *queue){
        if(queue->cur_length == 0){
            return NULL;
        }
        int *item = malloc(sizeof(int*));
        //*item = *(queue->head);
        memcpy(item, queue->head, sizeof(int));
        queue->head = increase(queue->head, queue);
        queue->cur_length--;
        return item;
    }

This is my main method to test funcionality of mentioned buffer operations:
(queue.h is where my functions are defined)

#include "queue.h"


void print_int(void* p){
    if(p != NULL){
        printf("%d\n", *((int*)p));
    } else {
        printf("NULL\n");
    }
}

int main(){
    int n = 2;
    int max = 10;
    queue_t *q;


    q = create_queue(n);

    for(int i = 0; i<max;i++){
        int* p = malloc(sizeof(int));
        *p = i;
        if(!push_to_queue(q, (void*)p)){
            free(p);
            exit(101);
        }
    }

    for(int i = 0;i<max;i++){
        void* p = pop_from_queue(q);
        print_int(p);
        free(p);
    }
    delete_queue(q);

    return 0;
}

And finally this is my valgrind output:

==20293== HEAP SUMMARY:
==20293==     in use at exit: 0 bytes in 0 blocks
==20293==   total heap usage: 15 allocs, 15 frees, 1,136 bytes allocated
==20293== 
==20293== All heap blocks were freed -- no leaks are possible
==20293== 
==20293== ERROR SUMMARY: 7 errors from 2 contexts (suppressed: 0 from 0)
==20293== 
==20293== 1 errors in context 1 of 2:
==20293== Invalid read of size 4
==20293==    at 0x40097C: pop_from_queue (queue.c:72)
==20293==    by 0x400713: main (main.c:30)
==20293==  Address 0x52030f0 is 16 bytes before a block of size 4 free'd
==20293==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20293==    by 0x4008B8: push_to_queue (queue.c:51)
==20293==    by 0x4006D5: main (main.c:23)
==20293==  Block was alloc'd at
==20293==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20293==    by 0x4006B5: main (main.c:21)
==20293== 
==20293== 
==20293== 6 errors in context 2 of 2:
==20293== Invalid write of size 4
==20293==    at 0x4008AB: push_to_queue (queue.c:50)
==20293==    by 0x4006D5: main (main.c:23)
==20293==  Address 0x52030d0 is 16 bytes after a block of size 16 alloc'd
==20293==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20293==    by 0x4007FB: create_queue (queue.c:33)
==20293==    by 0x40069E: main (main.c:18)
==20293== 
==20293== ERROR SUMMARY: 7 errors from 2 contexts (suppressed: 0 from 0)

Pointed lines of code are:

72: memcpy(item, queue->head, sizeof(int));
50: memcpy(queue->tail, temp, sizeof(int));

Thanks a lot in advance, I hope someone will be able to show me, what is that bad practice I am doing here :/


Solution

  • There's a few problems with this. First, you shouldn't cast the data to an int* because it can be a pointer to anything. In your struct declaration, the data array and all the other pointers should be declared as void** since it points to this void* type that is stored in the array. You don't actually need memcpy at all. You just assign it like this: *(queue->tail) = data; where data is of type void*. In my opinion, a more clear way would be to just store the head and tail as integers (as an index relative to the array) - then you could do this: queue->data[queue->tail] = data; without having to deal with the pointers manually.

    Right now what you're doing on these lines:

    int *item = malloc(sizeof(int*));
    memcpy(item, queue->head, sizeof(int));
    

    is allocating some memory that never gets freed but more importantly, you're not actually even returning the value that was stored in queue->head. You're returning the address of the block of memory you just allocated for the item. To get the value, you would have to dereference it with a star, as in: return *item; Again, what you really want though is a simple assignment: void *item = *(queue->head);