web-dev-qa-db-fra.com

valgrind - L'adresse ---- est de 0 octet après un bloc de taille 8 alloué

Tout d'abord, je sais similaire des questions ont été posées. Cependant, j'aimerais avoir une question simple plus générale avec les types de données C vraiment primitifs. Alors voilà.

Dans main.c J'appelle une fonction pour remplir ces chaînes:

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

    char *Host = NULL ;
    char *database ;
    char *collection_name;
    char *filename = ""; 
    char *fields = NULL;
    char *query = NULL;
    ...

    get_options(argc, argv, &Host, &database, &collection_name, &filename, 
                &fields, &query, &aggregation);

À l'intérieur get_options:

if (*filename == NULL ) {
   *filename = (char*)realloc(*filename, strlen(*collection_name)*sizeof(char)+4);
    strcpy(*filename, *collection_name);
    strcat(*filename, ".tde");  # line 69 
}

Mon programme fonctionne bien, mais Valgrind me dit que je le fais mal:

==8608== Memcheck, a memory error detector
==8608== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==8608== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==8608== Command: ./coll2tde -h localhost -d test -c test
==8608== 
==8608== Invalid write of size 1
==8608==    at 0x403BE2: get_options (coll2tde.c:69)
==8608==    by 0x402213: main (coll2tde.c:92)
==8608==  Address 0xa2edd18 is 0 bytes after a block of size 8 alloc'd
==8608==    at 0x4C28BED: malloc (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==8608==    by 0x4C28D6F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==8608==    by 0x403BBC: get_options (coll2tde.c:67)
==8608==    by 0x402213: main (coll2tde.c:92)

Pouvez-vous expliquer l'erreur Address 0xa2edd18 is 0 bytes after a block of size 8 alloc'd? Comment puis-je résoudre ce problème?

22
Oz123

strcpy ajoute un caractère de terminaison nul '\0'. Vous avez oublié de lui allouer de l'espace:

*filename = (char*)realloc(*filename, strlen(*collection_name)*sizeof(char)+5);

Vous devez ajouter de l'espace pour 5 caractères: 4 pour ".tde" suffixe, et un de plus pour le '\0' terminateur. Votre code actuel n'en alloue que 4, donc la dernière écriture est effectuée dans l'espace immédiatement après le bloc que vous avez alloué pour le nouveau nom de fichier (c'est-à-dire 0 octet après).

Remarque: Votre code a un problème commun - il assigne les résultats de realloc directement à un pointeur en cours de réallocation. C'est très bien lorsque realloc réussit, mais crée une fuite de mémoire en cas d'échec. La correction de cette erreur nécessite de stocker le résultat de realloc dans une variable distincte et de le vérifier pour NULL avant de réaffecter la valeur à *filename:

char *tmp = (char*)realloc(*filename, strlen(*collection_name)*sizeof(char)+5);
if (tmp != NULL) {
    *filename = tmp;
} else {
    // Do something about the failed allocation
}

Affectation directe à *filename crée une fuite de mémoire, car le pointeur *filename indiqué ci-dessous serait écrasé en cas d'échec et deviendrait irrécupérable.

37
dasblinkenlight