Analyser valgrind de sortie: non valide “free()”

J'ai cette étrange erreur trouvée par valgrind sur un (stupide) module d'authentification qui fait de certains sur les allocations de tas.

 ==8009== Invalid free() / delete / delete[] / realloc()
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x40263F: authenticate (server_utils.c:109)
==8009==    by 0x401A27: main (server.c:240)
==8009==  Address 0x51f1310 is 0 bytes inside a block of size 18 free'd
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x402633: authenticate (server_utils.c:108)
==8009==    by 0x401A27: main (server.c:240)
=8009== 
==8009== Invalid free() / delete / delete[] / realloc()
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x40264B: authenticate (server_utils.c:110)
==8009==    by 0x401A27: main (server.c:240)
==8009==  Address 0x51f1319 is 9 bytes inside a block of size 18 free'd
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x402633: authenticate (server_utils.c:108)
==8009==    by 0x401A27: main (server.c:240)
==8009== 
==8009== Invalid free() / delete / delete[] / realloc()
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x402657: authenticate (server_utils.c:111)
==8009==    by 0x401A27: main (server.c:240)
==8009==  Address 0x51f131e is 14 bytes inside a block of size 18 free'd
==8009==    at 0x4C2A739: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8009==    by 0x402633: authenticate (server_utils.c:108)
==8009==    by 0x401A27: main (server.c:240)
==8009== ...

De ce que je comprends, dit-il, je n'trois invalide free() sur les lignes 109-110-111. L'erreur doit être que j'essaie de libérer plus d'espace que ce qui est effectivement alloués, mais je ne peux pas décider de combien d'espace désallouer. Aussi je ne comprends pas pourquoi il se réfère à la ligne 108 (qui est aussi un free()).

C'est à partir de la documentation (non Valide gratuit):

Memcheck conserve la trace des blocs attribués par votre programme avec malloc/nouvelles, de sorte qu'il peut savoir exactement si oui ou non l'argument de free/delete est légitime ou pas. Ici, ce programme de test a libéré le même bloc deux fois. Comme avec le caractère illégal des erreurs de lecture/écriture, Memcheck tente de donner un sens à l'adresse libéré. Si, comme ici, l'adresse est celle qui a déjà été libéré, vous wil être dit que -- double libère de la même bloc facile à repérer.

Vous permettra également de recevoir ce message si vous essayez de libérer un pointeur qui ne pointe pas le début d'un bloc de segment.

Je ne peux pas vraiment imaginer comment être dans un de ces cas.

/* Authentication through <user_id:password:flag>
* Returns 1 on success, -1 if user_id doesn't exist, -2 on password mismatch 
* On success sets access_permissions
*/
int authenticate(USR_PSW *received, int *access_permissions) {
/*Users file opening*/
FILE *fd;
fd = fopen(USERS_FILE, "r");
if (fd == NULL) {
fprintf(stderr, "Users file opening error\n");
exit(EXIT_FAILURE);
}
char *usr_psw_line = malloc(USR_SIZE + PSW_SIZE + 3 + 1);
if (usr_psw_line == NULL) {
fprintf(stderr, "Dynamic alloc error\n");
exit(EXIT_FAILURE);
}
char *usr_tok = malloc(USR_SIZE);
if (usr_tok == NULL) {
free(usr_psw_line);
fprintf(stderr, "Dynamic alloc error\n");
exit(EXIT_FAILURE);
}
char *psw_tok = malloc(PSW_SIZE);
if (psw_tok == NULL) {
free(usr_psw_line);
free(usr_tok);
fprintf(stderr, "Dynamic alloc error\n");
exit(EXIT_FAILURE);
}
char *flg_tok = malloc(sizeof (char) *2);
if (flg_tok == NULL) {
free(usr_psw_line);
free(usr_tok);
free(psw_tok);
fprintf(stderr, "Dynamic alloc error\n");
exit(EXIT_FAILURE);
}
/*Reading from file <user_id:password:flag> */
while (fgets(usr_psw_line, USR_SIZE - 1 + PSW_SIZE - 1 + 3 + 1, fd) != NULL) {
usr_tok = strtok(usr_psw_line, ":");
if (strcmp(usr_tok, received->user_id) == 0) {
/*user_id found, password check*/
psw_tok = strtok(NULL, ":");
/*password match*/
if (strcmp(psw_tok, received->password) == 0) {
flg_tok = strtok(NULL, ":");
*access_permissions = atoi(flg_tok);
free(usr_psw_line); //108
free(usr_tok);//109
free(psw_tok);//110
free(flg_tok);//111
fclose(fd);
return AUTHENTICATED;
} else { //password unmatch
free(usr_psw_line);
free(usr_tok);
free(psw_tok);
free(flg_tok);
fclose(fd);
return INVALID_PSW;
}
} else {
fseek(fd, 1, SEEK_CUR);
continue;
}
}
/*EOF Reached without match*/
free(usr_psw_line);
free(usr_tok);
free(psw_tok);
free(flg_tok);
fclose(fd);
return INVALID_USR;
}

Le fichier d'en-tête:

#ifndef __SERVER_UTILS_H__
#define __SERVER_UTILS_H__
#define USR_SIZE 9
#define PSW_SIZE 5 
/*Authentication data structure definition*/
typedef struct{
char user_id[USR_SIZE]; //eg: user_123
char password[PSW_SIZE]; //eg: a1b2
} USR_PSW;
#define NM_MAX_SIZE 17
#define NR_MAX_SIZE 11
/*System record structure*/
typedef struct{
char first_name[NM_MAX_SIZE];
char last_name[NM_MAX_SIZE];
char number[NR_MAX_SIZE];
}TBOOK_RECORD;
/*Session status flags*/
#define NOT_AUTHENTICATED 0
#define AUTHENTICATED 1
#define INVALID_USR 2
#define INVALID_PSW 3
/*Persmissions flags*/
#define NO_PERM 0
#define READ_WRITE  1
#define O_WRITE  2
#define O_READ  3
/*Contains <user_id:password,flag> triplets stored in server */
#define USERS_FILE "users.txt" 
/*Contains all telbook records <first_name:last_name:phone_number>*/
#define RECORDS_FILE "records.txt"
/*Single records file line max length (before \n) */
#define RECFILE_LINE_MAX_LEN  2*NM_MAX_SIZE+NR_MAX_SIZE+3+1;
/*Client operation*/
#define NO_OP 0
#define SEARCH_BY_NAME 1
#define SEARCH_BY_NUMBER 2
#define INSERT_RECORD 3
/* 
* Returns number of bytes copied into buffer (excluding terminating null byte), 
* or 0 on EOF, or -1 on error.
* size_t : used for sizes of objects.
* ssize_t: used for a count of bytes or an error indication (-1).
*/
ssize_t readline(int fd, void *buffer, size_t n);
/* Authentication through <user_id:password:flag>
* Returns 1 on success, -1 if user_id doesn't exist, -2 on password mismatch 
* On success sets access_permissions
*/
int authenticate(USR_PSW *received, int *access_permissions);
int close_session(int sock_ds);
int search_by__(int op_code, TBOOK_RECORD *rc_rcvd, TBOOK_RECORD *rc_rspn
, FILE *fd, char *file_line);
int insert_record(TBOOK_RECORD *rc_rcvd);
#endif  /* __SERVER_UTILS_H__ */
Édité avec les numéros de ligne

OriginalL'auteur Fabio Carello | 2013-03-25