Every time I run my app I get a different error

I’ve written 5 different versions of this question and I still don’t know what to ask.

I’m making a game in C with SDL. I’m on a Mac using Xcode. I made a basic main loop, and structures to hold tiles, tile maps, and levels, as well as a function to create a level from a json file (using cJSON). The state of my app is rather simple: it will just read a level from JSON, a texture file and try to render the level. I am doing all the stuff like checking for errors and null pointers, set pointers to null after freeing them etc… But every time I run my app WITHOUT MAKING ANY CHANGES TO THE CODE I get a different error on a different part of my program. Sometimes it actually runs just fine, then I hit run again and now there’s an error.

It’s really hard to ask because I really get very different errors for example:
I get an error on SDL_PollEvent

int shouldClose = 0;
while (!shouldClose) {
    SDL_Event event;
    while (SDL_PollEvent(&event)) { // Thread 1: signal SIGABRT
        if (event.type == SDL_QUIT) {
            shouldClose = 1;
            break;
        }
    }
   
    SQLevel_Render(level);
}

Upon closer inspection the error is in SDL_cocoaevents.m in the function Cocoa_PumpEvents in the following line:

NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ];

You would say the problem is clearly in my main loop, but then I run my program again and it works just fine, run it one more time and now the error is in

SQLevel_Render(level); // Thread 1: EXC_BAD_ACCESS (code=1, address=0xe2b5e925)

again with closer inspection the actual error is in SDL_Render.c in the function SDL_RenderCopyF in the line:

CHECK_TEXTURE_MAGIC(texture, -1);

these are just some of the errors that occur randomly when I run my app. There’s no specific order in which they occur and they don’t always occur (sometimes the app works just fine). Besides checking for SDL_QUIT there’s no input, the program is just supposed to render the an image and do nothing else.

All of my code can be found in https://github.com/lsauceda/sour-quest in you want to take a look at it. Any help will be greatly appreciated.

UPDATE: Someone in Reddit pointed out that since my errors are so inconsistent I’m probably accessing some freed memory or running out or an array’s bounds (that’s why sometimes it works and sometimes it doesn’t depending on what the invalid pointed data is). But in any way if someone wants to help me review my code I’ll really appreciate it (its not too large).

In SQLevel_ReadFromFile you are setting the input pointers tilemap and level to point to variables that are allocated on the stack. Those variables are de-allocated when the function exits, this could cause the problems you are having. I didn’t check the rest of the code, it’s possible this error is repeated elsewhere too.

I suppose you mean this:

// Set return values
*tilesets = deserializedTilesets;
*tilemap = deserializedMap;
*level = deserializedLevel;

Well AFAIK I’m copying the local (stack allocated) data into the memory pointed to by tilemap and level so when the function exits, the deserialized- versions get deallocated but their copies are not (because their scope outlives the function).

You are correct, I brain farted on that. Hopefully these suggestions will be more helpful…

In SQLite.c:26 you are declaring a local variable and assigning the output of malloc(...), which I believe returns a void *. You are probably getting compiler warnings about that, I believe you want SQTileset* tiles = (SQTileset*)malloc(...) I’m optimistic this is the root problem.

Another comment, in SQLevel.c:SQLevel_Render(struct SQLevel level) you are passing by value. In this case you would want to pass a pointer or reference as it will save copying the SQLevel each call. Similarly, on line 120, you probably want a pointer to the tile, not to copy the tile.

I hope these comments are more helpful than my first! :slight_smile:

Oh that, well perhaps there’s a better to name it but SQTileset is just a typedef for a SQTile array like this:

typedef struct SQTile* SQTileset;

So the pointer is implicit in the type’s name, I just wanted to make declaring tilesets shorter.

I took a moment to try and build this on Linux, a couple culprits:

  • In pathToRelativeResources, you allocate a string that’s the strlen(a)+strlen(b), but this is one char short. You need a byte for the NULL terminator. So allocate strlen(a)+strlen(b)+1.
  • Same function, you strcat(ret, absolute), but ret is uninitialized memory from malloc(), so this will randomly cause strange effects (notably, crashes if there isn’t a null char in the buffer already, or the level failing to load because the filename will have bogus bytes at the start). Make this first one a strcpy (the second remains a strcat) or consider using snprintf() instead to do it in one (overflow-protected!) call.
  • This line is extremely suspect:
deserializedMap.tiles[i] = deserializedTilesets[tilesetIndex][tileIndex];

I didn’t look closely, but it looks like deserializedMap is misallocated:

deserializedMap.tiles = malloc(tileArraySize * sizeof(struct SQTile*));

You allocated enough for an array of pointers to SQTile, not an array of SQTiles. The assignment from deserializedTilesets definitely overflows this buffer, so I assume that just needs to be sizeof(struct SQTile) without the *.

There might be other things, but your crashes look like you’re overwriting unrelated memory, and both of these things would cause that.

Consider turning on AddressSanitizer in Xcode when debugging, as it (should) catch these things for you!

1 Like

Thank you so much for taking your time to review my code. You were right I was allocating deserializedTiles incorrectly now it always runs flawlessly. Also I didn’t know the address sanitizer was a thing but boy is it useful. The string concatenation stuff I had already fixed since some people on reddit pointed that out.

Again thank you so much, I was about to give up using SDL for this project and just go with an off the shelf engine, but now I can continue (hopefully I won’t get stuck this bad).

1 Like