[Date Prev][Date Next][Thread Prev][Thread Next]
- Subject: Re: Lua and undefined behaviour: A bullet-proof (sandboxed?) secure environment is an impossible dream
- From: Philippe Verdy <verdyp@...>
- Date: Tue, 7 Sep 2021 18:44:47 +0200
That attack based on excessive usage of memory is not impossible to prevent, as long as Lua provides a correct resource manager checking allowed quotas, and then properly generates an error for any scripts attempting to bypass the quota.
Here it is just the lack of quota checking in the currently deployed implementation that is the only problem. It could happen to any project. And even Linux provides per-process quotas, so it should not hang the machine (at worst, that process will be killed with a fatal error: this may not be desirable, but it's up to the process to take preventive measures and implement them, if that process must continue working (for example if it runs concurrent threads for other sessions that should continue working in an unaltered way, you need to make Lua kill the offending thread only, garbage collect its resources, if the thread does not want to die; as the thread may be running a full Lua instance with its coroutines, it's then to the Lua instance to take the preventive measures on quotas, and gracefully send a standard Lua error to the current offending coroutine; then Lua scripts will have to adapt if they want to resist to these errors, so the call to string.rep() can return an error and reject the request to allocate more than what is permitted by the quotas set in the Lua instance, and not just assume everything to be handled by the standard C allocator, which may be more permissive and not checking the quotas for each thread, and certainly not for each Lua coroutine).
In summary, fix the functions in the string.* library to not use the C allocator blindly...
Some of the things that the C standard(s) leave undefined are left
for the OS/runtime libraries to decide. It is simply impossible to
stub off every loose end at the Lua-to-C interface, so please give
Here is a variant of the "billion laughs" attack that I saw Roberto
post many years ago... extended by a significant number of steps.
As the subject line of this message says:
A bullet-proof (sandboxed?) secure environment
is an impossible dream.
I have a fairly hefty machine that can run over ten virtual machines
simultaneously.... I use this as a test-bed for my "lgcicua" project
so I can expose a single tarball to multiple Ubunt/LinuxMint/CentOS
releases, and can check that very simple sanity tests pass.
At the time of posting, the billion-laughs-attack script below is
running at 100% CPU; "top" shows the resident memory for the Lua
script as having just passed the 61GiB mark... and we've just
reached the point to print "lol11"...
... What will happen at some point, is that the kernel's
out-of-memory killer will be invoked, as it sees the precious
resource run out, and the Lua script will be killed mid-operation.
s-b etc etc
----- (cut here) -----
-- Demonstration of billion-laughs attack
lol1 = "lol" print("lol1")
lol2 = string.rep(lol1, 10) print("lol2")
lol3 = string.rep(lol2, 10) print("lol3")
lol4 = string.rep(lol3, 10) print("lol4")
lol5 = string.rep(lol4, 10) print("lol5")
lol6 = string.rep(lol5, 10) print("lol6")
lol7 = string.rep(lol6, 10) print("lol7")
lol8 = string.rep(lol7, 10) print("lol8")
lol9 = string.rep(lol8, 10) print("lol9")
lol10 = string.rep(lol9, 10) print("lol10")
lol11 = string.rep(lol10, 10) print("lol11")
lol12 = string.rep(lol11, 10) print("lol12")
lol13 = string.rep(lol12, 10) print("lo1l3")
lol14 = string.rep(lol13, 10) print("lo1l4")
lol15 = string.rep(lol14, 10) print("lo1l5")
lol16 = string.rep(lol15, 10) print("lo1l6")