random seg fault

Hello,

I'm writing a small tool in C to extract comments from Z80 assembler sources. I get random segmentation fault, generaly after one successful execution of the binary. lldb shows always the same line involved : 121 at calloc(...).

ndx[i]->text = (char *) calloc(tsz, sizeof(char));

Thanks for your help.

// AX
// my Z80 assembler API extractor
//
// T.Peycru, 2023

// NOTE:
// -----
//
// API extractor will extract all the comment lines following ;;; 
//
// The string following the trigger sequence of LF+';'+';'+';'
// is the sorting label of the paragraph of comments
//
// All the files in the current directory and its childhood are scanned
// Output is sorted on the label used to build the TOC (unimplemented)
//
// Output format is Markdown (unimplemented)
//

#define _BSD_SOURCE // required to make this program work on Linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for getcwd()

#include <sys/errno.h> // lets us directly access errno

// includes recommended by "man fts":
#include <sys/types.h>
#include <sys/stat.h>
#include <fts.h>

// max constants
#define C_MAX 4096      // buffer for fgets (one line from asm source file)
#define I_MAX 1000      // number of entries
#define N_MAX 4096      // buffer for entry name
#define T_MAX 32768     // buffer for entry text 

// entry counter
int i = 0;

// buffers

// entry structure
struct entry
{
    char *name;
    char *text;
};

// array of pointers to entries
struct entry *ndx[I_MAX];

//
// function to extract API comments in a given filename
// ----------------------------------------------------
//
// trigger is a CRLF+";;;" followed by entry name (to be sorted in a TOC)
// next lines with comment (beginning with ';') are entry text
// any other line ends the text
//
int ApiExtract(char *argv)
{
    // file to parse
    FILE *fp = fopen(argv, "r");
    if(fp == NULL) return 0;

    // state : 0=detect mode, 1=scanning mode
    int scanning = 0;

    // sizes
    int nsz = 0;
    int tsz = 0;

    // buffers
    char chunk[C_MAX];
    char nbuf[N_MAX];
    char tbuf[T_MAX];

    // reset buffers
    memset(chunk, 0, C_MAX);
    memset(nbuf,  0, N_MAX);
    memset(tbuf,  0, T_MAX);

    while(fgets(chunk, C_MAX-1, fp) != NULL)
    {
        if (!scanning)
        {
            // detect mode, look for trigger
            // ;;; trigger sequence for API name
            if (chunk[0] == ';' && chunk[1] == ';' && chunk[2] == ';')
            {
                // trigger found, switch to scanning mode
                scanning = 1;
                // store name (for TOC and entry sorting)
                if (chunk[3]) strncpy(nbuf, &chunk[3], N_MAX-4);
            } 
        }
        else
        {
            // scanning mode
            if (chunk[0] == ';')
            {
                // append line of text
                strncat( tbuf, &chunk[1], T_MAX-strlen(tbuf)-1);
            } 
            else
            {
                // text done, store name and text in a new entry
                ndx[i] = (struct entry *) calloc(1, sizeof(struct entry));

                // set allocation sizes
                if (!(nsz = strlen(nbuf)+1)) return -1;
                if (!(tsz = strlen(tbuf)+1)) return -1;

                // allocate and store name
                ndx[i]->name = (char *) calloc(nsz, sizeof(char));
                strncpy( ndx[i]->name, nbuf, N_MAX-1);

                // allocate and store text
                ndx[i]->text = (char *) calloc(tsz, sizeof(char));
                strncpy(ndx[i]->text, tbuf, T_MAX-1);

                // clear buffers for next iteration
                memset(nbuf, 0, N_MAX);
                memset(tbuf, 0, T_MAX);

                // next entry
                i++;
                if (i>I_MAX) return -1;

                // switch to detect mode
                scanning = 0;
            }
        }
    }
    fclose(fp);
    return 0;
}


//
// function to process entries and output API
// ------------------------------------------
//
int ApiOutput(void)
{
    int l = 0;

    // for now, just output to stdout
    // TBD : sorting, TOC building, MD format enlighting
    while(l<i)
    {
        printf("%s",ndx[l]->name);
        printf("%s",ndx[l]->text);
        free(ndx[l]->name);
        free(ndx[l]->text);
        free(ndx[l]);
        ndx[l] = 0;
        l++;
    }
    return 0;
}


//
// main parse current file tree using FTS
// --------------------------------------
//
int main(void)
{
    /* An array of paths to traverse. Each path must be null
     * terminated and the list must end with a NULL pointer. */
    char *paths[] = { ".", NULL };
    
    /* 2nd parameter: An options parameter. Must include either
       FTS_PHYSICAL or FTS_LOGICAL---they change how symbolic links
       are handled.
       The 2nd parameter can also include the FTS_NOCHDIR bit (with a
       bitwise OR) which causes fts to skip changing into other
       directories. I.e., fts will call chdir() to literally cause
       your program to behave as if it is running into another
       directory until it exits that directory. See "man fts" for more
       information.
       Last parameter is a comparator which you can optionally provide
       to change the traversal of the filesystem hierarchy.
    */
    FTS *ftsp = fts_open(paths, FTS_PHYSICAL, NULL);
    if(ftsp == NULL)
    {
        perror("fts_open");
        exit(EXIT_FAILURE);
    }

    while(1) // call fts_read() enough times to get each file
    {
        FTSENT *ent = fts_read(ftsp); // get next entry (could be file or directory).
        if(ent == NULL)
        {
            if(errno == 0)
                break; // No more items, bail out of while loop
            else
            {
                // fts_read() had an error.
                perror("fts_read");
                exit(EXIT_FAILURE);
            }
        }
            
        // Given a "entry", call extractor if it is a file
        if (ent->fts_info & FTS_F) // The entry is a file.
        {
            if (ApiExtract(ent->fts_path))
            {
                // extraction error
                perror("apix");
                exit(EXIT_FAILURE);
            }
        }
    }

    // output
    ApiOutput();

    // close fts and check for error closing.
    if(fts_close(ftsp) == -1)
        perror("fts_close");
    return 0;
}
random seg fault
 
 
Q