替换弃用的 gets()

Replace deprecated gets()

我正在使用 CMU-Cambridge 的 SLM 工具包对语言数据进行一些基线语言建模,但是当我 运行 构建的可执行文件之一时,我的系统在尝试执行一个时检测到缓冲区溢出命令的数量。

基于 this Whosebug question I noticed that __gets_chk+0x179 caused the problem, and I've found two occurrences of gets/fgets in the source code (evallm.c, also available in this GitHub project someone made) 但我不知道如何以 proper/secure 方式修复它们。

错误信息的相关部分:

*** 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() 的语法,所以我很难找到正确的参数。

在您的特定情况下,您知道 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] = '[=12=]`;

(如评论所述,您可能不太经常清除 input_string,例如在开始时和 fgets 失败时)

注意 getline(3) is POSIX specific and is managing a heap-allocated buffer. Read about C dynamic memory allocation. If you are unfamiliar with C programming, that might be tricky to you. BTW, you could even consider using the Linux specific readline(3)

主要是你对C编程的熟悉程度。

注意:在 C 中,# 不是开始注释,而是 preprocessor 指令。

您将 gets 替换为 fgets

几乎就这么简单,区别(除了参数)是 fgets 缓冲区末尾可能有一个换行符。 (请注意,我说 可能 在那里。)

我推荐this fgets reference