1

I am making a JSON interpreter. The problem I am running across is that every time I add the value of an object, it will result in a segmentation fault once i call json->pairsSize++. I have tried looking through the code, and found that every time i call realloc or malloc on json->pairs[json->pairsSize]->values inside of case NEXT_PAIR, it will cause the error, but if i were to comment out the json->pairsSize++, the code will run fine, since it is replacing already existing data.

json.c

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

#include <json.h>

/* Local Data */
void remove_spacing(char * contents);

/* Init/Cleanup */
struct JSON * init_json() {
    struct JSON * json = malloc(sizeof(struct JSON)+1);
    json->objectName = malloc(1);
    json->objectName[0] = '\0';
    json->pairs = malloc(sizeof(struct Pair *));
    json->pairsSize = 0;
    json->objects = malloc(sizeof(struct JSON *));
    json->objectsSize = 0;
    json->prev = NULL;
    return json;
}
struct Pair * init_pair() {
    struct Pair * pair = malloc(sizeof(struct Pair));
    pair->key = malloc(1);
    pair->key[0] = '\0';
    pair->values = malloc(sizeof(void*));
    pair->valuesSize = 0;
    return pair;
}
void interpret(struct JSON * json, char * filePath) {
    FILE * json_file;
    json_file = fopen(filePath, "r");

    if (!json_file) {
        printf("Couldn't find JSON file %s\n", filePath);
        exit(EXIT_FAILURE);
    }

    char * jsonContent = malloc(1);
    unsigned int jsonContentPtr = 0;
    char ch;
    while(!feof(json_file)) {
        ch = fgetc(json_file);
        jsonContent = realloc(jsonContent, jsonContentPtr+1);
        jsonContent[jsonContentPtr] = ch;
        jsonContentPtr++;
    }
    jsonContent[jsonContentPtr] = '\0';
    printf("%s\n", jsonContent);

    // Now the fun begins...
    size_t jsonContentLen = strlen(jsonContent);
    char * placeHolder = malloc(1);
    unsigned int placeHolderIndex = 0;
    bool isArray = false;

    for (size_t i = 0; i < jsonContentLen; i++) {
        switch(jsonContent[i]) {
            case OBJECT_OPEN: {
                if (i != 0 && isArray == false) {
                    struct JSON * newJson = init_json();
                    add_object(json, newJson);
                    json = json->objects[json->objectsSize-1];

                    // // Set objectName
                    placeHolder[placeHolderIndex] = '\0';
                    set_objectName(json, placeHolder);
                    placeHolder[0] = '\0';
                    placeHolderIndex = 0;
                }
                else if (isArray == true) {

                }
                break;
            }
            case OBJECT_CLOSE: {
                // Add last value

                if (json->prev != NULL) {
                    json = json->prev;
                }
                break;
            }
            case KEYPAIR_DELIM: {
                placeHolder[placeHolderIndex] = '\0';

                json->pairs = realloc(json->pairs, sizeof(struct Pair *) * (json->pairsSize+1));
                json->pairs[json->pairsSize] = init_pair();
                json->pairs[json->pairsSize]->key = realloc(json->pairs[json->pairsSize]->key, placeHolderIndex+1);
                strncpy(json->pairs[json->pairsSize]->key, placeHolder, placeHolderIndex+1);
                printf("%s\n", json->pairs[json->pairsSize]->key);

                placeHolder[0] = '\0';
                placeHolderIndex = 0;

            }
            case NEXT_PAIR: {
                placeHolder[placeHolderIndex] = '\0';
                size_t placeHolderLen = strlen(placeHolder);

                // Where the error occurs everytime i have json->pairsSize++ uncommented
                json->pairs[json->pairsSize]->values = realloc(json->pairs[json->pairsSize]->values, sizeof(void *) * (json->pairs[json->pairsSize]->valuesSize+1));
                json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize] = malloc(placeHolderLen+1);
                
                strncpy(json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize], placeHolder, placeHolderLen+1);
                printf("%s\n", json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize]);

                placeHolder[0] = '\0';
                placeHolderIndex = 0;
                json->pairs[json->pairsSize]->valuesSize++;
                
                if (isArray == false) {

                    // Causes a segmentation fault for some reason, can't figure it out
                    json->pairsSize++;
                }
                break;
            }
            case DOUBLE_QUOTE: {
                break;
            }
            default: {
                placeHolder = realloc(placeHolder, placeHolderIndex+1);
                placeHolder[placeHolderIndex] = jsonContent[i];
                placeHolderIndex++;
                break;
            }
        }
    }

    free(placeHolder);
    fclose(json_file);
}

/* Setters */
void set_objectName(struct JSON * json, char * value) {
    size_t valueLen = strlen(value);
    json->objectName = realloc(json->objectName, valueLen+1);
    strcpy(json->objectName, value);
}

void add_object(struct JSON * json, struct JSON * newJSON) {
    newJSON->prev = json;
    json->objects = realloc(json->objects, sizeof(struct JSON *)*(json->objectsSize+1));
    json->objects[json->objectsSize] = newJSON;
    json->objectsSize++;
}

json.h

#ifndef C_JSON_H
#define C_JSON_H

#define DOUBLE_QUOTE '"'
#define KEYPAIR_DELIM ':'
#define OBJECT_OPEN '{'
#define OBJECT_CLOSE '}'
#define ARRAY_OPEN '['
#define ARRAY_CLOSE ']'
#define NEXT_PAIR ','

struct Pair {
    char * key;
    void ** values;
    unsigned int valuesSize;
};

struct JSON {
    char * objectName;

    struct Pair ** pairs;
    unsigned int pairsSize;

    struct JSON ** objects;
    unsigned int objectsSize;
    struct JSON * prev;
};

struct JSON * init_json();
struct Pair * init_pair();
void interpret(struct JSON * json, char * filePath);

/* Setters */
void set_objectName(struct JSON * json, char * value);

#endif

main.c

#include <json.h>
#include <stdio.h>

int main(void) {
    struct JSON * json = init_json();
    interpret(json, "./tests/objects.json");
    printf("\n%s\n", json->objects[0]->pairs[0]->key);
    return 0;
}

JSON test file (objects.json)

{
    "Test-Object-1": {
        "Song": "My Heroine",
        "Author": "Silverstein",
        "Length": 240
    }
}
  • 1
    Statements like this: `json->objectName = malloc(1);` should concern you. – ryyker Sep 07 '22 at 14:28
  • @ryyker I reallocate the size of it later on, so that it doesn't cause a problem. I just need the size of placeHolder which contains the string i will use for the objectName – ThatPrimitive_Remastered Sep 07 '22 at 14:30
  • Possibly unrelated?? [Why is “while( !feof(file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) – pmg Sep 07 '22 at 14:32
  • I suggest making a [mcve]. This one isn't very minimal. – Nate Eldredge Sep 07 '22 at 14:33
  • @NateEldredge if you could tell me how to make it more minimal that would be perfect, since i'm providing all the data that makes the function work, and telling you what I need help on. I tried my best to make it as minimal as possible and I know it's a lot of data, but it's the lowest I can go in json.c – ThatPrimitive_Remastered Sep 07 '22 at 14:35
  • In short, you start deleting or stubbing out code until the bug no longer appears. Then you put it back and delete something else. – Nate Eldredge Sep 07 '22 at 14:37
  • `jsonContent[jsonContentPtr] = '\0';`: Hint: what is the value of `jsonContentPtr` at this point, relative to the number of bytes allocated for `jsonContent`? – Nate Eldredge Sep 07 '22 at 14:38
  • As a general tip, if you have an array of size `sz`, then `array[sz]` is off the end and must never appear. There are several other places in your code that look suspicious in this regard. – Nate Eldredge Sep 07 '22 at 14:39
  • @NateEldredge That's not the issue at hand, and it points to the end of the ```char *``` so that I'm able to close it off with a '\0', so that no memory leaks can happen in the string. The string reallocates so that it is able to be included in the string – ThatPrimitive_Remastered Sep 07 '22 at 14:41
  • What is the purpose of `malloc(1)`? Why can you not just have a one byte member in the struct instead of a pointer to a one byte allocation? – user16217248 Sep 07 '22 at 17:42
  • @ThatPrimitive_Remastered: It doesn't point to the end of the buffer, it points *past* the end. Step through it with a pencil. Suppose the loop iterates 3 times. On entry to the third iteration, `jsonContentPointer == 2`, so you realloc the buffer to size 3, and then `jsonContentPointer++` leaves `jsonContentPointer == 3` when the loop exits. So after the loop, you write a null to `jsonContent[3]`, i.e. its fourth element, while it only points to a buffer of size 3. (As a bonus, per pmg's comment about `while (!feof(file))`, `jsonContent[2]` contains an unwanted `EOF` byte.) – Nate Eldredge Sep 08 '22 at 13:56
  • @ThatPrimitive_Remastered: And as to "not the issue at hand": Well, nobody can really be sure of that, unless you fix it and see that the problem still appears. When you corrupt memory in one part of your program, it is entirely capable of causing totally unrelated parts of the program to misbehave. – Nate Eldredge Sep 08 '22 at 13:58
  • I think running this code through valgrind would be educational. It catches the invalid memory accesses that occur *before* the segfault and probably contribute to it. (There are a lot of them.) – Nate Eldredge Sep 08 '22 at 14:05

1 Answers1

0

problems i have identified:

  • init_json() allocates 1 extra byte for json struct for some reason

  • interpret() uses a loop with !feof() condition which is incorrect

  • interpret() uses char ch for fgetc() , it should use int since fgetc can return EOF

  • interpret() has a buffer overflow in line jsonContent[jsonContentPtr] = '\0';

The code looks quite messy, you should take some time to rethink your approach.