1

I did a program in c to do some avl sorting. the program runs well when i test it with no crash. however i ran the program for possible bugs with afl fuzzer and i dont seem to know why i keep getting segmentation fault. below is the tree.c. I dont get bugs when i fuzz without my main.c connecting to the tree.c. Only comes when i send inputs to the tree sorter. Some certain inputs brings about this bug. i made changes to memory allocation but still cant figure out why. i used valgrind, cppcheck and dont get any errors on them. Please why does this error happen and how can i fix it?

an example input from the fuzzer that brings code to segmentation fault

i 3o
i 7ÿo
i
i 3ÿo
i3
3
i 3ÿ3
i83 3
i23ÿo

tree.c

#include "tree.h"

Tree* tree_create(){
    Tree *tree = malloc(sizeof(Tree));
    tree->root = NULL;

    return tree;
}

void tree_node_delete(Node* node) {
    if (node == NULL) {
        free(node);
        return;
    }

    if (node->left) {
        tree_node_delete(node->left);
    }
    if (node->right) {
        tree_node_delete(node->right);
    }
    free(node->name);
    free(node);


}

void tree_delete(Tree* tree) {
    tree_node_delete(tree->root);
    free(tree);
}

void node_insert(Node* node, int age, char* name, int gen) {
    if (age <= node->age){
        if (node->left == NULL){
            Node* newLeft = calloc(1, sizeof(Node));
            newLeft->age = age;
            newLeft->name = name;
            newLeft->parent = node;
            newLeft->right = NULL;
            newLeft->left = NULL;
            newLeft->isRight = false;
            node->left = newLeft;
        } else {
            node_insert(node->left, age, name, node->left->generation);
        }
    } else {
        if (node->right == NULL){
            Node* newRight = calloc(1, sizeof(Node));
            newRight->age = age;
            newRight->name = name;
            newRight->parent = node;
            newRight->right = NULL;
            newRight->left = NULL;
            newRight->isRight = true;
            node->right = newRight;

        } else {
            node_insert(node->right, age, name, node->right->generation);
        }
    }
}

void tree_insert(Tree* tree, int age, char* name) {
    if (tree->root == NULL) {
        Node *node = calloc(1, sizeof(Node));
        node->name = name;
        node->age = age;
        node->isRoot = true;
        node->right = NULL;
        node->left = NULL;
        tree->root = node;

    } else {
        node_insert(tree->root, age, name, 1);
    }
}

void tree_erase(Tree* tree, int age, char* name) {
    Node* data = tree_find(tree, age, name);
    if (data == NULL) {
        printf("\nThis node doesn't exist in the current tree\n");
    } else {

        data->name = NULL;
        data->age = NULL;
        if (data->grandparent) {
            if(data == data->grandparent->grandchildRR) {
                data->grandparent->grandchildRR = NULL;
            } else  if(data == data->grandparent->grandchildRL) {
                data->grandparent->grandchildRL = NULL;
            } else  if(data == data->grandparent->grandchildLR) {
                data->grandparent->grandchildLR = NULL;
            } else  if(data == data->grandparent->grandchildLL) {
                data->grandparent->grandchildLL = NULL;
            }
        }

        tree_cleanup(tree, &tree->root, tree->root);
    }
}

// Will clean the tree to release previously used nodes
void tree_cleanup(Tree* tree, Node** nodeAdress, Node* node) {
    if (node->left) {
        tree_cleanup(tree, &node->left, node->left);
    }
    if (node->right) {
       tree_cleanup(tree, &node->right, node->right);
    }

    if(node->age == NULL && node->name == NULL) {
         *nodeAdress = NULL;
      free(*nodeAdress);
    }

    if(node->left == NULL && node->grandchildLL) {
        node->left = node->grandchildLL;
        node->left->parent = node;
        node->left->grandparent = node->parent;
        if(node->left->left) {
            node->grandchildLL = node->left->left;
        } else {
            node->grandchildLL = NULL;
        }

    } else if(node->right == NULL && node->grandchildRL) {
        node->right = node->grandchildRL;
        node->right->parent = node;
        node->right->grandparent = node->parent;
        node->right->isRight = true;
        if(node->right->left) {
            node->grandchildRL = node->right->left;
        } else {
            node->grandchildLR = NULL;
        }

    } else if(node->left == NULL && node->grandchildLR) {
        node->left = node->grandchildLR;
        node->left->parent = node;
        node->left->grandparent = node->parent;
        node->left->isRight = false;
        if (node->left->right) {
            node->grandchildLR = node->left->right;
        } else {
            node->grandchildLR = NULL;
        }

    } else if(node->right == NULL && node->grandchildRR) {
        node->right = node->grandchildRR;
        node->right->parent = node;
        node->right->grandparent = node->parent;
        if (node->right->right) {
            node->grandchildRR = node->right->right;
        } else {
            node->grandchildRR = NULL;
        }

    }
}

//Calculate the weight and the balance factor of the node.
void tree_balance_factor(Tree* tree, Node* node) {
    if (node == NULL) {
        return;
    }
    if (node->left) {
        tree_balance_factor(tree, node->left);
    }
    if (node->right) {
        tree_balance_factor(tree, node->right);
    }

    if (node->parent) {
        if (node->isRight == true) {
            if (node->weightRight > node->weightLeft) {
                node->parent->weightRight = node->weightRight+1;
            } else {
                node->parent->weightRight = node->weightLeft+1;
            }

        } else if (node->isRight == false) {
            if (node->weightRight > node->weightLeft) {
                node->parent->weightLeft = node->weightRight+1;
            } else {
                node->parent->weightLeft = node->weightLeft+1;
            }
        }
    }

    node->balancefactor = node->weightRight - node->weightLeft;

    if (node->balancefactor == 2) {
        if(node->right->balancefactor == 1) {
            leftRotation(tree, node);
        } else {
            rightPermutation(tree, node);
            leftRotation(tree, node);
        }

    } else if (node->balancefactor == -2) {
        if(node->left->balancefactor == -1) {
            rightRotation(tree, node);
        } else {
            leftPermutation(tree, node);
            rightRotation(tree, node);
        }

    }
}

// Reset the weightings and set up the grandchilds and grandparents relations back
void tree_balance_factor_reset(Tree* tree, Node* node) {
    if (node == NULL) {
        return;
    }
    if (node->left) {
        tree_balance_factor_reset(tree, node->left);
    }
    if (node->right) {
        tree_balance_factor_reset(tree, node->right);
    }

    node->weightLeft = 0;
    node->weightRight = 0;

    if(node->right) {
        if(node->right->right) {
            node->grandchildRR = node->right->right;
            node->right->parent = node;
            node->right->right->grandparent = node;
        } else {
            node->grandchildRR = NULL;
        }

        if(node->right->left) {
            node->grandchildRL = node->right->left;
            node->right->parent = node;
            node->right->left->grandparent = node;
        } else {
            node->grandchildRL = NULL;
        }
    }

    if(node->left) {
        if(node->left->left) {
            node->grandchildLL = node->left->left;
            node->left->parent = node;
            node->left->left->grandparent = node;
        } else {
            node->grandchildLL = NULL;
        }

        if (node->left->right) {
            node ->grandchildLR = node->left->right;
            node->left->parent = node;
            node->left->right->grandparent = node;
        } else {
            node->grandchildLR = NULL;
        }
    }

}
void setGen (Node* node) {

    if(node->parent) {
        node->generation = node->parent->generation +1;
    }

    if (node->left) {
        setGen(node->left);
    }
    if (node->right) {
        setGen(node->right);
    }
}

void leftRotation(Tree* tree, Node* node ) {
    Node* newNode = node->right;
    if(node->isRoot == true) {
        tree->root = newNode;
        newNode->isRoot = true;
        node->isRoot = false;
        newNode->parent = NULL;
    } else if (node->isRoot = false){
        newNode->parent = node->parent;
        if(node->isRight) {
            newNode->parent->right = newNode;
        } else {
            newNode->parent->left = newNode;
        }
    }
    newNode->left = node;
    node->parent = newNode;
    node->right = NULL;
}

void rightRotation(Tree* tree, Node* node) {
    Node* newNode = node->left;
    if(node->isRoot == true) {
        tree->root = newNode;
        newNode->isRoot = true;
        node->isRoot = false;
        newNode->parent = NULL;
    } else {
        newNode->parent = node->parent;
        if(node->isRight) {
            newNode->parent->right = newNode;
        } else {
            newNode->parent->left = newNode;
        }
    }
    newNode->right = node;
    node->parent = newNode;
    node->left = NULL;
}

void rightPermutation(Tree* tree, Node* node) {
    Node* newNode = node->right;
    node->right = newNode->left;
    node->right->right = newNode;
    newNode->left = NULL;
    newNode->parent = node->right;
    node->right->parent = node;
}

void leftPermutation(Tree* tree, Node* node) {
    Node* newNode = node->left;
    node->left = newNode->right;
    node->left->left = newNode;
    newNode->right = NULL;
    newNode->parent = node->left;
    node->left->parent = node;
}

void tree_print_node(Node* node){
    if (node == NULL) {
        printf("null");
        return;
    }

    printf("[");
    printf("{\"%d\":\"%s\"},", node->age, node->name);
    tree_print_node(node->left);
    printf(",");
    tree_print_node(node->right);
    printf("]");
}

void tree_print(Tree* tree, int printNewline){
    if (tree == NULL) {
        printf("null");
        return;
    }

    tree_print_node(tree->root);

    if (printNewline){
        printf("\n");
    }
}

Node* node_find(Node* node, int age, char* name) {
    int i = strcmp(node->name, name);

    if (node->age == age && i == 0) {
        return node;
    }

    if (age <= node->age) {
        if (node->left) {
            return node_find(node->left, age, name);
        } else {
            return NULL;
        }

    } else {
        if (node->right) {
            return node_find(node->right, age, name);
        } else {
            return NULL;
        }

    }
}

Node* tree_find(Tree* tree, int age, char* name) {
    if(tree->root != NULL) {
        return node_find(tree->root, age, name);
    } else {
        printf("\nNo node inserted in the tree yet\n");
        return NULL;
    }

}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "main.h"
#include "tree.h"

int main() {
    char* commandBuffer = (char*)malloc(sizeof(char) * 20);
    Tree *tree = tree_create();


    for(;;) {

        if (tree == NULL){
            tree = tree_create();
        }
        tree_balance_factor_reset(tree, tree->root);
        tree_balance_factor(tree, tree->root);
        tree_balance_factor_reset(tree, tree->root);

        printf("\n- Enter <i age name> to insert a node in the tree \n- Enter <e age name> to erase a node \n- Enter <c age name> to check the tree \n- Enter <p> to "
               "print the tree \n- Enter <x> the delete the tree \n- Enter <q> to exit the program\n\n" );

        fgets(commandBuffer, 20, stdin);

        int b = strlen(commandBuffer);
        if(b>=19) {
            int clearVar;
            while (((clearVar = getchar()) != '\n' && clearVar != EOF)) {
        }
        };

        // Quit on EOF or 'q'
        if (feof(stdin) || *commandBuffer == 'q' ){
            break;
        }

        tree = handleString(commandBuffer, tree);

    };

    free(tree);
    free(commandBuffer);

    return 0;
}


Tree* handleString(char command[], Tree *tree){

    if (command == NULL){
        fprintf(stderr, "Invalid command; null pointer\n");
        return tree;
    }

    switch(command[0]){
        case 'i':
            insert(command, tree);
            break;
        case 'e':
            erase(command, tree);
            break;
        case 'c':
            check(command, tree);
            break;
        case 'p':
            tree_print(tree, 1);
            break;
        case 'x':
            tree_delete(tree);
            return NULL;
        default:
            fprintf(stderr, "Invalid command string: %s\n", command);
            break;
    }

    return tree;
}

Tree* insert(char* command, Tree* tree) {
    int age;
    char* name = malloc(sizeof(char) * 20);

    if (2 != sscanf(command, "i %d %19s", &age, name)){
        fprintf(stderr, "Failed to parse insert command: not enough parameters filled\n");
       // return NULL;
    }

    if (tree == NULL){
        tree = tree_create();
    }

    tree_insert(tree, age, name);

    return tree;
}

int erase(char* command, Tree* tree) {
    int age;
    char* name = malloc(sizeof(char) * 20);

    if (2 != sscanf(command, "e %d %19s", &age, name)){
        fprintf(stderr, "Failed to parse erase command: not enough parameters filled\n");
       // return 0;
    }
    tree_erase(tree, age, name);
    return 0;
}

void check(char* command, Tree* tree) {
    int age;
    char* name = malloc(sizeof(char) * 20);

    if (2 != sscanf(command, "c %d %19s", &age, name)){
        fprintf(stderr, "Failed to parse check command\n");
    }

    Node* result = tree_find(tree, age, name);
    if (result){
        printf("\nThe node is present\n");
    } else {
        printf("\nThe node could not be found\n");
    }
}

tree.h

#ifndef C_IMPLEMENTATION_TREE_H
#define C_IMPLEMENTATION_TREE_H

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

typedef struct Node {
    /**
     * Left child of this node
     */
    struct Node* left;
    /**
     * Right child of this node
     */
    struct Node* right;
    /**
     * The age of the data in this node
     */
    int generation;
    int balancefactor;

    int weightLeft;
    int weightRight;

    struct Node* parent;
    struct Node* grandparent;
    struct Node* grandchildLL;
    struct Node* grandchildLR;
    struct Node* grandchildRL;
    struct Node* grandchildRR;


    bool isRight;
    bool parentIsRight;
    bool isRoot;
    int age;
    /**
     * The name of the data in this node
     */
    char* name;
} Node;


typedef struct Tree {
    Node *root;
} Tree;

/**
 * Create a new tree
 * @param age The age value for the first data point
 * @param name The name value for the first data point
 * @return The root Node of the tree
 */
Tree* tree_create();

/**
 * Delete an entire tree. This will delete the passed Node and all children below it
 * @param node The root Node of the tree to delete.
 */
void tree_delete(Tree* tree);

/**
 * Insert a new data point into the tree
 *
 * @param tree The root node of the tree
 * @param age The age part of the data point
 * @param name The name part of the data point
 */
void tree_insert(Tree* tree, int age, char* name);

/**
 * Remove a data point from a tree
 * @param tree The root node of the tree
 * @param age The age part of the data point to delete
 * @param name The name part of the data point to delete
 */
void tree_erase(Tree* tree, int age, char* name);

/**
 * Prints a tree in the following format:
 * [<data>, <left>, <right>]
 * where the elements above have the following format:
 *  <data>             {<age:int>: "<name:string>"}
 *  <left>, <right>:   The same format as the root node. When a child node is NULL, the string NULL is to be printed.
 */

void tree_cleanup(Tree* tree, Node** nodeAdress, Node* node);
    /**
     * Will free and release null nodes.
     */
void setGen(Node* node);

void tree_balance_factor(Tree* tree,Node* node);

void tree_balance_factor_reset(Tree* tree, Node* node);

void leftRotation(Tree* tree, Node* node);

void rightRotation(Tree* tree,Node* node);

void rightPermutation(Tree* tree, Node* node);

void leftPermutation(Tree* tree, Node* node);

void tree_print(Tree *tree, int printNewline);

Node* tree_find(Tree* node, int age, char* name);

#endif //C_IMPLEMENTATION_TREE_H
King Josh
  • 65
  • 10
  • Can you please include the `main` as well as the data structure definitions? In particular, `node->name = name` may or may not be correct depending on the parameters passed to the insert functions when called. – kaylum Jan 09 '20 at 22:09
  • `char* name = malloc(sizeof(char) * 20); sscanf(command, "i %d %21s", &age, name)`. Why do you use `21` for a buffer of size `20`? It should be `19` as need to leave one byte for the NUL terminator. – kaylum Jan 09 '20 at 22:20
  • i saw that moments ago and ran the fuzzer however still get the segmentation bug – King Josh Jan 09 '20 at 22:23
  • At this point your best bet is to run your program in a debugger with the same inputs as the fuzzer. You said you used valgrind already but was it with the same fuzzer inputs? – kaylum Jan 09 '20 at 22:24
  • i did that as well still get segmentation fault. and the thing is i dont know why? – King Josh Jan 09 '20 at 22:26
  • In that case you should drop mention of the fuzzer as that is no longer important. It's the exact input that matters. So use standard debugging techniques from here forward. For starters, what does the seg fault stack trace show? – kaylum Jan 09 '20 at 22:28
  • thats the thing it just ends the program with segmentation fault, no more details are given – King Josh Jan 09 '20 at 22:32
  • Huh? The debugger should catch that seg fault and then you can examine the stack trace and variables at that point. – kaylum Jan 09 '20 at 22:36
  • @KingJosh if that is all you get, your environment is not fit to develop software. You need a debugger that works. Without that, you are going to have a really, really tough time with your wall of code with intermittent segfaults:( – Martin James Jan 10 '20 at 00:08
  • Did you use Cppcheck as well to test your code? – orbitcowboy Jan 11 '20 at 10:18
  • yes I did use cppcheck. actually found the error with the help of clion but each time I enter a fuzzer input I get an error of segmentation. it all comes from the permutation and rotation functions but the issue is I still trying to figure out how to fix it. all attempts I made have not yet worked. Will keep trying tho – King Josh Jan 12 '20 at 08:33

1 Answers1

1

I managed to reproduce SIGSEGV in gdb. It is hapened in function rightPermutation, which does not check if pointer to left is NULL or not:

void rightPermutation(Tree* tree, Node* node) {
    Node* newNode = node->right;
    node->right = newNode->left;

In case the node->left is NULL, you will get SIGSEGV on next line:

node->right->right = newNode;

Further, you can see details of the message and data of the Node structure.

Program received signal SIGSEGV, Segmentation fault. 0x000055555555570c in rightPermutation (tree=0x555555758280, node=0x555555758d20) at tree.c:322 322 node->right->right = newNode; (gdb) p node $1 = (Node *) 0x555555758d20 (gdb) p *node $2 = {left = 0x555555758ed0, right = 0x0, generation = 0, balancefactor = 2, weightLeft = 1, weightRight = 3, parent = 0x0, grandparent = 0x555555758b70, grandchildLL = 0x0, grandchildLR = 0x0, grandchildRL = 0x0, grandchildRR = 0x555555758c00, isRight = false, parentIsRight = false, isRoot = true, age = 0, name = 0x555555758d00 ""}

risbo
  • 188
  • 7
  • this helped in finding the error how ever this just comes from the permutation side. and I have not figured how to fix this. all my attempts have failed to fix it. this error also comes from the rotate functions and both right and left permutation functions. – King Josh Jan 12 '20 at 08:36
  • once I remove the call of the rotate and pemutation functions I don't get fuzzer errors no more. its now getting fustrating – King Josh Jan 12 '20 at 08:38
  • Your rotation and permutation functions have problems with handling pointers of nodes that participate in operations. I wold like to recommend an example for study at following location: https://www.geeksforgeeks.org/avl-tree-set-1-insertion/. As well, try to compile your code with -Wall option. Additionally, you should simplify your 'Node' structure, it is unnecessary complicated. – risbo Jan 12 '20 at 22:01