0

why do I get an Invalid read of size 8

My goal is to pass an array of integers to a function that will return a pointer to the last Item in the linked list and it will fill the array of Item pointers with pointers for each item created

everything works fine and compiling does not show any errors and running the program does not show any errors as well but when I use valgrind with it I get the error in valgrind output below

typedef struct Item
{
    int num ;   
    struct Item *next;  
}Item;


Item * create_list(int * arr, int len, Item ** lst)
{
    Item * tmpItem = malloc(sizeof(Item));

    for (int i=0; i < len; i++)
    {
        lst[i] = tmpItem;
        tmpItem->num = arr[i];
        if ( i+1!=len )
        {
            tmpItem->next = malloc(sizeof(Item));
            tmpItem = tmpItem->next;
        }
        else
            tmpItem->next = NULL;
    }
    return tmpItem;
}



void free_lst(Item ** lst, int len)
{
    for (int i =0; i < len; i++)
    {
        free(lst[i]);
    }
}
int main()
{
    int arr[] = {1,2,3,4,5};
    Item * items[sizeof(arr)/sizeof(int)];
    Item * tmp = create_list(arr, sizeof(arr)/sizeof(int), items);
    free_lst(items, sizeof(arr)/sizeof(int));
    printf('%p\n',tmp->next);

}

valgrind output

==12169== Invalid read of size 8
==12169==    at 0x109270: main (main.c:26)
==12169==  Address 0x4a59228 is 8 bytes inside a block of size 16 free'd
==12169==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==12169==    by 0x10939D: free_lst (list.c:45)
==12169==    by 0x10926B: main (main.c:24)
==12169==  Block was alloc'd at
==12169==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==12169==    by 0x10931C: create_list (list.c:24)
==12169==    by 0x109209: main (main.c:13)
==12169== 

I don't understand why am I getting this error but I guess this is because the last element in the linked list has a null pointer and this causes the last element to be 8 bytes long not 16

KGBGUY
  • 29
  • 5
  • 2
    Why do you think the size of a null pointer is different from any other pointer? – Barmar May 16 '22 at 16:21
  • You should rethink your creation loop. The `if ( i+1!=len )` should not be necessary, you have an equivalent condition in the `for` loop. You just need to rearrange the order of things a bit. – Eugene Sh. May 16 '22 at 16:22
  • The error doesn't say that the size is invalid. It's saying that the data being read is invalid (uninitialized), and its size happens to be 8 bytes. – Barmar May 16 '22 at 16:22
  • You are mixing a linked-list with an array? – Neil May 16 '22 at 16:23
  • 1
    @Neil i am trying to convert an array to a linked list – KGBGUY May 16 '22 at 16:28
  • @EugeneSh. can you explain this more in an answer with a code snippet, please – KGBGUY May 16 '22 at 16:29
  • @Barmar I assumed it would be different – KGBGUY May 16 '22 at 16:31
  • That makes no sense. The size is the size of the pointer variable, it has to be able to hold either null or non-null pointers. – Barmar May 16 '22 at 16:32
  • 2
    Fyi, change `int *next;` in the structure to `struct Item *next;`, which is what it should be in the first place. – WhozCraig May 16 '22 at 16:35
  • @WhozCraig can you explain why should I do so – KGBGUY May 16 '22 at 16:37
  • Because an `int` is not `struct Item`, would be reason enough for me (and your code). – WhozCraig May 16 '22 at 16:41
  • I don't see how this code can possibly create this error, since you don't touch the items after freeing them – user253751 May 16 '22 at 16:58
  • Always enable and heed your compiler's warnings (e.g. using `-Wall -Wextra -pedantic` using gcc). Should be `struct Item { int num; struct Item *next; }; typedef struct Item Item;`. Do you still have the problem after fixing this (which I think results in undefined behaviour)? – ikegami May 16 '22 at 17:07
  • @ikegami I did `gcc -Wall -pedantic -ggdb3` and this does not show any warnings or errors – KGBGUY May 16 '22 at 17:20
  • Are you sure you are compiling the same code you posted here? Every version of gcc I tried gives `warning: assignment to 'Item *' from incompatible pointer type 'int *'`, as it rightly should (that is the issue that WhozCraig noted above). https://godbolt.org/z/WEvYqW7sr – Nate Eldredge May 16 '22 at 17:24
  • I also cannot reproduce any valgrind error with your code. Again, are you sure it is exactly the same version you are compiling and testing? (In fact it can't be *exactly* the same because you are missing `#include ` which should produce a ton of warnings.) – Nate Eldredge May 16 '22 at 17:28
  • @KGBGUY You're assigning to a `int *` from a `Item *`. There's no compiler in existence that won't warn you about that major error. Noteably, gcc does warn contrary to your claim – ikegami May 16 '22 at 18:28
  • As far as I understand the problem was in the `printf` line in the main function, it is printing the address of a null pointer and this causes the error to appear – KGBGUY May 17 '22 at 05:38

2 Answers2

2

Two main bugs here:

  1. printf('%p'... instead of printf("%p"...

  2. You try to printf() tmp->next after you've already freed it in free_lst(). Meaning that you've already freed this heap block, but you're still trying to read from it. And that's why valgrind throws an error.

malkaroee
  • 187
  • 1
  • 6
0

Some problems

One is missing required headers.

typedef struct Item

See discussion of typedef.

Item * create_list(int * arr, int len, Item ** lst)

The first two arguments are necessary, but the last one is unused. In the function itself, one forward declares the pointer in a manner that is confusing and will not work for a null list. This could be simplified.

void free_lst(struct Item ** lst, int len)

This frees a list as an array. This doesn't make sense since one is converting it to a linked-list.

int main()

See What's the correct declaration of main()?.

Item * items[sizeof(arr)/sizeof(int)];

Why do you need an array of pointer to Item?

free_lst(items, sizeof(arr)/sizeof(int));

One is freeing the array of elements that haven't been initialized. I think you are seeing this behaviour from valgrind because free is being called on uninitialized memory.

printf('%p\n',tmp->next);

See when should I use single quotes? One assumes that the list is not empty and prints the second element?

Intent

One is trying to push an item on the linked-list, an operation that is efficient, but the code is unnecessarily complex. Also, when one pushes a dynamically allocated object, it makes sense to pop to free the object. I've modified one's code with error checking (assuming POSIX-like compliance) and the changes listed above.

#include <stdlib.h>
#include <stdio.h>

struct Item
{
    int num ;
    struct Item *next;
};

int main(void)
{
    int arr[] = {1,2,3,4,5};
    struct Item * head = 0, *cursor;
    /* `size_t` stddef, included by stdlib and stdio */
    size_t i = sizeof arr / sizeof *arr;
    int success = EXIT_SUCCESS; /* stdlib */
    while(i) { /* Push backwards. */
        struct Item *tmp;
        if(!(tmp = malloc(sizeof *tmp))) goto catch; /* stdlib */
        tmp->num = arr[--i];
        tmp->next = head;
        head = tmp;
    }
    for(cursor = head; cursor; cursor = cursor->next)
        printf("%d\n", cursor->num); /* stdio */
    goto finally;
catch:
    success = EXIT_FAILURE; /* stdlib */
    perror("to list"); /* stdio */
finally:
    while(head) { /* Pop off linked-list. */
        struct Item *const prev = head;
        head = head->next;
        free(prev); /* stdlib */
    }
    return success;
}
Neil
  • 1,767
  • 2
  • 16
  • 22