I'm trying to make a quick loadstring command that I can execute when I submit a certain string to the function. However I don't want someone to be able to say "while true do end" or "repeat until nil" or "for i=1,math.huge end" etc. Is there a way to prevent these infinite loops from happening, or at least give them a default number of iterations? For example:
local code=[[ while true do end ]] loadstring(code)() -- would still run, but just wouldn't run the "while" loop forever
Sorry that I have no other example code to provide, I'm absolutely clueless as to how (or if) this is even possible. Can anyone help?
(Short answer: look at final paragraph)
If you found every instance of where an infinite loop might happen (and, as Epidemiology points out, this would have to include recursion) and added a "wait" command, you would prevent the server from outright crashing. However, there'd be nothing to stop someone from spawning thousands (or millions) of threads that are in infinite loops, which would cause tremendous server lag.
Another option is, instead of a "wait" command, run a function that checks how long that thread has been processing since its last "wait" command (you'd have to replace any "wait" commands with a custom one of your making so that you could keep track of when the thread last waited). Then, if a full 0.1 real time seconds have gone past (use tick()
to keep track of this), you could kill the thread (by using error(), for instance).
Of course, a person could get around this by loading the infinite loop using loadstring. You'd have to prevent loadstring from ever occurring.
One can also lag the server by creating tons of parts/instances - you could limit this by monitoring the use of Instance.new, but there's also the matter of table creation and usage. ex, consider this loop:
local x = 0 local t = {} while true do for i = 1, 10000 do x = x + 1 t[x] = i end wait() end
This won't crash the server immediately, but it will start using up a lot of memory. You could monitor the memory usage by replacing all instances of {} with a table of your own construction (then you could monitor table access using __newindex and __index; you'd add the number of indices in use to some global per-player "memory usage" value; if that gets too large, error() and exit the script) - though this would slow the script down.
One thing to consider trying is to loadstring on the client side. Theoretically, if the player creates an infinite loop, their client would crash and the server wouldn't receive the script. However, since loadstring cannot be used from localscripts, you'd have to enable FilteringEnabled and then communicate all things a script does to the server (ex if it creates a Part, you'd have to replicate the part using RemoteEvents; this wouldn't be simple but I imagine it could be done. You might use NetworkOwnership to the client, which means the client script would be able to manipulate it to some degree from then on - certain changes would still have to go through the server, however). On the plus side, the server wouldn't be running any custom scripts and so couldn't get a memory leak (assuming your scripts don't have one). Further, the server could keep track of which clients created/modified which parts. Another advantage is that no one can kill other players or remove the Baseplate (if you script it correctly) since you can simply make sure that the proposed change from the client doesn't violate the list of rules you set up for it (ex no touching player characters and no touching the Baseplate).