Is there a programmatically way to limit duration, memory usage and run as less privileged user of a Linux program execution in C/C++ or Ruby ?
since system or `` can't do this.
sprintf(cmd_str,"/tmp/pro-%d < /tmp/in.txt > /tmp-%d.txt",id,id);
system(cmd_str); // in C
`/tmp/pro-#{id} < /tmp/in.txt > /tmp/out-#{id}.txt` // in Ruby
both statement makes that command run as the same user as the executor, uses whole processing power and memory as they like.
You'll want to use the setrlimit
syscall to limit memory (Process::RLIMIT_AS
). To limit the runtime of the program, you can only control the total number of seconds a process gets CPU time (so that doesn't account for time spent sleeping or waiting on I/O). That's done with Process::CPU
.
Drop privileges with Process::Sys.setgid
followed by Process::Sys.setuid
after setting these rlimits, but before calling your target process with Process::exec
.
Example target program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ALLOC_SIZE_1 1024
#define ALLOC_SIZE_2 (1024 * 1024 * 5)
int
main(int argc, char *argv[])
{
char *buf;
fprintf(stderr, "[+] uid: %d, gid: %d\n", getuid(), getgid());
fprintf(stderr, "[+] trying to allocate %d bytes (should succeed)...\n", ALLOC_SIZE_1);
if (NULL == (buf = malloc(ALLOC_SIZE_1))) {
fprintf(stderr, "[!] failed!\n");
return -1;
}
fprintf(stderr, "[+] success.\n");
free(buf);
fprintf(stderr, "[+] trying to allocate %d bytes (should fail)...\n", ALLOC_SIZE_2);
if (NULL != (buf = malloc(ALLOC_SIZE_2))) {
fprintf(stderr, "[!] succeeded! (should have failed.)\n");
return -1;
}
fprintf(stderr, "[+] ok. now doing infinite loop (should get killed pretty soon)...\n");
for (;;);
return 0;
}
And accompanying Ruby script to invoke it (run this script as root with, e.g. sudo /tmp/foo.rb
):
#!/usr/bin/env ruby
TARGET_GID = 99
TARGET_UID = 99
Process::setrlimit(Process::RLIMIT_AS, 1024 * 1024 * 5)
Process::setrlimit(Process::RLIMIT_CPU, 3)
Process::Sys.setgid(TARGET_GID)
Process::Sys.setuid(TARGET_UID)
Process::exec('/tmp/test')
And finally, output of running on my machine:
$ sudo ./run.rb
[+] uid: 99, gid: 99
[+] trying to allocate 1024 bytes (should succeed)...
[+] success.
[+] trying to allocate 5242880 bytes (should fail)...
[+] ok. now doing infinite loop (should get killed pretty soon)...
$