简体   繁体   中英

How to limit per-request memory usage in ASP.NET?

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.

.NET CORE and .NET 5

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.

How to measure memory usage for a separate process

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.

Alternative approach

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM