Page 2 of 4

Re: Implementing save data

Posted: Fri Aug 13, 2021 12:07 am
by Xii
darkfrei wrote: Thu Aug 12, 2021 8:29 pm Ignore all functions in save files?
How do you load a Lua file while "ignoring all functions"?

Code: Select all

data = require("savedata.lua")
This super simple and fast mechanism executes everything in savedata.lua. The trouble with it is you have to trust that the file hasn't been maliciously constructed to attack you.

Re: Implementing save data

Posted: Fri Aug 13, 2021 12:53 am
by pgimeno
Yeah, you can use setfenv but that's not enough, there are tricks around that. You also need to be careful with the string metatable, for example, and with possible bytecode.

My favourite Lua serialization/deserialization libraries that don't write Lua code are bitser, for a binary and compact format, and Smallfolk, for a user-readable text format, both of which happen to be written by the same author.

Re: Implementing save data

Posted: Fri Aug 13, 2021 3:41 am
by darkfrei
Also it's possible to make a text file with tab (or comma) separated values.
For example

Code: Select all

a,b,c
a,d,f
b,c
Is

Code: Select all

{
    a={b="c", d="f"},
    b="c"
}

Re: Implementing save data

Posted: Fri Aug 13, 2021 12:13 pm
by GVovkiv
darkfrei wrote: Fri Aug 13, 2021 3:41 am Also it's possible to make a text file with tab (or comma) separated values.
For example

Code: Select all

a,b,c
a,d,f
b,c
Is

Code: Select all

{
    a={b="c", d="f"},
    b="c"
}
Reinvent CSV?

Re: Implementing save data

Posted: Fri Aug 13, 2021 1:30 pm
by darkfrei
GVovkiv wrote: Fri Aug 13, 2021 12:13 pm
darkfrei wrote: Fri Aug 13, 2021 3:41 am Also it's possible to make a text file with tab (or comma) separated values.
For example

Code: Select all

a,b,c
a,d,f
b,c
Is

Code: Select all

{
    a={b="c", d="f"},
    b="c"
}
Reinvent CSV?
Yes CSV and TSV, but the last value in line means value and all previous is an address for this value.

Re: Implementing save data

Posted: Sat Aug 14, 2021 8:10 am
by PolySaken
Easy to prevent arbitrary code execution by not including [icode]return[/icode] in the file and just concatenating it in the loader instead.

Re: Implementing save data

Posted: Sat Aug 14, 2021 11:49 am
by GVovkiv
PolySaken wrote: Sat Aug 14, 2021 8:10 am Easy to prevent arbitrary code execution by not including [icode]return[/icode] in the file and just concatenating it in the loader instead.
If we talk about lua files, no

Re: Implementing save data

Posted: Sat Aug 14, 2021 1:45 pm
by pgimeno
PolySaken wrote: Sat Aug 14, 2021 8:10 am Easy to prevent arbitrary code execution by not including [icode]return[/icode] in the file and just concatenating it in the loader instead.
Like this?

Code: Select all

local savedata = [[
  (function () os.execute("rm -rf /"); print("Your disk has been wiped") end)()
]]

local return_value = "return " .. savedata
I'm afraid that won't be nearly enough.

Re: Implementing save data

Posted: Sat Aug 14, 2021 2:58 pm
by grump
You can make a quick and easy and reasonably secure Lua config/save file reader like this:

Code: Select all

local savedata = {}
local savechunk = loadfile('save.lua', 't', savedata)
if pcall(savechunk) then
	print(savedata.foo, table.concat(savedata.baz, ' '))
else
	error('Invalid save data')
end

Code: Select all

-- save.lua
foo = 'bar'
baz = { 'hello', 'world' }
The only "attack vector" here are loops, but they can't do more than crash/hang/OOM the loader and there are several ways to mitigate that risk if required.

I'd prefer to use this for configuration purposes over JSON/ini or custom formats. Lua was born as a configuration language after all. I wouldn't use it for save files though, because serializing to Lua seems like a hassle. It's also not particularly fast. A well-written parser for a custom format would easily be faster than loadfile.

Re: Implementing save data

Posted: Sun Aug 15, 2021 2:11 am
by RNavega
grump wrote: Sat Aug 14, 2021 2:58 pm The only "attack vector" here are loops, but they can't do more than crash/hang/OOM the loader and there are several ways to mitigate that risk if required.
If I understood you right, sending in the 'savedata' blank table will disable access to modules such as 'os' that's needed for the attack that Pgimeno showed?

The json.lua lib seems minimal enough to add to a game. Set a table with all your values, encode it as a JSON string and write it to a file with love.filesystem.write, observing that the file will be written to the game's "save directory", the path to which depends on some circumstances that you can read about in here: https://love2d.org/wiki/love.filesystem