Home > Net >  My program throws SEGFAULT but if I compile with -fsanitize=address it works? Why is that and how ca
My program throws SEGFAULT but if I compile with -fsanitize=address it works? Why is that and how ca

Time:10-23

I'm writing a C project for school assignments with pthreads and for some reason my program throws SEGFAULT (exact error is "[1] 2446 segmentation fault ./time") if I try to run it when I compile it without fsanitize=address. However if I compile the same program with fsanitize=address, the program throws

time(2544,0x10b1d1600) malloc: nano zone abandoned due to inability to preallocate reserved vm space.

but still runs. I compile using the make file I've made:

CC = /usr/bin/gcc
CPPFLAGS =-g -Wall -fsanitize=address -std=c11
HDRS = gui2.h display.h
OBJS = gui2.o display.o

main: $(OBJS) $(HDRS)
    $(CC) $(CPPFLAGS) -lpthread -o time time.c $(OBJS)

%.o: %.c %.h

.PHONY: clean

clean:
    rm -r time.dSYM && rm $(OBJS) time

As this is incorrect behaviour, how can I fix this?

I'm on macOS 12.6
$ gcc --version
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

My code is here:

time.c

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include "gui2.h"


typedef struct data {
    int tid;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} data;

typedef struct gui_data {
    int bin;
    int *done;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} gui_data;


void *thread_function(void *args)
{
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 100000000;
    
    pthread_t t;
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_t cond;
    pthread_cond_init(&cond, NULL);


    data *thread_data = (data*)args;
    
    gui_data *threadData = (gui_data*)malloc(sizeof(gui_data));
    threadData->bin = thread_data->tid;
    threadData->done = (int*)malloc(sizeof(int));
    *(threadData->done) =  0;
    threadData->mutex = &mutex;
    threadData->cond = &cond;
    
    //instead of tracking the counter for the iteration with a variable, we implemented a simpler counting mechanism in the thread itself with a explicit var
    for (int i = 0; i < 2;   i)
    {
        *(threadData->done) = 0;
        pthread_mutex_lock(&mutex);
        pthread_create(&t, NULL, gui, threadData);

        if (threadData->done == 0)
        {
            pthread_cond_wait(threadData->cond, threadData->mutex);
        }
        nanosleep(&ts, NULL); 
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_join(t,NULL);

    free(threadData->done);
    free(threadData);
    pthread_exit(NULL);
}

int main()
{
    int n = 100;
    pthread_t tid[n];
    data args[n];

    for (int i = 0; i < n; i  )
    {
        args[i].tid = i;
    }
    
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 100000000;

    for (int i = 0; i < n;   i)
    {
        nanosleep(&ts, NULL);
        pthread_create(&tid[i], NULL, thread_function, &args[i]);
    }
    return EXIT_SUCCESS;
}

gui2.c

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <pthread.h>

#include "display.h"

#define PI2 6.28318530717

typedef struct gui_data {
    int bin;
    int *done;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} gui_data;

void *gui(void *count)
{
    gui_data *guiData = (gui_data*)count;

    pthread_mutex_lock(guiData->mutex);
    int x, y;
    char panel[HEIGHT][WIDTH];
    
    fill(panel);
    x = ((guiData->bin / 10) % 10);
    y = (guiData->bin % 10);
    set(panel, x, y, 'X');
    
    system("clear");
    display(panel);
    printf("\n");
    
    *(guiData->done) = 1;
    pthread_mutex_unlock(guiData->mutex);
    pthread_exit(NULL);
}

gui2.h

#ifndef GUI_H
#define GUI_H

void *gui(void *count);

#endif

display.c

#include <stdio.h>
#include <string.h>
#include "display.h"

void set(char buf[HEIGHT][WIDTH], int i, int j, char c)
{
    buf[j][i] = c;
}


void fill(char buf[HEIGHT][WIDTH])
{
    for (int j = 0; j < HEIGHT; j  )
    {
        for (int i = 0; i < WIDTH; i  )
        {
            buf[j][i] = '`';
        }
    }
}


void display(char buf[HEIGHT][WIDTH])
{
    char frame[HEIGHT*(WIDTH 1) 1];
    
    copy_frame(frame, buf);
    
        printf("%s", frame);

}


void copy_frame(char f[], char b[HEIGHT][WIDTH])
{
    for (int i = 0; i < HEIGHT; i  )
    {
        strncpy(
            &(f[i*(WIDTH 1)]),
            b[i],
            WIDTH
        );
        f[i*(WIDTH 1)   WIDTH] = '\n';
    }
    f[HEIGHT*(WIDTH 1)] = '\0';
}

and finally this is display.h

#ifndef HEADER_DISPLAY
#define HEADER_DISPLAY

#define WIDTH 10
#define HEIGHT 10

void set(char buf[HEIGHT][WIDTH], int, int, char);
void fill(char buf[HEIGHT][WIDTH]);
void display(char buf[HEIGHT][WIDTH]);
void copy_frame(char[], char buf[HEIGHT][WIDTH]);

#endif

CodePudding user response:

Is there any reason why done is allocated on heap? You can make this variable a regular int. I think that root of your problem is in wrong work with pointers:

*(guiData->done) = 1;

I think you wanted to do this:

*(guiData)->done = 1;

It looks that you are locking mutex in

void *thread_function(void *args)

and then trying to do the same in

void *gui(void *count)

CodePudding user response:

You are creating 100 threads in main and never join them. You pass each of them a pointer to local data. As soon as main exits, every one of these threads accesses memory through a dangling pointer.

You are also creating two additional threads inside thread_function, passing them pointers to local data, but you are clobbering the thread id for the second thread, and you only have one pthread_join. As soon as thread_function exits, the thread that was not joined will access memory through a dangling pointer.

  • Related