我正在使用CMU-Cambridge的SLM工具包对语言数据进行一些基线语言建模,但是当我运行其中一个构建的可执行文件时,我的系统在尝试执行其中一个命令时检测到缓冲区溢出。
基于this StackOverflow question我发现__gets_chk+0x179
导致了问题,我在源代码中发现了两次gets/fgets
(evallm.c,也可用{{}} 3}})但我不知道如何以适当/安全的方式修复它们。
错误消息的相关部分:
*** 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);
....
当我给evallm
命令的 input_string 太长时,基本上发生了错误。通常,它是从命令行调用的,您可以交互式地传递参数。但是,我将所有参数与命令一起管道(如文档示例中所示),但显然有时我的参数名称占用太多字节。当我将 input_string 的数组长度从500更改为2000时,问题就解决了(所以我猜错误是由于出现2)。但我真的想通过将gets()
替换为getline()
来解决这个问题,因为这似乎是正确的方法。或者用fgets()
替换它也是一个解决方案?如果是这样,我应该使用哪些参数?
但是,在尝试替换gets()
时,我总是会遇到编译错误。我不是C程序员(Python,Java),而且我不熟悉getline()
的语法,所以我很难找到合适的参数。
答案 0 :(得分:1)
在您的特定情况下,您知道input_string
是一个500字节的数组。 (当然,您可以用例如2048替换500)
我是偏执狂,擅长防御性编程,我会在任何输入之前将该缓冲区归零,例如
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指令。
答案 1 :(得分:0)
您将fgets
替换为fgets
。
它的几乎那么简单,差异(除了参数)是{
V1: 0,
V2: 0,
V3: 0.
}
,缓冲区的末尾可能会有换行符。 (注意我说可能在那里。)