Search code examples
cparameterszshfilenames

zsh: bus error when passing results filename to compile C program


In the following C program, there are different input parameters. One of those parameters, -f is to especify the filename of the results

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define SOURCES 4
#define NUM_T 1000

char *prgname;
char usage[]="%s [-uUdipT] -f filename\n";

int main(int argc, char *argv[])
{
    FILE *filefd;
    char *fname;
    int  lt;            /* lower threshold */
    int  ut;            /* upper threshold */
    int  T;             /* Actualization time (cells) */
    double RDF, RIF;        /* Rate Decrease-Increate Factor */
    double PCR;         /* Peak Cell Rate */
    int qlen;           /* queue length */
    int bitEFCI[SOURCES];   /* EFCI bit */
    double ACR [SOURCES];   /* Allowed Cell Rate */
    double mean[SOURCES];   /* Mean Cell Rate */
    int i, t;           /* control variables */
    double ACRacc;

    prgname = strrchr(argv[0], '/');
    if (prgname == (char *)NULL) prgname = argv[0];
    else prgname++; 
    
    /********************************************/
    /* DEFAULT VALUES & INICIALIZATION          */
    /********************************************/
    
    fname = (char *)malloc(200*sizeof(char));
    fname = "abr.res";
    ut = 550;
    lt = 450;
    RDF = 0.4;
    RIF = 0.02;
    PCR = 150E6;
    T = 70;
    qlen = 0;

    for (i=0; i < SOURCES; i++)
    {bitEFCI[i] = 0; ACR[i] = 0.0; mean[i] = 0.0;}

    /********************************************/
    /* USER COMMAND LINE VALUES                 */
    /********************************************/
    
    while (1)
    {
    int c;

    c = getopt(argc, argv, "u:U:d:i:p:T:f:h");
    if (c == EOF) break;

    switch (c)
    {
        case 'h': printf(usage, prgname);
                fputs("-h\tHelp\n", stdout);
                fputs("-u\tLower threshold (cells)\n", stdout);
                fputs("-U\tUpper threshold (cells)\n", stdout);
                fputs("-d\tDecrement factor\n", stdout);
                fputs("-i\tIncrement factor\n", stdout);
                fputs("-p\tPeak cell rate (Mbps)\n", stdout);
                fputs("-T\tActualization period (cells)\n", stdout);
                fputs("-f\tResults file name\n", stdout);
                exit(0);
        case 'u': lt = atoi(optarg);
                break;
        case 'U': ut = atoi(optarg);
                break;
        case 'd': RDF = atof(optarg);
                break;
        case 'i': RIF = atof(optarg);
                break;
        case 'p': PCR = atof(optarg);
                break;
        case 'T': T = atoi(optarg);
                break;
        case 'f': strcpy(fname, optarg);
                break;
        default : fprintf(stderr, usage, prgname); exit(1);
    }
    }
    
    if ((filefd = fopen(fname, "w")) == NULL)
    {perror("fopen"); exit(1);}

    /********************************************/
    /* MAIN LOOP                                */
    /********************************************/
    
    for (t = 1; t<= NUM_T; t++)
    {
    ACRacc = 0.0;
    fprintf(filefd, "%d\t", t);
    for (i=0; i < SOURCES; i++)
    {
        if (bitEFCI[i] == 0)
             ACR[i] = (ACR[i]+PCR*RIF>PCR)?(PCR):(ACR[i]+PCR*RIF);
        else ACR[i] *= RDF;

        mean[i] += (ACR[i]-mean[i])/(double)t;
        ACRacc  += ACR[i];
        fprintf(filefd, "%4.1f\t%4.1f\t", ACR[i]/1E6, mean[i]/1E6);
    }
        
    qlen += (int)(ACRacc*T*424/PCR);
    qlen  = (qlen<=T*424)?(0):(qlen - T*424);
    fprintf(filefd, "%d\n", qlen/424);
        
    for (i=0; i < SOURCES; i++)
    {
        if (qlen >= ut*424) bitEFCI[i] = 1;
        else if (qlen < lt*424) bitEFCI[i] = 0;
    }
    fflush(filefd);
    }   

    fclose(filefd);
    exit(0);
}

I have compiled with

gcc -c abr1.c -Wall
gcc abr1.o -o abr1

but when I run the program

./abr1 -f "result01.res"
zsh: bus error  ./abr1 -f "result01.res"

How I can pass the filename to this C program?


Solution

  • This is a problem:

    fname = (char *)malloc(200*sizeof(char));
    fname = "abr.res";
    

    You malloc space that fname points at, then in the next line tell fname to point at the string literal "abr.res". That creates a memory leak, since you now have nothing pointing to your malloced memory.

    Furthermore, with the f option, you use strcpy to write to fname. Modifying a string literal is Undefined Behavior, and could certainly explain the behavior you're seeing.

    Instead, I recommend

    char fname[200] = "abr.res";
    

    This creates fname as a writable memory area of 200 bytes long, initializes it with "abr.res" as a default value, and doesn't deal with manual memory management.

    Per @Laci 's comment, you're vulnerable to a buffer overflow blindly copying user input from the command line to your buffer. Steps should be taken to eliminate that risk. One way is to use strncpy:

    case 'f':
      strncpy(fname, optarg, sizeof fname);
      // one of the dangers of strncpy is it is NOT guaranteed to NUL
      // terminate the string, so do that manually just in case
      fname[sizeof(fname)-1] = '\0';
      break;