-1

I am supposed to write a bot which reads web adresses from file and performs a GET request which is writen to a file again. This works so far, but the program always terminates with a "stack smashing error". I work with multiple "consumer" threads, which will do the job, and when all GET requests are done and the first threads finishes, the program terminates.

Below is my code:

#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "Socket/msocket.h"
/*
 * 
 */

#define MAXLINE 512

typedef struct {
    char** addr;
    long head, tail;
    int full, empty, reading;
    pthread_mutex_t *mut;
    pthread_cond_t *notFull, *notEmpty;
} queue;

typedef struct {
    struct queue *q;
    int tid;
} arguments;

int queueSize;
int elemCount=0;
int isEmpty;
char file[MAXLINE];

void *readFile(void *args);
void *consume(void *args);
char* parseLine(char*);
char** parseAddr(char*);
queue *queueInit (int);
void queueDelete (queue *q);
void queueAdd (queue *q, char* new);
void queueDel (queue *q, char** out, int *o);

/* main function
    argv[0]: program name (here ./bot)
    argv[1]: file name (xyz.txt,...)
    argv[2]: size of queue
    argv[3]: number of client/consumer threads
*/
int main(int argc, char** argv) {

    strcpy(file, argv[1]);
    queueSize = atoi(argv[2]);
    int maxCli = atoi(argv[3]);
    printf("-------queueSize: %i--------\n", queueSize);
    printf("---------maxCli: %i-------\n", maxCli);
    int j=0;
    queue *fifo;
    pthread_t prod, con[maxCli];
    struct timeval tv;

    fifo = queueInit(queueSize);
    if(fifo == NULL){
        printf("queueInit() failed");
        exit(1);
    }

    gettimeofday(&tv, NULL);
    double start = (tv.tv_sec) * 1000 + (tv.tv_usec)/1000;
    pthread_create(&prod, NULL, readFile, fifo);
    while(j<maxCli){

        arguments *threadSet = malloc(sizeof(arguments));
        threadSet->q = fifo;
        threadSet->tid = j+1;
        pthread_create(&con[j], NULL, consume, threadSet);
        j++;
    }
    j=0;
    pthread_join(prod, NULL);
    while(j<maxCli){
        pthread_join(con[j], NULL);
        j++;
    }
    double end = (tv.tv_sec) * 1000 + (tv.tv_usec)/1000;
    printf("time elapsed: %d\n", end-start);

    printf("----------------threads end----------------\n");
    queueDelete(fifo);

    return (EXIT_SUCCESS);
}

void *readFile(void *q){
    FILE *fp = fopen(file, "r");
    if(fp==NULL){
        printf("fopen() failed");
        return;
    }

    char tmp[MAXLINE];
    arguments *threadSet;
    queue *fifo;
    int k;

    fifo = (queue *)q;

    while(fgets(tmp, MAXLINE, fp) != NULL){
        pthread_mutex_lock(fifo->mut);
        if(fifo->full){
            printf("producer: queue FULL\n");
            pthread_cond_wait(fifo->notFull, fifo->mut);
        }
        strcpy(tmp, parseLine(tmp));
        queueAdd(fifo, tmp);
        elemCount++;
        printf("producer: added %s\n", tmp);
        printf("element count: %i\n", elemCount);

        pthread_mutex_unlock(fifo->mut);
        pthread_cond_signal(fifo->notEmpty);

        usleep(100000 + 100000);
    }
    fclose(fp);
    fifo->reading = 0;
    printf("--------------read end---------------\n");
    return(NULL);
}

void *consume(void *a){
    printf("consume begin\n");
    arguments *threadSet;
    queue *fifo;
    char* c;
    int elemNr;
    int retValue;

    threadSet = (arguments *)a;
    fifo = (queue *)threadSet->q;

    while(1){
        pthread_mutex_lock(fifo->mut);
        //printf("---------------consume begin--------------\n");
        if(fifo->empty && !fifo->reading){
            printf("end\n");
            break;
        }
        if(fifo->empty && fifo->reading){
            printf("consumer(%i): queue EMPTY\n", threadSet->tid);
            pthread_cond_wait(fifo->notEmpty, fifo->mut);
        }
        if(!fifo->empty){
            queueDel(fifo, &c, &elemNr);
            char fname_a[] = "file_";
            char* fname_b = malloc(MAXLINE);
            snprintf(fname_b, MAXLINE, "<%i>_<%i>.html", elemNr, threadSet->tid);
            strcat(fname_a, fname_b);
            printf("%s\n", fname_a);

            char** args;
            args = parseAddr(c);
            if( (retValue = askServer(args[0], args[1], fname_a)) < 0){
                printf("askServer() failed: %s\n", args[0]);
                printf("error value: %i\n", retValue);
                return(NULL);
            }
            elemCount--;
            printf("consumer(%i): picked %s\n", threadSet->tid, c);
            printf("---------------consume end--------------\n");
        }
        pthread_mutex_unlock(fifo->mut);
        pthread_cond_signal(fifo->notFull);
        usleep(200000 + 300000);
    }
    printf("end thread: consumer(%i)\n", threadSet->tid);
    free (threadSet);
    return(NULL);
}

char** parseAddr(char* c){
    char* args[2];

    char* next = strchr(c, '/');
    args[1] = malloc(sizeof(char)*MAXLINE);
    strcpy(args[1], next);

    next[0] = '\0';
    args[0] = malloc(sizeof(char)*MAXLINE);
    strcpy(args[0], c);

    return args;
}

char* parseLine(char* c){
    char* next = strchr(c, ' ');
    next[0] = '\0';

    char* t = next+1;
    next = strchr(t, '\n');
    if(next != NULL) next[0] = '\0';
    strcat(c, t);

    return c;
}

queue *queueInit (int size){
    queue *q;

    q = (queue *)malloc (sizeof (queue));
    if (q == NULL) return (NULL);

    q->addr = malloc(size);
    int i=0;
    while(i<size){
        q->addr[i] = malloc(sizeof(char)*MAXLINE);
        i++;
    }
    q->empty = 1;
    q->full = 0;
    q->reading = 1;
    q->head = 0;
    q->tail = 0;
    q->mut = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
    pthread_mutex_init (q->mut, NULL);
    q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
    pthread_cond_init (q->notFull, NULL);
    q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
    pthread_cond_init (q->notEmpty, NULL);

    return (q);
}

void queueDelete (queue *q){
    pthread_mutex_destroy (q->mut);
    free (q->mut);  
    pthread_cond_destroy (q->notFull);
    free (q->notFull);
    pthread_cond_destroy (q->notEmpty);
    free (q->notEmpty);

    int i=0;
    while(i<queueSize){
        free (q->addr[i]);
        i++;
    }
    free (q->addr);
    free (q);
}

void queueAdd (queue *q, char* new){
    q->addr[q->tail] = (char*)malloc(sizeof(char));
    strcpy(q->addr[q->tail], new);
    q->tail++;
    if (q->tail == queueSize)
        q->tail = 0;
    if (q->tail == q->head)
        q->full = 1;
    q->empty = 0;

    return;
}

void queueDel (queue *q, char **out, int *o){
    *out = q->addr[q->head];
    *o = q->head+1;

    q->head++;
    if (q->head == queueSize)
        q->head = 0;
    if (q->head == q->tail)
        q->empty = 1;
    q->full = 0;

    return;
 }

Error I am getting:

* stack smashing detected *: ./bot terminated make: *** [run] Aborted

This happens after the first thread finishes. I know that there must be a mistake in the memory but I dont understand why I get this error. Am I missing something here? Also any other tips are appreaciated!

Biruk Abebe
  • 2,235
  • 1
  • 13
  • 24
Peter L.
  • 3
  • 2
  • How about just using wget? – nwp May 22 '16 at 11:05
  • Why use pointers to the thread-related elements of the queue structure? Why not simply embed instances of those types directly in the structure? That, however, is just a source of memory leakage; there are others visible in the code. But memory leaks are not directly a cause of stack smashing — the issues are somewhat tangential to your main problem. – Jonathan Leffler May 22 '16 at 12:23
  • @nwp because we are supposed to do it like that. Our teacher gave us a class for the GET request he wrote and we have to sort of code around it. – Peter L. May 22 '16 at 13:57
  • @JonathanLeffler I am not really getting what you mean. – Peter L. May 22 '16 at 13:57
  • You have: `typedef struct { char** addr; long head, tail; int full, empty, reading; pthread_mutex_t *mut; pthread_cond_t *notFull, *notEmpty; } queue;` — but why not avoid some extra memory allocations by using: `typedef struct { char** addr; long head, tail; int full, empty, reading; pthread_mutex_t mut; pthread_cond_t notFull, notEmpty; } queue;` (where the change is three missing asterisks indicating pointers). – Jonathan Leffler May 22 '16 at 15:01

2 Answers2

1

I'm pretty sure you have a bug in parseAddr:

char** parseAddr(char* c){
    char* args[2];
    ...
    return args;
}

While args[0] and args[1] are dynamically allocated, args itself isn't. When you return it, a pointer to args will actually be allocated. Because the args array is destroyed after the function exits, attempting to access the returned value will yield undefined behaviour.

If you want to do this, pass in an array to be filled in as one of the arguments to the function, and get the function to fill it in instead. You could also dynamically allocate the array (e.g. char** args; args = malloc(sizeof(char*)*2);.

If this doesn't help, running it under valgrind may help to pin down your error.

slugonamission
  • 9,562
  • 1
  • 34
  • 41
  • Now I got it like this: `char* args[2];
    parseAddr(c, args);
    ` And in the function: `char* next = strchr(c, '/'); printf("<>> %s\n", next); args[1] = malloc(sizeof(char)*MAXLINE); strcpy(args[1], next); next[0] = '\0'; args[0] = malloc(sizeof(char)*MAXLINE); strcpy(args[0], c);` Still an error sadly, but thanks for the hint
    – Peter L. May 22 '16 at 14:08
0

There is probably a problem here also:

void queueAdd (queue *q, char* new){
    q->addr[q->tail] = (char*)malloc(sizeof(char));

should be changed to:

void queueAdd (queue *q, char* new){
    q->addr[q->tail] = (char*)malloc(strlen(new)+1);

Else, the malloc allocate only one byte before 'strlen(new)+1' bytes are transfered into the allocated area.

You should also address this kind of warning (already mentioned in another answer above this one):

thread.c: In function ‘parseAddr’:
thread.c:189:5: warning: function returns address of local variable [-Wreturn-local-addr]
     return args;
     ^

Giving a short description of your environment may also help people to solve your problem.

jb07
  • 1
  • 2
  • First tip didnt did the trick but I see that I should change it anyway. Will look into that warning soon. What do you mean by environment? – Peter L. May 22 '16 at 13:48
  • Environment = your platform and compiler/linker. – jb07 May 22 '16 at 13:50
  • OS is Linux Mint, I write in NetBeans/Notepaddqq and use a makefile to compile it all through the terminal (gcc -o bot bot.c). I can post the makefile here but I dont think thats related to the error I am getting – Peter L. May 22 '16 at 14:01
  • I'm trying to compile it with gcc (I have a debian), with gcc -pthread thread.c, I have a problem with the askServer function which is undefined (I had to remove the #include "Socket/msocket.h", unknown on my platform). Solving the warnings is very important, because they may lead the program to write in any other variable, making situation very complicated, and possibly, they may lead the program to use senseless data (like length of 1,000,000 for a string for example). Which may in turn lead to smash a stack area... – jb07 May 22 '16 at 14:27
  • Yeah, that function was writen by our teacher, located in socket.h. As I learned you could also replace that with wget... Didnt know that warnings could also cause crashes. I will resolve any warnings and stuff later and look out what I can maybe simblify to locate my mistake. – Peter L. May 22 '16 at 15:13