cclangncursescurses

How can i find memory leaks in my program?


Im building a terminal-based file viewer project with ncurses.h and C. Its only a side project as a hobby, and has a few lines of code. I get some segmentation errors and I don’t even have a clue where they are, if someone can check the code, I will appreciate a lot.

#include "window.h"
#include <dirent.h>
#include <menu.h>
#include <ncurses.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static DIR *get_dir(const char *path)
{
    DIR *dir;
    if (strcmp(path, "current") == 0)
        dir = opendir(".");
    else
        dir = opendir(path);
    if (dir == NULL)
    {
        perror("Error opening dir");
        exit(-1);
    }
    return dir;
}

WINDOW *init_window(int start_y, int start_x, int y, int x)
{
    WINDOW *win;

    initscr();
    getmaxyx(stdscr, y, x);
    cbreak();
    noecho();

    if (x < WDIM_X || y < WDIM_Y)
    {
        start_x = (x - WDIM_X_MIN);
        start_y = (y - WDIM_Y_MIN);
        win = newwin(WDIM_Y_MIN, WDIM_X_MIN, 1, 2);
    }
    else {
        start_x = (x - WDIM_X) / 2;
        start_y = (y - WDIM_Y) / 2;
        win = newwin(WDIM_Y, WDIM_X, start_y, start_x);
    }

    box(win, 0, 0);
    start_color();
    curs_set(0);

    keypad(win, true);
    init_pair(1, COLOR_BLACK, COLOR_BLUE);
    wbkgd(win, COLOR_PAIR(1));
    wrefresh(win);
    return win;
}

void delete_window(WINDOW *win)
{
    delwin(win);
    endwin();
}

char *get_current_dir()
{
    /*
    char *dirs = malloc(MAX_LENGTH * sizeof(char));
    if (getcwd(dirs, sizeof(char) * MAX_LENGTH) != NULL)
        return dirs;
    return "Error";
    */
    char *cwd = getcwd(NULL, 0);
    return cwd;
}

char **dir_items(DIR *dir)
{

    char **current_dirs = (char **)malloc(MAX_LENGTH * sizeof(char *));
    struct dirent *entry;
    int i = 0;
    while ((entry = readdir(dir)) != NULL)
    {
        char *name = entry->d_name;
        int j=i;
        while (j > 0 && strcmp(name, current_dirs[j-1]) < 0)
        {
            current_dirs[j] = current_dirs[j-1];
            j--;
        }
        current_dirs[j] = name;
        i++;
    }
    return current_dirs;
}

void cd(const char *path)
{
    if (chdir(path) == -1)
    {
        perror("Error");
    }
}

void ls(const char *path)
{
    DIR *dir = get_dir(path);
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL)
        printf("%s | %s\n", path, entry->d_name);
    closedir(dir);
}

static int n_elements(char **items)
{
    int i = 0;
    while (items[i] != NULL)
    {
        i++;
    }
    return i;
}

/* static void moveMenu(MENU *menu, WINDOW *win)
{
    char c;
    WINDOW *w;
    while ((c = wgetch(win)) != '1')
    {
        switch (c)
        {
        case 'j':
            menu_driver(menu, REQ_DOWN_ITEM);
            wrefresh(win);
            break;
        case 'k':
            menu_driver(menu, REQ_UP_ITEM);
            wrefresh(win);
            break;
        case 'q':
            delete_window(win);
            exit(-1);
            break;
        case '\n':
            delete_window(win);
            win = init_window(WDIM_Y, WDIM_X, 5, 5);
            cd("/");
            sleep(3);
            render_dir(win);
            break;
        }
    }
} */

void render_dir(WINDOW *win)
{
    char **items_chars, *titulo, c;
    int i=0, x, y;
    int n;
    DIR *dir;
    ITEM **items;
    MENU *menu;
    char *path = get_current_dir();
    path = strcat(path, "/");
    dir = get_dir(path);
    items_chars = dir_items(dir);

    wclear(win);
    titulo = malloc(MAX_LENGTH * sizeof(char));
    strcpy(titulo, get_current_dir(path));
    mvwprintw(win, 0, 1, titulo);
    wrefresh(win);
    n = n_elements(items_chars);
    items = (ITEM **)calloc(n + 1, sizeof(ITEM *));

    for (int j = 0; j < n; j++)
    {
        items[j] = new_item(items_chars[j], "");
    }
    // final have to be NULL
    items[n] = (ITEM *)NULL;

    menu = new_menu(items);
    set_menu_win(menu, win);
    set_menu_sub(menu, derwin(win, 25, 40, 2, 1));
    // set_menu_fore(menu, A_NORMAL);
    set_menu_format(menu, 25, 1);
    set_menu_back(menu, COLOR_PAIR(1));
    post_menu(menu);
    wrefresh(win);
    while ((c = wgetch(win)) != '1')
    {
        switch (c)
        {
        case 'j':
            menu_driver(menu, REQ_DOWN_ITEM);
            if (i > n)
                i = n;
            else
                i++;
            //i++;
            wrefresh(win);
            break;
        case 'k':
            menu_driver(menu, REQ_UP_ITEM);
            if (i < 0)
                i = 0;
            else
                i--;
            //i--;
            wrefresh(win);
            break;
            break;
        case 'q':
            delete_window(win);
            exit(-1);
            break;
        case '\n':
            for (int j = 0; j < n; j++)
            {
                free_item(items[j]);
            }
            free_menu(menu);
            path = strcat(path, items_chars[i]);
            free(items_chars);
            free(titulo);
            closedir(dir);
            wclear(win);
            delwin(win);
            getmaxyx(stdscr, y, x);
            if (x < WDIM_X || y < WDIM_Y)
                win = init_window(WDIM_Y_MIN, WDIM_X_MIN, 5, 5);
            else
                win = init_window(WDIM_Y, WDIM_X, 5, 5);
            cd(path);
            free(path);
            mvwprintw(win, 20, 1, "s");
            render_dir(win);
            break;
        }
    }

    wrefresh(win);
}


Here is main

#include "window.h"

int main(int argc, char **argv)
{
    WINDOW *win;
    win = init_window(WDIM_Y, WDIM_X, 5, 5);
    render_dir(win);
    delete_window(win);
    return 0;
}

I have tried to free all the memory in all ways but it still has segmentation faults and memory leaks.


Solution

  • The problem (or a problem, there may be others) is that you are doing this:

    char *path = get_current_dir();
    path = strcat(path, "/");
    

    get_current_dir() does this:

    char *cwd = getcwd(NULL, 0);
    return cwd;
    

    And getcwd() does this, according to the man page

    As an extension to the POSIX.1-2001 standard, glibc's getcwd() allocates the buffer dynamically using malloc(3) if buf is NULL. In this case, the allocated buffer has the length size unless size is zero, when buf is allocated as big as necessary. The caller should free(3) the returned buffer.

    So the buffer returned is exactly the size of the path. There is no space to add a '/'.

    So this:

    path = strcat(path, "/");
    

    is causing a buffer overflow, and undefined behaviour.