Reputation: 26888
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.
Upvotes: 0
Views: 1690
Reputation: 121
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)...
$
Upvotes: 1
Reputation: 25129
As @falsetru noted, the system call you want to run as another user is setrlimit
or from the command line su
or sudo
.
If you want to limit resources, you want to use the setrlimit
system call or ulimit
from the shell. That will limit memory usages etc., but not total run duration - you'll have to keep track of the process and kill
it if you want that.
You might also look at nice
to set its priority.
Upvotes: 1
Reputation: 369274
Use seteuid(2)
system call; sets the effective user ID of the calling process.
Following is Ruby example (See Process::Sys.seteuid
)
Process.uid # => 0
Process.euid # => 0
Process::Sys.seteuid(1000) # Etc::getpwnam('falsetru').uid == 1000
Process.uid # => 0
Process.euid # => 1000
Upvotes: 2