替换已弃用的gets()

时间:2016-11-17 14:02:09

标签: c getline buffer-overflow gets

我正在使用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()的语法,所以我很难找到合适的参数。

2 个答案:

答案 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. } ,缓冲区的末尾可能会有换行符。 (注意我说可能在那里。)

我建议this fgets reference