Bithack / principia

Open source physics-based sandbox game.
https://principia-web.se
Other
260 stars 25 forks source link

Opening level in sandbox cuts off last byte of file #151

Closed rollerozxa closed 5 months ago

rollerozxa commented 5 months ago

As part of the game's score integrity system, when playing levels from Bithack's community site a single byte is appended to the end of the level file. This is used to prevent cheating when submitting level scores. However, when you try to derive or edit a level the game will trim off this byte to create a clean level file, by decreasing the level buffer size by one.

The issue is that my reimplementation in principia-web does not take this into account when sending levels to the client (working off of very tiny hints from RE'ing most of the time, I had no idea this was a thing). So when you go to derive or edit a level on principia-web, the client will still trim off the last byte of the level file. But this time it is the Adler32 checksum part of the zlib compressed level buffer.

Theoretically this should make the level loading fail immediately, since it will check the Adler32 checksum at the end and either notice it is only 3 bytes or it will be misaligned and as such fail the verification. But it still opens the level perfectly fine despite this. No matter how many times I've read the zlib spec I don't understand why it just accepts that without a second thought, because it would be malformed and treated as such.

I cannot use this bug to make a level unreadable. The scope of the issue is also most likely very limited as the next time a level opened in the sandbox from the Internet is saved, it will recompress the level buffer and produce a level file with an intact Adler32 checksum. But I'm treating it as a level corruption bug since it damages level data even if the game somehow doesn't notice or care about it, and I do not know the full scope of the issue yet.

TL:DR

rollerozxa commented 5 months ago

Apparently the game is perfectly able to handle the compressed buffer being cut off at the end, since the length of the compressed buffer is stored in the level header. It does throw a Z_BUF_ERROR error since it expects more data than what exists, but doesn't get misaligned and just carries on with its day despite the checksum being cut off.

I'll still fix this, but there doesn't seem to be any risk to level data. Quite the overreaction on my part.