首先解释下什么是AJC,其实就是alxwvj_judge_core。vj系统本身设计成可以支持多种oj,所以又一个目录下专门放置各个oj的各种采集模块。ajc其实也是写成这种结构的一个模块,只不过它是一个本地的oj模块。没错,vj
+ ajc秒变oj。
这里我借鉴了几个开源项目。其一是
,一个用python写的oj核心,所以其实我大部分时间是在处理逻辑问题而不是处理安全性问题。其二就是 的源码,因为基本现在大部分学校的校内oj都是基于hustoj搭建的。
这里简单说几下oj核心的实现流程:
- 检测数据库提交的Record并且入队列并更新数据库状态为waiting
- 多线程worker从队列取任务
- 取到任务的worker取相应代码和数据,编译执行对比结果
- worker更新相应数据库状态
其中比较关键的两个问题就是多线程和评测安全性:
- 直接使用python2的多线程,受限制于GIL,此部分代码移植自
- 安全性问题,原来的acmjudger里面解决的不好。查看文档发现lo-runner提供了文件白名单和系统调用白名单。参考了hustoj的白名单(写的太乱Orz...),最后才用了这份白名单(32位-static编译):
| sys_call | id |
|:----------------:|:------:|
| name | 122 |
| brk | 45 |
| set_thread_area | 243 |
| readlink | 85 |
| access | 33 |
| fstat64 | 197 |
| mmap2 | 192 |
| exit_group | 252 |
这份名单其实很好采集,更改lo-runner的源码syscalls拦截部分,一律放行并且printf(),调用的时候开启白名单即可,如图:
github代码中的白名单是混合了32位和64位的表只保证两张架构下能够运行但是安全性会有问题!需要具体更改!
直接放地址吧:
贴上编译的核心部分,超时自动结束防止 #include</dev/null> 等卡编译的操作:
p = subprocess.Popen(
build_cmd[language],
shell=True,
cwd=dir_work,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
kill_proc = lambda p: p.kill()
timer = threading.Timer(5, kill_proc, [p])
try:
timer.start()
out, err = p.communicate() # 获取编译错误信息
finally:
timer.cancel()
if p.returncode == 0: # 返回值为0,编译成功
return True