1

Numerous websites allow us to build and execute C code from web browsers (repl.it, onlinegdb.com, ideone.com...). For my own application (education purposes) I would like to do the same on my web backend.

My current solution is to use an Alpine Docker container with gcc constrained with ulimits. To avoid mounting files I simply use gcc in stdin/stdout with :

protected $container = "frolvlad/alpine-gcc";
protected $cc = "gcc";
protected $cflags = "--static -std=c99 -Wall -pedantic";

protected $ulimit = [
    'locks' => 10,
    'sigpending' => 100,
    'rttime' => 1,
    'nproc' => 1,
    'nofile' => 50,
    'msgqueue' => 1000,
    'core' => 1,
    'cpu' => 2,
    'fsize' => 1000000,
    'memlock' => 1000,
];

protected function execute($cmd, $args=[], $stdin=null, $env=[])
{
    $descriptorspec = [
        0 => ["pipe", "r"],  // stdin
        1 => ["pipe", "w"],  // stdout
        2 => ["pipe", "w"],  // stderr
    ];

    $cwd = '/tmp';
    $ulimits = $this->getUlimitOptions();
    $docker = "docker run --stop-timeout 1 -i --rm $ulimits $this->container";

    $process = proc_open("$docker $cmd", $descriptorspec, $pipes, $cwd, $env);

    if (is_resource($process)) {
        if ($stdin) {
            fwrite($pipes[0], $stdin);
            fclose($pipes[0]);
        }

        $stdout = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        $stderr = stream_get_contents($pipes[2]);
        fclose($pipes[2]);

        $exit_status = proc_get_status($process)['exitcode'];
    }

    return (object)[
        'stdout' => $stdout,
        'stderr' => $stderr,
        'exit_status' => $exit_status
    ];
}

public function build($code, $args=[])
{
    return $this->execute("$this->cc -xc $this->cflags -o/dev/fd/1 -", $args, $code);
}

The execution is done the same with

public function run($executable, $args=[])
{
    return $this->execute("cat > a.out && chmod +x a.out && timeout 1 ./a.out", $args, $executable);
}

Would this solution be secure enough and what would be the possible improvements?

Of course, the backend API is throttled, and only authenticated users can access the build interface.

Obviously codes such as the following would be still an issue:

#include <stdio.h>
int main() {
    while(1) printf("hello, world!\\n");
}
1

As for the infinity loop scenario you can actually:

  1. Limit the resources granted per container.
  2. Assign the maximum execution time allowed. Optionally kill the container if someone uses 100% of assigned resources for too long (you should monitor the use of assigned processing time).
  3. You can monitor the prcesses in the deployed containers for any strange patterns.

As for the security, it is as safe as your sandbox is, you should be at lookout for any escaping technics and vurnebilities related to that.

5
  • 1
    Limiting the run time to 0.5 seconds already allows a 100MB output. So I check if any output is bigger than a threshold.
    – nowox
    Oct 8 '20 at 19:40
  • Or you can grant a limited memory for any type of output you get.
    – nethero
    Oct 8 '20 at 19:49
  • How do you do that on stdout?
    – nowox
    Oct 8 '20 at 19:59
  • 1
    A ram configuration for container is your best option.
    – nethero
    Oct 8 '20 at 20:06
  • On the topic of #2, One of the very common ways of setting up a SLURM grid is to put a time limit of X minutes, unless the user specifies a larger upper time limit. Now you've got users explicitly stating how much time they think it should need, which is useful information, letting you do proactive resource management rather than reactive management.
    – Cort Ammon
    Nov 22 '20 at 0:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.