简体   繁体   中英

ncurses program crashes on anything other than PuTTY

I have an older ncurses-based program that does some simple IO on a few files (ie: setup program). However, from a terminal different from PuTTY, it crashes with SIGBUS

Program received signal SIGBUS, Bus error.
0x00000000004028b1 in fDisplay (ptr=Variable "ptr" is not available.
) at file_cpy.c:676
676             sprintf(p, " %-36s ", (*ptr)->datainfo.option);
(gdb) where
#0  0x00000000004028b1 in fDisplay (ptr=Variable "ptr" is not available.
) at file_cpy.c:676
#1  0x0000000000402cdb in fredraw (c=0x7fffffffe860) at file_cpy.c:696
#2  0x0000000000401996 in ls_dispatch (c=0x2020202020202020) at ds_cell.c:324
#3  0x0000000000403bf2 in main_dir (c=0x2020202020202020) at file_cpy.c:811
#4  0x0000000000403cb3 in main () at file_cpy.c:1345
(gdb) x/i $pc
0x4028b1 <fDisplay+17>: mov    (%rax),%rdx
(gdb)

This happens on Linux and FreeBSD, regardless of ncurses version and 32/64bit architecture. I'm completely stumped.

fDisplay() is called here:

/*
 * File redraw routine. Draws current list on screen.
 */
int fredraw (CELL * c)
{
        register int row = c->srow;
        dlistptr p = c->list_start;
        int i = 0;
        char buff[200];
        if (c->ecol - c->scol)
                sprintf(buff, "%*s",c->ecol - c->scol + 1, " ");
        while (i <= c->erow - c->srow && p != NULL)
        {
                if (p == c->current) wattron(c->window,A_REVERSE);
                        mvaddstr (row , c->scol, fDisplay(&p));
                if (p == c->current) wattroff(c->window,A_REVERSE);
                        row++; i++;
                p = p->nextlistptr;
        }
        if (row <= c -> erow)
                for (; row <= c -> erow ; row++)
                        mvaddstr(row, c->scol, buff);
        wrefresh(c->window);
        c -> redraw = FALSE;
        return TRUE;
}

fredraw() is called here:

int main_dir(CELL *c) {
int i;

        getcwd(current_path, sizeof(current_path));
        strcat(current_path, "/.config.h");
        load_file(current_path);

        c->keytable = file_cpy_menu;
        c->func_table = file_cpy_table;
        c->ListEntryProc = File_Entry;
        c->UpdateStatusProc = status_update;
        c->redraw = TRUE;
        c->ListExitProc = List_Exit;
        c->ListPaintProc = fredraw;

        c->srow = 3;
        c->scol = 1;
        c->erow = c->window->_maxy - 5;
        c->ecol = c->window->_maxx - 1;
        c->max_rows = c->window->_maxy;
        c->max_cols = c->window->_maxx;

        c->filename = "[ Config ]";
        c->menu_bar = 0;
        c->normcolor = 0x07;
        c->barcolor = 0x1f;
        init_dlist(c);
        for (i = 0; config_type[i].option; i++)
                insert_fdlist(c, &config_type[i]);


        /*
        * Go Do It
        */
        do {
                c->redraw = TRUE;
                ls_dispatch(c);
        } while (c->termkey != ESC && c->termkey != ALT_X);
        return TRUE;
}

Finally, main() calls the above functions:

int main() {
    CELL file_cpy = {0};
    WINDOW *mainwin;
    mainwin = initscr();
    start_color();
    setup_colors();
    cbreak();
    noecho();
    keypad(mainwin, TRUE);
    meta(mainwin, TRUE);
    raw();

    leaveok(mainwin, TRUE);
    wbkgd(mainwin, COLOR_PAIR(COLOR_MAIN));
    wattron(mainwin, COLOR_PAIR(COLOR_MAIN));
    werase(mainwin);
    refresh();

    file_cpy.window = mainwin;

    main_dir(&file_cpy);

    wbkgd(mainwin, A_NORMAL);
    werase(mainwin);
    echo();
    nocbreak();
    noraw();
    refresh();
    endwin();
    return TRUE;
}

Apparently you are calling main_dir and ls_dispatch with a pointer c initialized to 0x2020202020202020 . While not completely impossible, I find it very unlikely, and it looks to me as if you're overwriting the pointer with spaces.

valgrind would probably help, or some kind of memory or stack protection check.

Why this depends on the terminal, I could not say; possibly there's some TERM-depending code (such as "allocate as many rows as there are on screen"). Anyway, that is not the bug: it's only the condition bringing the bug in the open. This kind of error is usually caused by a hard-wired constant which sometimes is exceeded by the real value (in the example above I could say "allocate 96 rows", assuming that 96 rows will always be enough for everyone ; whereas a TERM with 100 rows would foil the assumption and lead to a buffer overrun).

It might also be an artifact of the debugger, seeing as how c is allocated on the stack (but I don't think so: it should be c=0x7fffsomething - at least on the systems where I checked) . Anyway, I'd repeat the test like this, by making file_cpy heap dynamic:

int main() {
    CELL *file_cpy = NULL;
    WINDOW *mainwin;

    file_cpy = malloc(sizeof(CELL));

    ...

    file_cpy->window = mainwin;

    main_dir(file_cpy);

    ...
    free(file_cpy); // file_cpy = NULL; // we immediately return
    return TRUE;

and then I'd try and tabulate the pointer's value throughout the main_dir function, in case it's overwritten.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM