Как ограничить время выполнения программы, использование памяти и как конкретного пользователя программно в Linux

Существует ли программный способ ограничения продолжительности, использования памяти и запуска в качестве менее привилегированного пользователя при выполнении программы Linux на C/C++ или Ruby?

так как система или `` не может этого сделать.

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

оба утверждения заставляют эту команду работать от имени того же пользователя, что и исполнитель, используя всю вычислительную мощность и память, как им нравится.

3 ответа

Решение

Вы хотите использовать setrlimit системный вызов для ограничения памяти (Process::RLIMIT_AS). Чтобы ограничить время выполнения программы, вы можете контролировать только общее количество секунд, в течение которых процесс получает процессорное время (таким образом, не учитывается время, потраченное на сон или ожидание при вводе-выводе). Это сделано с Process::CPU,

Отбросьте привилегии с Process::Sys.setgid с последующим Process::Sys.setuid после установки этих ограничений, но перед вызовом целевого процесса с Process::exec,

Пример целевой программы:

#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;
}

И сопровождающий скрипт Ruby для его вызова (запустите этот скрипт с правами root, например, 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')

И наконец, вывод обкатки на моей машине:

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

Использование seteuid(2) системный вызов; устанавливает эффективный идентификатор пользователя вызывающего процесса.

Ниже приведен пример Ruby (см. Process::Sys.seteuid)

Process.uid    # => 0
Process.euid   # => 0
Process::Sys.seteuid(1000)  # Etc::getpwnam('falsetru').uid == 1000
Process.uid    # => 0
Process.euid   # => 1000

Как заметил @falsetru, системный вызов, который вы хотите запустить от имени другого пользователя, setrlimit или из командной строки su или же sudo,

Если вы хотите ограничить ресурсы, вы хотите использовать setrlimit системный вызов или ulimit из скорлупы Это ограничит использование памяти и т. Д., Но не общую продолжительность выполнения - вам придется следить за процессом и kill это если ты этого хочешь.

Вы также можете посмотреть на nice установить свой приоритет.

Другие вопросы по тегам