How to save and load in love(easiest method I've found while trying to learn)

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
dusoft
Party member
Posts: 482
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by dusoft »

pgimeno wrote: Tue Jan 31, 2023 6:40 pm There are several serialization options that don't generate Lua: bitser, binser, smallfolk and even dkjson.
OT: Last time I tried both bitser and binser were crashing or unable to provide proper export of Lua tables. Not sure what was the issue - if the UTF-8 chars or something different.
RNavega
Party member
Posts: 235
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by RNavega »

pgimeno wrote: Tue Jan 31, 2023 6:40 pm Imagine that a game becomes popular enough that there's a webpage in some site that is dedicated to sharing stored savegames, with insufficient moderation.

Or that someone in a chat tricks a guy who doesn't even know that a savegame is executable code, to use a manipulated savegame, by asserting that it will give them advantage, or it will unlock hidden stuff, or whatever other means of social engineering.
As far as the terms of use of your game should go, your game is offered as is and your only warranty is that it's not malware.
If people start modding/tampering with it (what's to say they don't mod the actual source files and make it a malware), then that's a breach of your terms of use, it's not the content from your game anymore.

But in any case, you can safeguard this in two ways.
The first way is to supply a custom environment when running the function (I'm using Lua's built-in loadstring instead of love.filesystem.load, for simplicity):

Code: Select all

local func, errorMessage = loadstring(saveGameScript)
if func then
    setfenv(func, {}) -- Set an empty environment, no globals/builtins at all.
    return func()
else
    error(errorMessage)
end
I think that severely limits what a bad actor can do. They can't require any modules and use them because the 'require' global is gone, or use metatable/string/table functions etc.

The second way is preventing "pranks", like infinite recursion until stack overflow, or using a loop to allocate up to the memory limit etc.
I don't think there's any way to prevent this other than setting yourself some conventions: you write a partial Lua file with the contents of the data table, but not the table curly brackets and the 'return' statement, those are added by the loading code.
That is, the data table for the save game is:

Code: Select all

["Player"] = {
    ["x"] = 430,
    ["y"] = 122,
},
["Settings"] = {
    ["Volume"] = 0.762,
}
...which you then load like this:

Code: Select all

local func, errorMessage = loadstring("return {" .. saveGameScript .. "}")
...
This limits the available syntax to only simple pranks like an infinite loop, maybe overflowing available memory etc.
Edit: this type of prank:

Code: Select all

["MyKey"] = (function() while true do end end)(),
Is there a way of preventing those?
Since they'd have to use Lua keywords (like for, while, repeat), I think the only way of preventing them is by encoding all data strings before saving the file, so that before running the unknown source you can search for those keywords out in the open, and raise an error.
If there aren't any, after loading the data you go through all strings and decode them.
There's also the issue of size. Lua files have a size limit; arbitrary files don't.
I didn't know this. What size does LuaJIT limit the script files to?
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by pgimeno »

RNavega wrote: Wed Feb 01, 2023 12:51 am As far as the terms of use of your game should go, your game is offered as is and your only warranty is that it's not malware.
If people start modding/tampering with it (what's to say they don't mod the actual source files and make it a malware), then that's a breach of your terms of use, it's not the content from your game anymore.
Being legally liable is a separate matter; my argument is that it is a security problem that puts your users in danger.

RNavega wrote: Wed Feb 01, 2023 12:51 am But in any case, you can safeguard this in two ways.
The first way is to supply a custom environment when running the function (I'm using Lua's built-in loadstring instead of love.filesystem.load, for simplicity):
Okay, that diverges from your initial "just save to Lua because it's secure" that you were advising. But even then, you would not be securing it properly, because you're not preventing bytecode execution - a known exploit in LuaJIT. And these are the known attack vectors.

RNavega wrote: Wed Feb 01, 2023 12:51 am Edit: this type of prank:

Code: Select all

["MyKey"] = (function() while true do end end)(),
Is there a way of preventing those?
With the JIT disabled, there is, via debug.sethook(). With the JIT enabled, compiled loops, including tail calls, escape the detection of sethook, so the only way would be to use a Lua parser to detect it, because keywords can appear in strings, for example a valid string is "Eat all pills while there are any on the screen", so you have to distinguish strings from non-strings, and even recognize comments - but if you're doing that, you'd better use a non-Lua save format anyway since you're already parsing.

If the goal is to allow users to run code in a secure environment, the best sandbox I know so far, which uses debug.sethook(), is https://github.com/minetest-mods/meseco ... r/init.lua. I think it doesn't protect against bytecode execution, though, but it catches infinite loops and the like, as it's designed to allow users to write code that runs in servers. If bytecode has not been a problem yet, it's probably because it isn't that easy to send it: it would require a client expressly modified for that purpose, and even then there could be caveats, not sure.

Or maybe it's protected against that and it just doesn't use the word "bytecode" in the code, which is what I searched for.

RNavega wrote: Wed Feb 01, 2023 12:51 am
There's also the issue of size. Lua files have a size limit; arbitrary files don't.
I didn't know this. What size does LuaJIT limit the script files to?
I haven't experienced it myself, and this may be old information that only applies to PUC Lua or to LuaJIT 2.0. I just remember people complaining about it in old threads (2016-ish). Something like "~1 MB bytecode chunks" rings a bell.


dusoft wrote: Tue Jan 31, 2023 11:03 pm OT: Last time I tried both bitser and binser were crashing or unable to provide proper export of Lua tables. Not sure what was the issue - if the UTF-8 chars or something different.
There have been crash fixes to bitser; depends on when you tried last time. If UTF-8 chars was the problem, then the problem would be in your code and not in bitser, because bitser deals with binary strings without interpreting their characters.
RNavega
Party member
Posts: 235
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by RNavega »

pgimeno wrote: Wed Feb 01, 2023 4:44 am But even then, you would not be securing it properly, because you're not preventing bytecode execution - a known exploit in LuaJIT. And these are the known attack vectors.
That's a cool hack!
To prevent bytecode being executed, I'd load the save file into a string, then check if it starts with the LuaJIT bytecode header: "(ESC)LJ" or "\x1b\x4c\x4a".

Reference: https://github.com/LuaJIT/LuaJIT/blob/7 ... .h#L16-L34
User avatar
fridays18
Citizen
Posts: 90
Joined: Tue Nov 01, 2022 3:24 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by fridays18 »

This simple post has really blown up lol
RNavega
Party member
Posts: 235
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by RNavega »

Congrats on the productive thread :)
User avatar
togFox
Party member
Posts: 764
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by togFox »

The two lines of bitser I posted on page 1 or 2 is to somewhat guard against unwanted lua code execution because one of my dedicated fans getting attacked would make me feel bad and me saying "sorry - I'm not legally responsible" won't make me or my fan feel better.
Current project:
https://togfox.itch.io/backyard-gridiron-manager
American football manager/sim game - build and manage a roster and win season after season
User avatar
fridays18
Citizen
Posts: 90
Joined: Tue Nov 01, 2022 3:24 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by fridays18 »

Wdym?
RNavega
Party member
Posts: 235
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by RNavega »

I was going to see how you could inspect the LuaJIT bytecode of the save file to find unwanted function calls or loops etc, but on second thought, time is better spent implementing / interfacing with the serialization libs that are being mentioned here, which use formats like CSV, JSON etc where there's no risk.

It's a shame, as fun as it is to try to sandbox loadstring() or love.filesystem.load() to be safe for that kind of use, you shouldn't use them to execute scripts that users might tamper with.
togFox wrote: Wed Feb 01, 2023 12:48 pm one of my dedicated fans getting attacked would make me feel bad and me saying "sorry - I'm not legally responsible" won't make me or my fan feel better.
I almost feel offended by that.
All of us care about our products and users.

The legal text is supposed to cause a strong impression so that everyone understands that modded content is uncharted territory and that you can't guarantee the quality or safety of it.
Android unknown sources warning.jpg
Android unknown sources warning.jpg (42.71 KiB) Viewed 1631 times
User avatar
dusoft
Party member
Posts: 482
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: How to save and load in love(easiest method I've found while trying to learn)

Post by dusoft »

pgimeno wrote: Wed Feb 01, 2023 4:44 am
dusoft wrote: Tue Jan 31, 2023 11:03 pm OT: Last time I tried both bitser and binser were crashing or unable to provide proper export of Lua tables. Not sure what was the issue - if the UTF-8 chars or something different.
There have been crash fixes to bitser; depends on when you tried last time. If UTF-8 chars was the problem, then the problem would be in your code and not in bitser, because bitser deals with binary strings without interpreting their characters.
Not sure, it was maybe two or three years back. I was dissapointed by these supposedly well tested serializers. I come from PHP / Javascript environments, where such low-level libraries just work. These in Lua had basic issues, a clear symptom of not well tested libs.

UTF-8 was just something I remembered, possibly it was something completely unrelated such as tables with mixed and complicated structure including callbacks / functions.

I went with https://github.com/pkulchenko/serpent in the end.
Post Reply

Who is online

Users browsing this forum: No registered users and 19 guests