G'day,
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
up hoping.
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.
https://sourceforge.net/projects/lglicua/
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.
cheers,
s-b etc etc
----- (cut here) -----
#!/usr/bin/env lua
-- Demonstration of billion-laughs attack
-- https://en.m.wikipedia.org/wiki/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")
Avoiding undefined behavior is ONE part of writing secure software. Well-formed code that does not invoke undefined behavior can still be a security risk. Case in point, consider the function "os.execute()". Its behavior is well-defined. It is also horrifically dangerous to call with user-supplied input.
In this particular case, this attack is trivially defeated by applying a maximum buffer size, which is a sensible thing to do when operating on user-supplied input of unknown size.
Just because you can't stop all security issues just by avoiding undefined behavior doesn't mean it isn't appropriate to avoid undefined behavior.
/s/ Adam