Анализ вывода valgrind: неверный free()

У меня есть эта странная ошибка, обнаруженная valgrind в (глупом) модуле аутентификации, который делает некоторые выделения в куче.

 ==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== ...

Насколько я понимаю, он говорит, что я делаю три недопустимых free() в строках 109-110-111. Ошибка должна заключаться в том, что я пытаюсь освободить больше места, чем на самом деле выделено, но не могу решить, сколько места освободить. Также я не понимаю, почему тогда он ссылается на строку 108 (которая также является бесплатной()).

Это из документации (недействительно бесплатно):

Memcheck отслеживает блоки, выделенные вашей программой с помощью malloc/new, поэтому он может точно знать, является ли аргумент для освобождения/удаления законным или нет. Здесь эта тестовая программа дважды освобождала один и тот же блок. Как и в случае недопустимых ошибок чтения/записи, Memcheck пытается понять освобожденный адрес. Если, как здесь, адрес является тем, который ранее был освобожден, вам будет сообщено об этом -- что позволяет легко обнаруживать повторяющиеся освобождения одного и того же блока.

Вы также получите это сообщение, если попытаетесь освободить указатель, который не указывает на начало блока кучи.

Я реально не представляю, как быть в одном из таких случаев.

/* 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;

}

Заголовочный файл:

#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__ */

person Fabio Carello    schedule 25.03.2013    source источник
comment
Отредактировано с номерами строк   -  person Fabio Carello    schedule 25.03.2013


Ответы (3)


usr_tok = strtok(usr_psw_line, ":");
...
    psw_tok = strtok(NULL, ":");

Вы перезаписываете свой указатель на свою память malloced (тем самым пропуская ее) указателем в другом месте. Затем вы пытаетесь free получить этот указатель из другого места, который (как говорит valgrind) недействителен.

person Kevin    schedule 25.03.2013

Чтобы добавить к приведенному выше ответу, просто убедитесь, что вы используете fclose (fp) во всех случаях, когда у вас есть оператор возврата.

person Pradheep    schedule 25.03.2013

Когда вы используете strok(), вы меняете адрес указателя. Таким образом, вы можете не указывать на весь блок при вызове free(). Вы можете использовать вспомогательные указатели для хранения возвращаемых значений strok(), а затем вы можете использовать исходные указатели для free() всего выделенного пространства.

person FELIPE_RIBAS    schedule 25.03.2013
comment
Что вы подразумеваете под первым указателем? Что вам нужно сделать, так это создать новый указатель для хранения адреса возврата strok() вместо того, чтобы перезаписывать переменные указателя, которые вы использовали при вызове malloc(). Другими словами, вы всегда должны сохранять адрес, возвращаемый malloc(), чтобы затем вы могли использовать free() без проблем. Когда вы вызываете strok() и назначаете тот же указатель, который вы использовали с malloc, он перезапишет этот адрес и может указывать на середину этого пробела. Вот почему вы получили другую космическую ошибку при освобождении. - person FELIPE_RIBAS; 26.03.2013
comment
Поэтому вместо того, чтобы перезаписывать этот адрес, вы всегда должны создавать новый указатель и сохранять распределенный адрес, чтобы вы могли использовать free(). - person FELIPE_RIBAS; 26.03.2013