Как ограничить время выполнения программы, использование памяти и как конкретного пользователя программно в 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
установить свой приоритет.