How would you design and write a system that takes some C# code and runs it "in the browser"?
In general, my answer would be: Roslyn. Roslyn was already quite hot and mature at the end of 2014; having something like scriptcs
would give you complete control on each line of code you are going to execute.
But this particular project, being something that must work for StackOverflow, had several constraints, most of which were in stern contrast one with the other:
- High fidelity: if I am asking a question about a peculiar problem I am having with C# 1 on .NET 1.1, I want my "snippet" to behave as if it is compiled with C# 1 and run on .NET CLR 1.1
- Safe: can you just compile and execute your snippet inside your IIS? Mmmm.. not a great idea...
- High performance: can you spin up a VM (or a container), wait for it to be ready, "deploy" the snippet, execute it, get it back? That would be very safe, but a bit slow.
Safety/security is particularly important. For example: you do not want users to use WMI to shutdown the machine, or open a random port, install a torrent server, read configuration files from your machine, erase files...
For safety, we want to be able to handle dependencies in a sensible way. Also, some assemblies/classes/methods just do no make any sense in this scenario: Windows Forms? Workflow Foundations? Sql?
For safety and performace, we want to monitor and cap resource usage (no snippet that does not terminate).
Going a deep further, I stared to dash out some constraints. It turns out that we need to disallow something, even if this means going againt the goal of "high-fidelity":
- no "unsafe", no pointers
- no p/invoke or unmanaged code
- nothing from the server that runs the snippet is accessible: no file read, no access to local registry (read OR write!)
- no arbitrary external dependency (assemblies): whitelist assemblies
Also, we need control over some "resources". We cannot allow snippets to get a unlimited or uncontrolled amount of them.
- limit execution time
limit kernel objects
- per process/per thread?
- running time/execution time
limit process creation (zero?)limit memory usagelimit file usage (no files)limit network usage (no network)
- thread creation (avoid "fork-bombs")
- limit other too? Events, mutexes, semaphores...
- deny (or handle in a sensible way) access to named kernel objects (e.g. named semaphores.. you do not want some casual interaction with them!)
limit output (Console.WriteLine, Debug.out...)
- in the future: virtual network, virtual files?
- and of course redirect it
Does it sounds familiar? For me, it was when I learned about something called cgroups
. Too bad we don't have it in windows! Yes, there are Job Objects
, but they do not cover every aspect.
Could we have cgroups-like control for .NET applications?