2012-03-10 48 views
5

Tôi đang làm việc trên một thẩm phán trực tuyến để tiến hành ACM-ICPC như các cuộc thi trên mạng LAN của trường đại học của tôi. Vì lý do đó, tôi yêu cầu thẩm phán có thể đủ an toàn để ngăn các chương trình độc hại thực thi chính mình trên máy chủ của tôi. (Một ví dụ về một chương trình như vậy sẽ)Làm thế nào để ngăn chặn một quá trình sinh sản nhiều đứa trẻ hơn

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

Cho phép gọi thực thi của chương trình này testcode.

Chương trình này sẽ khiến máy chủ của tôi chạy thẩm phán để đóng băng. Rõ ràng tôi không muốn điều đó happen.So để ngăn chặn điều đó tôi đã cố gắng sử dụng ptrace.I đã đưa ra đoạn mã sau: (Cho phép gọi thực thi của mã này màn)

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

Mã này hoạt động khá tốt trong khi chặn các cuộc gọi hệ thống có thể gây ra sự cố.Nhưng khi mã được theo dõi có chứa các cuộc gọi ngã ba trong một vòng lặp như mã kiểm tra máy bị đóng băng do bị đập. Lý do tôi có thể thực hiện là trong khi quá trình gốc bị giết bởi màn hình mã con của nó tồn tại và tiếp tục mang theo quả bom ngã ba. Cách sửa mã theo dõi để có thể triển khai thành công?

PS: Tính di động không phải là mối lo ngại.Tôi đang tìm kiếm câu trả lời cụ thể cho Linux.

EDIT: Tôi đã sử dụng setrlimit để đặt số lượng tối đa các quy trình con thành 0 trước khi gọi exec.This cho đến bây giờ dường như là một giải pháp tốt. Thật tuyệt khi được nghe từ cộng đồng nếu vẫn còn sơ hở trong mã theo dõi.

+4

Bạn có xem xét giới hạn tài nguyên với 'setrlimit (2)' và hạn chế 'khả năng (7)'. RTFM để biết thêm. –

+0

dường như hoạt động ... Tôi đặt số lượng giới hạn quy trình thành 0. – bashrc

Trả lời

1

Bạn có thể ngừng đứa trẻ và theo dõi quy trình mới:

PTRACE_O_TRACEFORK (vì Linux 2.5.46) Dừng trẻ em tại ngã ba tiếp theo (2) gọi với (SIGTRAP | PTRACE_EVENT_FORK << 8) và tự động bắt đầu truy tìm mới được chia hai quy trình, sẽ bắt đầu với SIGSTOP. PID cho quy trình mới có thể được truy xuất với PTRACE_GETEVENTMSG.

Bạn có thể có một ví dụ làm việc với những thay đổi này:

/* ... */ 
    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

bạn có thể vui lòng cung cấp một số đoạn mã không? Tôi không thể sử dụng các tùy chọn này một cách chính xác. – bashrc

+0

@bashrc, đã cập nhật câu trả lời bằng một đoạn mã – perreal

0

Đối với những gì bạn đang làm, tôi sẽ khuyên điều tra seccomp, mà sẽ cho phép bạn để không cho phép một quá trình từ việc sử dụng bất kỳ cuộc gọi hệ thống nào khác ngoài exit, readwrite (chỉ cho các tệp mô tả đã mở) và sigreturn.

+0

Nhưng sau khi gọi pctrl (seccomp, 1); tôi sẽ không thể thực hiện quá trình thông qua lệnh gọi exec ...Ứng dụng này được thiết kế theo cách mà khi nhận được bản trình, mã thẩm phán tạo ra tệp thi hành cho mã được gửi và chuyển tên của tệp thi hành tới mã theo dõi. – bashrc

+0

Phải, giải pháp này sẽ yêu cầu một số cơ cấu lại cách bạn biên dịch và thực thi mã được cung cấp. – duskwuff

Các vấn đề liên quan