2012-03-10 9 views
5

Pracuję nad sędzią online do prowadzenia ACM-ICPC jak konkursy w mojej uczelni LAN. W tym celu wymagam, aby sędzia mógł być wystarczająco bezpieczny, aby zapobiec uruchamianiu się złośliwego oprogramowania na moim serwerze. (Przykładem takiego programu byłoby)Jak zapobiec procesowi powstawania większej liczby dzieci?

int main(){ 
      while(1) fork(); 
      } 

Nazwijmy wykonywalny tego programu testcode.

Ten program spowodowałby zawieszenie mojego serwera z uruchomionym sędzią. Oczywiście ja nie chcę, żeby happen.So aby zapobiec Próbowałem za pomocą ptrace.I wpadł na następujący kod: (Nazwijmy wykonywalnego tego kodu monitora)

#include <sys/ptrace.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/user.h> 
#include <sys/syscall.h> 
#include <sys/reg.h> 
#include<stdio.h> 
#include <sys/ptrace.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/user.h> 
#include <sys/syscall.h> 
#include <sys/reg.h> 
#include<stdio.h> 
#include<signal.h> 
#include<sys/prctl.h> 
#include<stdlib.h> 
#define NOBANNEDSYS 40 
int bannedsys[50]={2,14,12,15,26,37,38,39,39,40,41,42,46,47,48,49,50,60,61,63,72,83,88,120,102,182,183,190}; 

int main(int argc,char **argv) { 
       int insyscall=0; 
       if(argc!=2) { 
        fprintf(stderr,"Usage: %s <prog name> ",argv[0]); 
        exit(-1); 
        } 

    int status = 0; 
    int syscall_n = 0; 
    int entering = 1; 
    int amp; 
    struct user_regs_struct regs; 
    int pid = fork(); 

    if (!pid) { 
    prctl(PR_SET_PDEATHSIG, SIGKILL); 
    ptrace(PTRACE_TRACEME, 0, 0, 0); 
    execlp(argv[1],argv[1], 0); 
    } 

    else { 

    //ptrace(PTRACE_SINGLESTEP ,pid, 0, 0); 
    // ptrace(PTRACE_SYSCALL, pid, 0, 0); 
    while (1) { 

     wait(&amp); 
      if (WIFEXITED(amp)) break; 

       //ptrace(PTRACE_SINGLESTEP ,pid, 0, 0); 

    if(insyscall==0){ 
     ptrace(PTRACE_GETREGS, pid, 0,&regs); 
     int i=0; 
     for(i=0;i<NOBANNEDSYS;i++) if(regs.orig_eax==bannedsys[i]) { kill(pid,SIGKILL); 
           printf("%d killed due to illegal system call\n",pid); 

            abort(); 
            } 

     insyscall=1; 
       } 
     else insyscall=0; 
     // ptrace(PTRACE_CONT,pid,0,0); 
    // wait(&amp); 

    ptrace(PTRACE_SYSCALL, pid, 0, 0); 
    // puts("Here"); 
//ptrace(PTRACE_CONT, pid, 0, 0); 

    } 

    } 

    return 0; 
} 

Ten kod działa całkiem dobrze w blokowaniu wywołań systemowych, które mogą powodować problemy. Ale gdy kod, który ma być monitorowany, zawiera wywołania fork w pętli, jak kod testowy, urządzenie zawiesza się z powodu rzucania. Powodem, dla którego mogłem to stwierdzić, jest to, że oryginalny proces został zabity przez monitor koduje swoje dziecko, które przeżywa i nadal niesie bombę widłową. Jak naprawić kod monitora, aby mógł zostać pomyślnie wdrożony?

PS: Przenośność nie jest problemem. Szukam konkretnej odpowiedzi na Linuksa.

EDYTOWANIE: Użyłem setrlimit do ustawienia maksymalnej liczby procesów podrzędnych na 0 przed wywołaniem exec.To do tej pory wydaje się być dobrym rozwiązaniem.Ale byłoby miło usłyszeć od społeczności, jeśli nadal istnieją luki w kod monitora.

+4

Czy rozważałeś ograniczenie zasobów za pomocą 'setrlimit (2)' i ograniczenie 'możliwości (7)'. RTFM po więcej. –

+0

wydaje się działać ... Ustawiłem liczbę limitów procesowych na 0. – bashrc

Odpowiedz

1

można zatrzymać dziecko i śledzenia nowych procesów:

PTRACE_O_TRACEFORK (ponieważ Linuksa 2.5.46) Zatrzymać dziecko w następnym fork (2) wezwanie z (SIGTRAP | PTRACE_EVENT_FORK << 8) i automatycznie początek śledzenia nowo rozwidlony proces, który rozpocznie się od SIGSTOP . PID dla nowego procesu można pobrać przy pomocy PTRACE_GETEVENTMSG.

Można mieć przykład pracy z tymi zmianami:

/* ... */ 
    ptrace(PTRACE_SETOPTIONS,pid,NULL, PTRACE_SYSCALL | PTRACE_O_TRACEFORK) ; 
    while (1) { 
    printf("Waiting\n"); 
    pid = wait(&amp); 
    printf("Waited %d\n", amp); 
    if (WIFEXITED(amp)) { 
     break; 
    } 
    if (WSTOPSIG(amp) == SIGTRAP) 
    { 
     int event = (amp >> 16) & 0xffff; 
     if (event == PTRACE_EVENT_FORK) { 
     printf("fork caught\n"); 
     pid_t newpid; 
     ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &newpid); 
     kill(newpid, SIGKILL); 
     kill(pid, SIGKILL); 
     break; 
     } 
    } 
    if (insyscall == 0) { 
     ptrace(PTRACE_GETREGS, pid, 0, &regs); 
     int i = 0; 
     for (i = 0; i < NOBANNEDSYS; i++) if (regs.orig_eax == bannedsys[i]) { 
     kill(pid, SIGKILL); 
     printf("%d killed due to illegal system call\n", pid); 

     abort(); 
     } 

     insyscall = 1; 
    } else { 
     insyscall = 0; 
    } 
    ptrace(PTRACE_CONT, pid, NULL, 0); 
    } 

Reference

+0

czy możesz podać jakiś fragment? Nie jestem w stanie prawidłowo korzystać z tych opcji. – bashrc

+0

@ bashrc, zaktualizowano odpowiedź z fragmentem kodu: – perreal

0

Na co robisz, gorąco polecam bada seccomp, który pozwoli Ci uniemożliwić proces od używania wywołań systemowych innych niż exit, read i write (tylko dla już otwartych deskryptorów plików) i sigreturn.

+0

Ale po wywołaniu pctrl (seccomp, 1); nie będę w stanie wykonać procesu przez wywołanie exec ...Aplikacja została zaprojektowana w taki sposób, aby po otrzymaniu zgłoszenia kod sędziowski tworzył plik wykonywalny dla przesłanego kodu i przekazywał nazwę pliku wykonywalnego do kodu monitora. – bashrc

+0

W porządku, to rozwiązanie wymagałoby pewnej restrukturyzacji sposobu kompilacji i wykonania dostarczonego kodu. – duskwuff

Powiązane problemy