如何保證oj的安全
本人在安全方面的知識比較匱乏析校,本文只是對代碼運行時如何保證oj的安全做分析妥凳。
兩種方案
-
ptrace
顧名思義這個函數(shù)時對進程進行跟蹤的啊送,它是提供給父進程一種跟蹤檢查子進程的能力扶踊,包括監(jiān)視子進程的寄存器的值(這也是我用來保證oj安全的一種方法)干发。
-
這是一種容器的概念绞愚,一種新的虛擬化技術(shù)叙甸,但它不是虛擬機,它的啟動是虛擬機無法比的位衩。
它可以保證容器之間互不影響裆蒸,因為本身具有虛擬機的概念,所以糖驴,我們oj的程序可以丟在里面隨便跑僚祷,
即使執(zhí)行rm -rf /*
這種騷操作也不會影響真正的物理機。(但本文所說的不是采用這種方式)
ptrace
-
ptrace 與 gdb
或許說道ptrace讓人很懵(我第一次聽說是這樣)贮缕,但是說到gdb就再熟悉不過了吧辙谜,其實gdb的調(diào)試原理就是基于ptrace的。
首先gdb會和要調(diào)試的程序關(guān)聯(lián)起來感昼,就是建立跟蹤關(guān)系装哆,父進程(gdb)fork出子進程,子進程調(diào)用ptrace設(shè)置第一個參數(shù)為PTRACE_TRACEME
這樣子進程(被調(diào)試的程序)收到的所有信號(不包括SIGKILL)都會被父進程捕獲定嗓,此時父進程就可以去觀察子進程的內(nèi)存蜕琴,寄存器狀態(tài)甚至修改他們。 -
如何使用ptrace保證oj代碼安全執(zhí)行
說到這個問題首先就要講一下系統(tǒng)調(diào)用的原理了宵溅。
-
什么是系統(tǒng)調(diào)用
簡單來說就是給用戶程序提供一種訪問操作系統(tǒng)更高特權(quán)的服務(wù)的能力奸绷,
具體可以移步維基百科-系統(tǒng)調(diào)用 -
系統(tǒng)調(diào)用如何實現(xiàn)的
- 把系統(tǒng)調(diào)用號寫入eax寄存器
- 提供參數(shù)
- 觸發(fā)int 0x80中斷(此中斷就是用于系統(tǒng)調(diào)用的)
- 從eax獲取返回值
明白了系統(tǒng)調(diào)用原理,現(xiàn)在要保證oj的代碼執(zhí)行安全就很容易了层玲,由于所有的函數(shù)調(diào)用最終都會轉(zhuǎn)化到系統(tǒng)調(diào)用上
(讓我想到了計網(wǎng)的everthing over ip, ip over everthing),每個系統(tǒng)調(diào)用都有系統(tǒng)調(diào)用號(具體查看#include <syscall.h>
)辛块,所以我們的oj只需要去跟蹤程序的系統(tǒng)調(diào)用是否合法畔派,從而
保證oj的安全。原理就是:- 和用戶提交的程序建立跟蹤與被跟蹤關(guān)系
- 監(jiān)視系統(tǒng)調(diào)用
- 獲取系統(tǒng)調(diào)用號
- 合法放行润绵,不合法發(fā)送SIGKILL信號
代碼框架:
int fdRes[2]; int ret1 = pipe(fdRes); pid_t pid; int res; pid = fork(); long orig_rax; chdir(workDir.c_str()); if(pid > 0) { close(fdRes[1]); while(1) { waitpid(pid,&res,0); //獲取系統(tǒng)調(diào)用號 orig_rax = ptrace(PTRACE_PEEKUSER,pid,8*ORIG_RAX,NULL); if(orig_rax >= 0 && sysCall[orig_rax] == OK_CALL/*判斷是否合法*/) printf("The child made a system call %ld\n",orig_rax); else if(orig_rax < 0){ break; }else{ cout<<"非法系統(tǒng)調(diào)用:"<<orig_rax<<endl; //非法系統(tǒng)調(diào)用线椰,殺死進程 kill(pid,SIGKILL); break; } //跟蹤系統(tǒng)調(diào)用 ptrace(PTRACE_SYSCALL, pid, NULL, NULL); } char buf[1024]; //讀取程序運行結(jié)果 read(fdRes[0],buf,sizeof(buf)); close(fdRes[0]); runRes = buf; if(res == 0) { this->status = RUN_OK; } else { this->status = RUN_ERROR; } cout<<"運行結(jié)果:"<<runRes<<endl; } else { close(fdRes[0]); freopen("data.in", "r", stdin); //運行用戶程序 todo dup2(fdRes[1], STDOUT_FILENO); dup2(fdRes[1], STDERR_FILENO); //建立跟蹤關(guān)系 ptrace(PTRACE_TRACEME, 0, NULL, NULL); this->doRun(); close(fdRes[1]); exit(0); }
-