Why is reading a struct pointer field invalid?

2019-08-24 06:43发布

Running the program in Valgrind, it says that there is an "Invalid read of size 8" at the transition pointer of the struct. It has something to do with the calloc? It is (nil) if it is read as is.

Having a struct (called trie), it is used as follows:

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

const int MAX_SIZE = 20;

struct _trie {
    int maxNode;
    int nextNode;
    int** transition;
    char* fin;
};

typedef struct _trie * Trie;

Trie createTrie (int maxNode){

    Trie trie;

    trie = (Trie) malloc(sizeof(Trie));

    printf("size of trie: %lu, size of the struct: %lu, size of _trie: %lu\n",sizeof(trie),sizeof(Trie), sizeof(struct _trie));

    trie->maxNode = maxNode;

    printf("maxNode = %d, size of maxNode: %lu\n",trie->maxNode,sizeof(trie->maxNode));
    printf("size of nextNode : %lu, size of transition: %lu, size of fin: %lu\n",
           sizeof(trie->nextNode),sizeof(trie->transition),sizeof(trie->fin));

Here, when valgrid tries to read, it says "Invalid read of size 8":

    //invalid read
    printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);

Same message from the char * fin:

    //invalid read
    printf("fin points to: %p, address: %p\n",trie->fin,&trie->fin);

    getchar();

    trie->transition = (int**)calloc(maxNode,sizeof(int*));

    printf("trie->transition done.\n");
    printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);

    if(trie->transition == NULL){
        printf("null for trie->transition\n");
        exit(0);
    }

    printf("Size of transition: %lu, size of int:%lu, pointer: %p\n\n",sizeof(trie->transition),sizeof(int),trie->transition);

    for(int counter = 0; counter < maxNode; ++counter){

        trie->transition[counter] = calloc(UCHAR_MAX,sizeof(int));

        if(trie->transition[counter] == NULL){
            printf("null for trie->transition[%d]\n",counter);
            exit(0);
        }

        //printf("size of transition[%d]: %lu\n",counter,sizeof(trie->transition[counter]));

    }


    printf("\nFilling up trie->transition\n");


    for(int counter = 0; counter < maxNode; ++counter){


        for(int counter2 = 0; counter2 < UCHAR_MAX; ++counter2){

            trie->transition[counter][counter2] = -1;

            //printf("size of transition[%d][%d]: %lu, value: %d\n",counter,counter2,sizeof(trie->transition[counter]),trie->transition[counter][counter2]);

        }

        //getchar();
    }

    return (trie);
}

void free_all(Trie trie){

    for(int counter = 0; counter < trie->maxNode; ++counter){

        free(trie->transition[counter]);

    }

    free(trie->transition);
    free(trie);
}

int main(int argc, char *argv[]){

    Trie trie = createTrie(MAX_SIZE);

    free_all(trie);
    return (0);
}

Valgrind output:

==3079== Memcheck, a memory error detector
==3079== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3079== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3079== Command: ./debug_test
==3079== 
size of trie: 8, size of the struct: 8, size of _trie: 24
maxNode = 20, size of maxNode: 4
size of nextNode : 4, size of transition: 8, size of fin: 8
==3079== Invalid read of size 8
==3079==    at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
transitions points to: (nil), address: 0x5201048
==3079== Invalid read of size 8
==3079==    at 0x1088D1: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201050 is 8 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
fin points to: (nil), address: 0x5201050

==3079== Invalid write of size 8
==3079==    at 0x108907: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
trie->transition done.
==3079== Invalid read of size 8
==3079==    at 0x108923: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
transitions points to: 0x5201910, address: 0x5201048
==3079== Invalid read of size 8
==3079==    at 0x10893F: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
==3079== Invalid read of size 8
==3079==    at 0x108962: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
Size of transition: 8, size of int:4, pointer: 0x5201910

==3079== Invalid read of size 8
==3079==    at 0x108991: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
==3079== Invalid read of size 8
==3079==    at 0x1089B9: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 

Filling up trie->transition
==3079== Invalid read of size 8
==3079==    at 0x108A20: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
==3079== Invalid read of size 8
==3079==    at 0x108A84: free_all (in /projects/trie/debug_test)
==3079==    by 0x108AF8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
==3079== Invalid read of size 8
==3079==    at 0x108AB3: free_all (in /projects/trie/debug_test)
==3079==    by 0x108AF8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079== 
==3079== 
==3079== HEAP SUMMARY:
==3079==     in use at exit: 0 bytes in 0 blocks
==3079==   total heap usage: 24 allocs, 24 frees, 22,616 bytes allocated
==3079== 
==3079== All heap blocks were freed -- no leaks are possible
==3079== 
==3079== For counts of detected and suppressed errors, rerun with: -v
==3079== ERROR SUMMARY: 5167 errors from 11 contexts (suppressed: 0 from 0)

1条回答
趁早两清
2楼-- · 2019-08-24 07:07
==3079== Invalid read of size 8
==3079==    at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)
==3079==  Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079==    at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079==    by 0x108835: createTrie (in /projects/trie/debug_test)
==3079==    by 0x108AE8: main (in /projects/trie/debug_test)

This says your code is trying to read an 8-byte value from an invalid address.

This address is just after an 8 byte block allocated via malloc in createTrie. In other words, this line:

    trie = (Trie) malloc(sizeof(Trie));

Why does it think trie points to only 8 bytes of memory? Because you allocated sizeof (Trie) bytes, and Trie is

typedef struct _trie * Trie;

Or on other words, you allocated memory for a pointer when you meant to allocate enough memory for a whole struct.

It is strongly recommended not to hide pointers behind typedefs, for exactly this reason.

Suggested fix:

typedef struct Trie Trie;
struct Trie {
    int maxNode;
    int nextNode;
    int** transition;
    char* fin;
};

Trie *createTrie(int maxNode) {

    Trie *trie;

    trie = malloc(sizeof *trie);

Notes:

  • We use the same name (Trie) for both struct Trie and (bare) Trie because anything else is needlessly confusing.
  • All pointers are visibly declared as pointers, with *.
  • We don't cast the return value of malloc because that would be another potential source of errors.
  • We use sizeof *trie to get the correct number of bytes in the type trie is pointing to, no matter how trie is declared.
查看更多
登录 后发表回答