我正在为在我的大学 LAN 上进行类似 ACM-ICPC 比赛的在线评委工作。 为此,我要求法官可能足够安全,以防止恶意程序在我的服务器上自行执行。 (此类程序的一个示例是)
int main(){
while(1) fork();
}
让我们调用这个程序的可执行文件测试代码。
这个程序会导致我运行判断的服务器卡住。 显然我不希望这种情况发生。所以为了防止我尝试使用 ptrace。我想出了以下代码:(让我们调用这段代码的可执行文件 monitor)
#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( &);
if ( WIFEXITED( amp ) ) break;
//ptrace( PTRACE_SINGLESTEP ,pid, 0, 0 );
if(insyscall==0){
ptrace( PTRACE_GETREGS, pid, 0,®s );
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(&);
ptrace( PTRACE_SYSCALL, pid, 0, 0 );
// puts("Here");
//ptrace(PTRACE_CONT, pid, 0, 0);
}
}
return 0;
}
这段代码在阻止可能导致问题的系统调用方面效果很好。但是当要监视的代码在循环中包含 fork 调用时,如 testcode 机器会因为抖动而死机。我的原因可以看出,当原始进程被监视器代码杀死时,它的子进程幸存下来并继续携带 fork 炸弹。 如何修复监控代码,使其部署成功?
PS:可移植性不是问题。我正在寻找特定于 Linux 的答案。
编辑: 在调用 exec 之前,我使用 setrlimit 将子进程的最大数量设置为 0。到目前为止,这似乎是一个很好的解决方案。不过如果监视器代码中仍然存在漏洞,那么很高兴听到社区的消息。
最佳答案
您可以停止子进程并跟踪新进程:
PTRACE_O_TRACEFORK (since Linux 2.5.46) Stop the child at the next fork(2) call with
(SIGTRAP | PTRACE_EVENT_FORK << 8)
and automatically start tracing the newly forked process, which will start with a SIGSTOP. The PID for the new process can be retrieved with PTRACE_GETEVENTMSG.
您可以有一个包含这些更改的工作示例:
/* ... */
ptrace(PTRACE_SETOPTIONS,pid,NULL, PTRACE_SYSCALL | PTRACE_O_TRACEFORK) ;
while (1) {
printf("Waiting\n");
pid = wait(&);
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, ®s);
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);
}
关于c - 如何防止一个进程产生更多的 child ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9647487/