One of two similar while loops is causing segfault at fread

Advertisements

I am trying to load two files into memory, one as an array of 2d arrays of chars (i.e., a char ***) and the other as a hash table. My whole file is below, and I can share the files in files/ if they are asked for. My goal is to implement a minimax algorithm to solve a game a friend made called WordBord (wordbord.com, and I decided that C would be the most efficient language. If you have suggestions for other languages (I am proficient in Python and know probably enough Java to get this done), please tell me. I am mainly doing this to challenge myself.

Program:

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

#define BOARDS 730
#define BOARD_SIZE 5
#define NUM_BUCKETS 3001

/*
 * Plan: Board layout:
 * Array of 2d arrays, so a char *** array (lol)
 * Array of strings (words)
 */

struct node {
    struct node *next;
    char *word;
};

char ***boards;
struct node **words;

// Djb2 hash function
// Code from:
// https://gist.github.com/MohamedTaha98/ccdf734f13299efb73ff0b12f7ce429f
unsigned long hash(char *str) {
        unsigned long hash = 5381;
        int c;
        while ((c = *str++))
            hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
        return hash % NUM_BUCKETS;
}

bool isword(char *str) {
    unsigned long int idx = hash(str);
    struct node *node_ptr = words[idx];
    while (node_ptr != NULL) {
        if (!strcmp(node_ptr->word, str)) {
            return true;
        }
        node_ptr = node_ptr->next;
    }
    return false;
}

void add(char *str) {
    // Add string to hash table
    printf("%s\n", str);
}

int main() {
    FILE *boards_file = fopen("files/boards", "r");
    char c[BOARD_SIZE + 1];
    int count = 0;
    int sub_board_count = 0;
    boards = malloc(sizeof(char**) * BOARDS);
    for (int i = 0; i < BOARDS; i++) {
        boards[i] = malloc(sizeof(char*) * BOARD_SIZE);
        for (int j = 0; j < BOARD_SIZE; j++) {
            boards[i][j] = malloc(BOARD_SIZE + 1);
        }
    }

    printf("loading...\n");
    printf("here\n");
    while (fread(&c, 1, BOARD_SIZE, boards_file)) {
        printf("here\n");
        strcat(boards[count][sub_board_count], c);
        printf("here\n");
        printf("%s -> %s (%i, %i)\n", c, boards[count][sub_board_count], count, sub_board_count);

        fseek(boards_file, 1, SEEK_CUR);
        sub_board_count++;
        if (sub_board_count == 5) {
            // We reached the end of the board!
            count++;
            sub_board_count = 0;
        }
    }

    printf("done loading, printing...\n-----\n");
    for (int i = 0; i < BOARDS; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
            printf("%s\n", boards[i][j]);
        }
        printf("-----\n");
    }
    fclose(boards_file);
    printf("done loading boards, loading words...\n");

    FILE *words_file = fopen("files/words", "r");
    char c2[BOARD_SIZE + 1];
    printf("here\n");
    while (fread(&c2, 1, BOARD_SIZE, words_file)) {
        printf("here\n");
        add(c2);
    }

    printf("program done, freeing...\n");
    // don't forget to close and free everthing!
    for (int i = 0; i < BOARDS; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
            free(boards[i][j]);
        }
        free(boards[i]);
    }
    free(boards);
}

When compiled with gcc (full command: gcc -Wall -Werror -Wextra -Wno-sign-compare -Wshadow wordbord-solver.c -lcrypt -lm -lgmp -o wordbord-solver (I know I don’t need those links but they’re useful for other programs, ie make <name>), a segfault occurs on line 95, the second while loop.

Thank you in advance for your time. Apologies if I am being stupid and missing something obvious, but some google digging showed that lines like char c[BOARD_SIZE + 1] worked as well as some heap stuff (e.g. char *c = malloc(BOARD_SIZE + 1); memset(c, 0, BOARD_SIZE + 1);)

>Solution :

Issues

Here are some issues that might be causing your segmentation fault:

  1. Both the buffer c and c2 are declared on the stack, and not null-terminated. You’re unsafely assuming they are null-terminated. This can easily be a source of a segfault when you try to printf these buffers (printf always searches for the null character as an indication of the end of the buffer)
  2. You are using strcat in the same way on a non-nulled buffer that you allocate for your 2D boards (strcat(boards[count][sub_board_count], c);). You malloc‘d these buffers but that memory isn’t guaranteed to be zero. strcat also looks for a null character to indicate where to begin concatenating.
  3. You indefinitely read in boards from the file boards_file, and never check to see if you have capacity. You’ve fixed your board count at 730 (#define BOARDS 730). If the file contains more, you’ll also get a segmentation fault because you don’t perform a bounds check.

Fixes

  • Always zero your stack buffers. You can do this with: char c[N] = {0}; // Zeroes the entire buffer.
  • Always zero your allocated memory, if you’ll be storing strings in them and using C string functions. So either use memset or calloc to allocate them zeroed.

Leave a ReplyCancel reply