Pregunta

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.

¿Fue útil?

Solución 2

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)...
$

Otros consejos

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

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top