6

私は、大学の LAN で ACM-ICPC のような競技を実施するためのオンライン審査員に取り組んでいます。そのためには、サーバー上で悪意のあるプログラムが実行されないように、裁判官が十分に安全である必要があります。(そのようなプログラムの例は次のようになります)

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

このプログラムtestcodeの実行可能ファイルを呼び出しましょう。

このプログラムは、ジャッジを実行しているサーバーをフリーズさせます。明らかに、私はそれが起こることを望んでいません。それを防ぐために、ptraceを使用してみました。次のコードを思いつきました:(このコードモニターの実行可能ファイルを呼び出しましょう)

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

このコードは、問題を引き起こす可能性のあるシステム コールをブロックするのに非常にうまく機能します。しかし、監視対象のコードに、テストコードのようなループ内の fork 呼び出しが含まれている場合、マシンはスラッシングのためにフリーズします。元のプロセスが強制終了されている間、私が理解できる理由はのとおりです。モニターコードによって、その子は生き残り、フォーク爆弾を運び続けます。正常にデプロイできるようにモニター・コードを修正するにはどうすればよいですか?

PS: 移植性は問題ではありません。Linux 固有の回答を探しています。

編集: exec を呼び出す前に子プロセスの最大数を 0 に設定するために setrlimit を使用しました。

4

2 に答える 2

1

子プロセスを停止して、新しいプロセスを追跡できます。

PTRACE_O_TRACEFORK (Linux 2.5.46 以降) 次の fork(2) 呼び出しで子プロセスを停止し(SIGTRAP | PTRACE_EVENT_FORK << 8)、新しく fork されたプロセスのトレースを自動的に開始します。これは SIGSTOP で開始されます。新しいプロセスの PID は、PTRACE_GETEVENTMSG で取得できます。

これらの変更を加えた実際の例を次に示します。

/* ... */
  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);
  }

参照

于 2012-03-10T15:22:37.860 に答える
0

あなたがしていることについては、seccompを調査することを強くお勧めします。これにより、プロセスが、、 (すでに開いているファイル記述子のみ)、およびexitread以外のシステムコールを使用できないようにすることができます。writesigreturn

于 2012-03-11T07:53:42.653 に答える