我正在使用 CMU-Cambridge 的 SLM 工具包对语言数据进行一些基线语言建模,但是当我运行其中一个内置的可执行文件时,我的系统在尝试执行其中一个命令时检测到缓冲区溢出。
基于this StackOverflow question我注意到__gets_chk+0x179
引起了这个问题,我发现 gets/fgets
出现两次在源代码中(evallm.c,也可用 in this GitHub project someone made ),但我不知道如何以正确/安全的方式修复它们。
错误消息的相关部分:
*** buffer overflow detected ***: /home/CMU-Cam_Toolkit_v2/bin/evallm terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(__gets_chk+0x179)[0x7f613bc719e9]
Aborted
broken code
# declaration of input string variable
char input_string[500];
# occurence 1
...
while (fgets (wlist_entry, sizeof (wlist_entry),context_cues_fp)) { ... }
...
# occurence 2
...
while (!feof(stdin) && !told_to_quit) {
printf("evallm : ");
gets(input_string);
....
错误基本上发生在我将 input_string 提供给 evallm
时。命令太长。通常从命令行调用它,您可以交互地传递参数。但是,我将所有参数与命令一起通过管道传输(如文档示例中所示),但显然有时我的参数名称占用了太多字节。当我将 input_string 的数组长度从 500 更改为 2000 时,问题就解决了(所以我猜错误是由于发生 2 引起的)。但我真的想通过替换 gets()
来修复它通过getline()
因为这似乎是正确的方法。或者将其替换为 fgets()
还有解决办法吗?如果是这样,我应该使用什么参数?
但是,当尝试替换gets()
时,我总是遇到编译错误。我不是 C 程序员(Python、Java),也不熟悉 getline()
的语法,所以我正在努力寻找正确的参数。
最佳答案
在您的特定情况下,您知道 input_string
是一个 500 字节的数组。 (当然,您可以将 500 替换为 2048)
我很偏执,擅长防御性编程,我会在任何输入之前将缓冲区清零,例如
memset(input_string, 0, sizeof(input_string));
因此,即使 fgets
失败,缓冲区也会被清除。在大多数情况下,这原则上是没有用的。但也有一些极端情况,而问题就在于细节。
所以请阅读 fgets(3) 的文档并将 gets
调用替换为
fgets(input_string, sizeof(input_string), stdin);
(您实际上应该处理极端情况,例如 fgets
失败以及输入行长于 input_string
....)
当然,您可能希望将终止换行符归零。为此,添加
int input_len = strlen(input_string);
if (input_len>0) input_string[input_len-1] = '\0`;
(如评论所述,您可能会减少清除 input_string
的频率,例如在启动时和 fgets
失败时)
请注意 getline(3)是 POSIX 特定的并且正在管理堆分配的缓冲区。了解 C dynamic memory allocation 。如果您不熟悉 C 编程,这对您来说可能会很棘手。顺便说一句,您甚至可以考虑使用 Linux 特定的 readline(3)
要点是您对 C 编程的熟悉程度。
注意:在 C 语言中,#
并不开始注释,而是开始 preprocessor指令。
关于c - 替换已弃用的 gets(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40656902/