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?