我正在尝试为我正在教授的一门C++课程写一个自动评分程序。通常,自动评分器使用(输入,输出)对:提交的程序从标准输入读取并写入标准输出,评分器将其与预期输出进行比较。但我希望我的学生练习特定的C++结构(而不是用C编写程序),所以测试是用C++编写的。在一个简单的示例中,我给他们提供了一个主程序,如下所示:
#include "func.hpp"
...
int main() {
test(func(1)==2);
test(func(2)==33);
...
/* some 100 tests, including some randomized tests */
...
cout << "Grade: " << grade << endl;
}
学生必须提交文件func.hpp
和func.cpp
(对于更复杂的作业,需要更多的文件)。
有一个bash
脚本,它用func.cpp
编译main.cpp
,运行程序,并从最后一行读取成绩(它在docker
容器中运行,以防止无意中损坏主机)。
问题是,学生可以在func
中打印“成绩: 100”并退出。
有没有一种简单的方法可以让评分器更可靠?
发布于 2021-03-12 10:42:40
我不想为此发疯,但如果你想让它安全,你应该把学生作业放在所谓的“洁净室”里。这是一个乏味的练习,但一旦完成,它将是非常安全的。您说您在docker容器中运行,所以我将在一个子进程中运行学生的作业,并将IO重定向(重定向到管道、文件或/dev/null,这取决于您的一些作业是否涉及控制台输出)。这样一来,一个聪明的学生可以打印他们想要的任何东西,但他们只会把它传给你,而不是打印到控制台。我不打算深入到这方面的代码-我在Stack Overflow上看到了很多我喜欢的例子,通过搜索"fork with redirected stdout“,这是一个典型的forumula,你可能知道。
在伪代码中,它看起来像这样:
main() {
<tedious setup for stdin/stdout/stderr redirection>;
int ch = fork();
if (ch == 0) {
test(func(1)==2);
test(func(2)==33);
.
.
// test clearly generates "grade" so use it as an exit code as an
// easy way to return the information you want
exit(grade);
} else {
for (;;) {
<wait on and read stdout and stderr and do whatever you want to it - it won't go to the console, so no dirty tricks.>;
<you could analyze the student's output as part of grading, or pass it to the console with a highlight, so you know it's not your grade output>
<wait on ch to exit, get the exit code, display it as the grade and break>;
<generate some input if you want to have an assignment where the students read from stdin>;
<add a time-out on waiting for the child to exit. That way evil student can't just hang or busy-loop, whether on purpose by accident.>;
<if it seems to be hanging, you have to process id and can kill it.>;
}
}
}
显然,上面的内容是粗略的。当您循环管理子进程时,您必须同时使用select或类似的工具检查所有内容,但您听起来像是一个相当高级的工程师。你得到了基本的概念。如果您将学生的运行时环境放在您管理的子进程中,那么他们将无法逃脱任何惩罚。此外,它还为您提供了一个平台,可以在您认为合适的任何地方提取其代码的字符串。
发布于 2021-03-09 14:39:10
很简单。当您进行分析时,只需使用不同于您提供给学生的main
作为参考程序。您的版本会打印您的密钥。
int main() {
test(func(1)==2);
test(func(2)==33);
...
/* some 100 tests, including some randomized tests */
...
cout << "The student doesn't know my version prints this next line" << endl;
cout << "Secret validation code: NCC-1701" << endl;
cout << "Grade: " << grade << endl;
}
聪明的学生如果按照您的建议执行(打印100并退出),就不会知道您的版本打印和脚本验证的秘密消息。
真实的故事。当我在教网络课的时候,有一个家庭作业要用UDP套接字实现一个“停止并等待”协议。我的代码实现了“客户端”。学生版本实现了“服务器”。一个学生没有让网络调用工作,但他足够聪明,只在print语句之间使用睡眠调用来打印预期的输出。如果我没有先停止我的客户端程序,并注意到他的程序仍在打印我们正在接收的“传入”数据行,他可能会逃脱惩罚。
https://stackoverflow.com/questions/66541909
复制相似问题