Reputation: 28749
I have a multi-tenant .NET 5 application, running in ASP.NET, in which a request can run arbitrary code and use an arbitrary amount of memory. How can I protect against users using too much memory, on a per-request basis?
I'm aware that I can limit per-process, and also that I can limit the size of a HTTP request being made, but that's not what I need. I need a way that if a user runs code like int array = int[1,000,000,000,000,000,000,000]
they will get an exception rather than the entire site coming down.
Edit: this is a jsfiddle style application (specifically, it's darklang.com), though with a custom language and runtime, so I have pretty good control over the runtime.
Upvotes: 2
Views: 1555
Reputation: 38784
You need to use a process (within a job object or cgroup), there's no way to do this at the application level in .NET.
Upvotes: 1
Reputation: 2340
Because the question is about .Net Core I must include why going AppDomain route won't work.
App Domains
Why was it discontinued? AppDomains require runtime support and are generally quite expensive. While still implemented by CoreCLR, it’s not available in .NET Native and we don’t plan on adding this capability there.
What should I use instead? AppDomains were used for different purposes. For code isolation, we recommend processes and/or containers. For dynamic loading of assemblies, we recommend the new AssemblyLoadContext class.
Source: Porting to .NET Core | .NET Blog
This leaves us only one way to do this if you want to have "automatic" information about memory usage.
To measure other process memory we can use the Process
handle and get its WorkingSet
, PrivateMemory
, and VirtualMemory
. More about memory types
The code to handle another process is quite simple.
private Process InterpreterProcess;
// Run every how often you want to check for memory
private void Update()
{
var workingSet = InterpreterProcess.WorkingSet64;
if(workingSet > Settings.MemoryAllowed)
{
InterpreterProcess.Kill(true);
}
}
private void Start()
{
InterpreterProcess = new Process(...);
// capture standard output and pass it along to user
Task.Run(() =>
{
Update();
Thread.Sleep(50);
// This will be also convenient place to kill process if we exceeded allowed time
});
}
This, however, leaves us with a very important question, since we might allow users to access system critical resources - even if we do not run Administrator privileges on the Process.
Since you mentioned that you have a custom interpreter it might be easier for you to add memory management, memory counting, and security to the interpreter.
Since we can assume that memory allocation is only made with new
your interpreter needs to just test the size of each new allocation and test accordingly.
To test managed object size you need to "analyze" created instance and use sizeof()
on each simple type, a proper way is in the making There's no way to get the managed object size in memory #24200
Upvotes: 1