如何提升C++课程自动评分器可靠性,防止学生提交代码作弊
提升自动评分器可靠性的低成本方案
以下方法无需大幅改动现有Docker、编译脚本流程,落地成本极低,可覆盖绝大多数常见作弊场景:
- 隔离标准输出流,切断学生伪造成绩的通道
这是最立竿见影的实现:在你提供的主程序最开头,提前把真正关联系统标准输出的流对象保存下来,再把全局std::cout重定向到内存缓冲区。示例代码:
这种方式下,哪怕学生在func里直接写// 主程序入口第一行执行 #include <sstream> // 保存真正绑定系统stdout的流,只有评分逻辑能用这个对象输出 std::ostream& real_stdout = std::cout.rdbuf(); // 把全局cout重定向到内存空缓冲区:学生代码不管往cout打什么,都会被存在内存里,根本碰不到真正的系统输出 std::ostringstream student_output_holder; std::cout.rdbuf(student_output_holder.rdbuf()); // ... 原有的test逻辑、grade计算逻辑全部正常编写 // 最后输出成绩时,必须用提前保存的real_stdout,不能直接用std::cout real_stdout << "Grade: " << grade << std::endl;std::cout << "Grade: 100"; exit(0);,打印的内容只会被存到内存缓冲区,根本不会传到容器的真实标准输出;如果学生直接调用exit提前终止程序,主程序的成绩输出语句根本不会执行,评分脚本读不到任何成绩行,会直接判0分,完全拿不到伪造的满分。 - 给成绩输出加动态随机校验标记
每次批量编译作业的时候,由脚本生成一个6-8位的无规律随机字符串,作为宏传入主程序,让评分器输出的成绩行必须带这个串。示例流程:- bash脚本编译时生成随机串:
SECRET=$(head -c 8 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9') - 编译时把这个串作为宏传给主程序:
g++ -DGRADE_SECRET=\"$SECRET\" main.cpp func.cpp -o grader - 主程序输出成绩时带上这个串:
real_stdout << GRADE_SECRET << " Grade: " << grade << std::endl; - 评分脚本抓最后一行输出时,必须匹配到这个提前生成的随机串才算有效输出,否则直接判无效。
这个随机串每次编译都会变,学生根本没法提前知道值是什么,哪怕想办法绕过了输出重定向,伪造出来的成绩行没有这个随机标记,也通不过校验。
- bash脚本编译时生成随机串:
- 编译阶段拦截越权操作
在bash编译流程里加两个简单检查,几乎零成本:- 检查学生提交的代码里有没有自定义
main函数:如果学生提交的目标文件符号表里出现main符号,直接中断编译,不让学生有机会绕过你写的测试流程跑自己的伪造程序。 - 链接阶段用
--wrap参数把学生代码对exit、system等可以直接终止流程、执行外部命令的函数的调用,替换成空的占位实现。学生哪怕想调用exit直接退程序拿成绩,也根本无法触发程序终止。
- 检查学生提交的代码里有没有自定义
- 收紧Docker运行的权限边界
你现在已经用Docker做隔离了,只要启动容器的时候加两个简单参数:把容器的标准输出通过只读管道传给评分脚本,不允许学生进程直接写入管道的写端,从系统层面堵死学生代码直接往评分脚本读的通道写内容的可能。
内容的提问来源于stack exchange,提问作者Erel Segal-Halevi
相关产品推荐
相关产品推荐

