Hayes Pan
Hayes Pan

Reputation: 615

"free(): invalid next size (fast)" error in malloc

I have a problem in malloc/free. I want to implement a simple 'ls' to show unhidden files in a dir.

Here is my code:

void do_ls(char *dirname, _Bool ls_list) {
    DIR *dir_ptr;
    const char * d_name;
    struct dirent *direntp, ** dirarray;
    int fcnt, d_name_max;
    if((dir_ptr = opendir(dirname))==NULL) {
        fprintf(stderr,"ls: cannot open %s\n", dirname);
        exit(-1);
    }
    else {
        fcnt = 0; // count unhidden files
        d_name_max = -1; // max length of filename
        while((direntp = readdir(dir_ptr))!=NULL) {
            d_name = direntp->d_name;
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                fcnt++;
                if((int)strlen(d_name)>d_name_max)
                    d_name_max = strlen(d_name);
            }
        }
        // use a array to store dirent printer
        dirarray = (struct dirent**)malloc(sizeof(struct dirent *)*fcnt);
        // reset read position
        seekdir(dir_ptr, 0);
        int i = 0;
        while((direntp=readdir(dir_ptr))!=NULL) {
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                dirarray[i++] = direntp; // save pointer
            }
        }
        qsort(dirarray, fcnt, sizeof(struct dirent*), alphacmp); // qsort for lexicographical ordering
        for(int i=0;i<fcnt;i++)
            fprintf(stdout, "%s\n", dirarray[i]->d_name); // print result
        if(dirarray!=NULL)
            free(dirarray); // free if not NULL
        closedir(dir_ptr);
    }
}

It's confusing to me, thatit works or crashes in some cases. And I guess there must be some wrongs in do_ls but still cannot find out.

Files under my dir are:

`ls -a ~/file/c/`
./  ../  1*  a.c  db/  mpi/  test1.c  .test1.c.swo  .test1.c.swp  unix/  .ycm_extra_conf.py  .ycm_extra_conf.pyc    

But when run ./my-own-ls ~/file/c/, I'm getting these errors:

/home/panhzh3/file/c:
..
.test1.c.swo
.test1.c.swp
.ycm_extra_conf.py
.ycm_extra_conf.pyc
1
mpi
unix
*** glibc detected *** ./ls: free(): invalid next size (fast): 0x08d9b028 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75b12)[0xb7664b12]
./ls[0x80488e5]
./ls[0x8048a6f]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb76084d3]
./ls[0x8048621]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
08049000-0804a000 r--p 00000000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
0804a000-0804b000 rw-p 00001000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
08d93000-08dbc000 rw-p 00000000 00:00 0          [heap]
b75b4000-b75d0000 r-xp 00000000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75d0000-b75d1000 r--p 0001b000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75d1000-b75d2000 rw-p 0001c000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75ee000-b75ef000 rw-p 00000000 00:00 0 
b75ef000-b7793000 r-xp 00000000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7793000-b7795000 r--p 001a4000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7795000-b7796000 rw-p 001a6000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7796000-b7799000 rw-p 00000000 00:00 0 
b77b3000-b77b7000 rw-p 00000000 00:00 0 
b77b7000-b77b8000 r-xp 00000000 00:00 0          [vdso]
b77b8000-b77d8000 r-xp 00000000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
b77d8000-b77d9000 r--p 0001f000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
b77d9000-b77da000 rw-p 00020000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
bff21000-bff43000 rw-p 00000000 00:00 0          [stack]
[2]    32254 abort (core dumped)  ./ls ~/file/c

Source codes:

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>

int alphacmp(const void * a, const void * b) {
    struct dirent ** ta = (struct dirent**)a;
    struct dirent ** tb = (struct dirent**)b;
    return strcmp((*ta)->d_name, (*tb)->d_name);
}

void do_ls(char *dirname, _Bool ls_list) {
    DIR *dir_ptr;
    const char * d_name;
    struct dirent *direntp, ** dirarray;
    int fcnt, d_name_max;
    if((dir_ptr = opendir(dirname))==NULL) {
        fprintf(stderr,"ls2: cannot open %s\n", dirname);
        exit(-1);
    }
    else {
        fcnt = 0;
        d_name_max = -1;
        while((direntp = readdir(dir_ptr))!=NULL) {
            d_name = direntp->d_name;
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                fcnt++;
                if((int)strlen(d_name)>d_name_max)
                    d_name_max = strlen(d_name);
            }
        }
        dirarray = (struct dirent**)malloc(sizeof(struct dirent *)*fcnt);
        seekdir(dir_ptr, 0);
        int i = 0;
        while((direntp=readdir(dir_ptr))!=NULL) {
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                dirarray[i++] = direntp;
            }
        }
        qsort(dirarray, fcnt, sizeof(struct dirent*), alphacmp);
        for(int i=0;i<fcnt;i++)
            fprintf(stdout, "%s\n", dirarray[i]->d_name);
        if(dirarray!=NULL)
            free(dirarray);
        closedir(dir_ptr);
    }
}

int main(int argc, char **argv) {
    const char * const short_options = "l";
    const struct option long_options[] = {
       {"list", 0, NULL, 'l'},
       {NULL, 0, NULL, 0},
    };
    const char * program_name = argv[0];

    opterr = 0;

    int next_option;
    _Bool ls_list = false;

    do {
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
        switch(next_option) {
            case 'l':
                ls_list = true;
                break; 
            case '?':
                fprintf(stderr, "Invalid option: -%c", optopt);
                exit(-1);
                break;
            case ':':
                fprintf(stderr, "Option -%c needs argument.", optopt);
            case -1:
                break;
            default:
                abort();
        }
    } while(next_option!=-1);

    if(optind == argc)
        do_ls(".", ls_list);
    else
        for(;optind<argc;optind++) {
            printf("%s:\n", argv[optind]);
            do_ls(argv[optind], ls_list);
        }
    return 0;
}

Upvotes: 2

Views: 1226

Answers (1)

JS1
JS1

Reputation: 4767

In your second while loop, you are missing the line:

d_name = direntp->d_name;

One other thing you might need to know is that you can't depend on storing the dirent pointers in an array and sorting them. The man page for readdir says that the data may be overwritten by subsequent calls, so you might have to make copies of each entry and sort the copies instead.

Upvotes: 1

Related Questions