更有效地避免字符缓冲区溢出
avoiding char buffer overflow more efficiently
我写了一个简单的in/out程序
每当我 运行 它并输入输入并超过我得到的字符限制时
*** stack smashing detected ***: terminated Aborted (core dumped)
我搜索了一下,发现它是 gcc 的安全问题,我听说它可能会导致段错误,所以我尝试用 -fno-stack-protector
将其关闭,如果我超过 运行 通常字符限制
但是如果我想在输入长度未知的情况下编写程序怎么办,有没有更安全的方法呢?将 char 中的值增加到离谱的大值更有效?
代码:
#include <stdio.h>
int main()
{
char in[1];
printf("in: ");
scanf("%s[=10=]", &in);
printf("\nout: %s\n", in);
}
P.s- 我是 C 语言的新手,已经超过 2 天了,所以如果能提供一个简单的解释,我们将不胜感激
char in[1];
只能容纳空字符串(单个空终止字节),即 impossible to use safely with scanf
.
另请注意,在字符串文字中显式声明空终止字节是多余的,因为它们隐式以空终止。
but what if i want to write the program if the input length is unknown, is there a safer way to do this? more efficient that increasing the value in char to an ridiculously large value?
这里的counter-questions是:
- 您认为什么是低效的?
- 您将什么定义为大得离谱?
在我看来,您有两个选择:
- 使用动态分配的内存读取任意大小的字符串。
- 为期望的输入长度设置一个现实的上限。
#1 的示例可以在 POSIX getline
or getdelim
等库函数中看到。它的re-implementation可以简单到malloc
(realloc
),getchar
,一个循环
#2 的使用在很大程度上取决于程序的上下文以及它应该做什么。也许你只读一行,一个小的缓冲区就足够了。也许您期望更大的数据块,并且需要更多的内存。只有你自己才能决定。
无论如何,您可以通过在溢出发生之前防止溢出来避免undefined behavior。发生了就晚了。
使用%s
时使用field-width specifiers:
char buf[512];
if (1 != scanf("%511s", buf))
/* read error */;
或使用像 fgets
这样的合理函数,它允许您将缓冲区大小作为参数传递。
stack smashing detected
i searched it up and found it was a gcc thing for safety
这确实是 gcc 通过插入 so-called“堆栈金丝雀”来发现堆栈 corruption/overflows 来发现代码中的 run-time 错误的方法。检测到更多错误是一件好事。
i heard it might lead to seg faults
不,您的应用程序中的错误会导致段错误。如果编译器提供在 OS 之前检测它们的方法,那是一件好事。程序中潜在但严重的错误是一件坏事。然而,OS 也可能会检测到错误并说“seg fault”。
so i experimented turning it off with -fno-stack-protector and it ran normally if i exceeded the char limit
基本上你就知道自己没经验driver怕撞到别的车。为了解决这个问题,你改为闭着眼睛开车,这样你就不会看到那些你可能撞到的车。这并不意味着它们消失了。
char in[1];
只能容纳 1 个字节的数据,如果您越界读取该数组,则会调用未定义的行为,这将表现为堆栈粉碎或段错误。因为你正试图写入不属于你的内存。这就是错误,这就是问题所在。正确的解决办法是分配足够的内存。
(您还有一个错误 scanf("%s[=11=]", &in);
-> scanf("%s[=12=]", in);
。不需要 &,因为 in
是一个数组并自动“衰减”为指向其第一个的指针将元素传递给函数时。)
一个明智的方法是分配128字节左右,然后限制输入,使其不能超过128字节。读取输入长度受限的字符串的正确函数是 fgets
。所以你可以切换到 fgets
或者你可以接受你的初学者试用程序不需要达到生产质量并且现在只使用 scanf
。 (您可以安全地使用 scanf
,如另一个答案所示,但在我看来,这比使用 fgets
更麻烦。)
此外,我强烈建议 C 初学者不要担心他们分配 10 个字节还是 100 个字节。使用PC学习编程,然后就没有关系了。优化内存消耗是一个高级主题,您将在后面学习。
我写了一个简单的in/out程序
每当我 运行 它并输入输入并超过我得到的字符限制时
*** stack smashing detected ***: terminated Aborted (core dumped)
我搜索了一下,发现它是 gcc 的安全问题,我听说它可能会导致段错误,所以我尝试用 -fno-stack-protector
将其关闭,如果我超过 运行 通常字符限制
但是如果我想在输入长度未知的情况下编写程序怎么办,有没有更安全的方法呢?将 char 中的值增加到离谱的大值更有效?
代码:
#include <stdio.h>
int main()
{
char in[1];
printf("in: ");
scanf("%s[=10=]", &in);
printf("\nout: %s\n", in);
}
P.s- 我是 C 语言的新手,已经超过 2 天了,所以如果能提供一个简单的解释,我们将不胜感激
char in[1];
只能容纳空字符串(单个空终止字节),即 impossible to use safely with scanf
.
另请注意,在字符串文字中显式声明空终止字节是多余的,因为它们隐式以空终止。
but what if i want to write the program if the input length is unknown, is there a safer way to do this? more efficient that increasing the value in char to an ridiculously large value?
这里的counter-questions是:
- 您认为什么是低效的?
- 您将什么定义为大得离谱?
在我看来,您有两个选择:
- 使用动态分配的内存读取任意大小的字符串。
- 为期望的输入长度设置一个现实的上限。
#1 的示例可以在 POSIX getline
or getdelim
等库函数中看到。它的re-implementation可以简单到malloc
(realloc
),getchar
,一个循环
#2 的使用在很大程度上取决于程序的上下文以及它应该做什么。也许你只读一行,一个小的缓冲区就足够了。也许您期望更大的数据块,并且需要更多的内存。只有你自己才能决定。
无论如何,您可以通过在溢出发生之前防止溢出来避免undefined behavior。发生了就晚了。
使用%s
时使用field-width specifiers:
char buf[512];
if (1 != scanf("%511s", buf))
/* read error */;
或使用像 fgets
这样的合理函数,它允许您将缓冲区大小作为参数传递。
stack smashing detected
i searched it up and found it was a gcc thing for safety
这确实是 gcc 通过插入 so-called“堆栈金丝雀”来发现堆栈 corruption/overflows 来发现代码中的 run-time 错误的方法。检测到更多错误是一件好事。
i heard it might lead to seg faults
不,您的应用程序中的错误会导致段错误。如果编译器提供在 OS 之前检测它们的方法,那是一件好事。程序中潜在但严重的错误是一件坏事。然而,OS 也可能会检测到错误并说“seg fault”。
so i experimented turning it off with -fno-stack-protector and it ran normally if i exceeded the char limit
基本上你就知道自己没经验driver怕撞到别的车。为了解决这个问题,你改为闭着眼睛开车,这样你就不会看到那些你可能撞到的车。这并不意味着它们消失了。
char in[1];
只能容纳 1 个字节的数据,如果您越界读取该数组,则会调用未定义的行为,这将表现为堆栈粉碎或段错误。因为你正试图写入不属于你的内存。这就是错误,这就是问题所在。正确的解决办法是分配足够的内存。
(您还有一个错误 scanf("%s[=11=]", &in);
-> scanf("%s[=12=]", in);
。不需要 &,因为 in
是一个数组并自动“衰减”为指向其第一个的指针将元素传递给函数时。)
一个明智的方法是分配128字节左右,然后限制输入,使其不能超过128字节。读取输入长度受限的字符串的正确函数是 fgets
。所以你可以切换到 fgets
或者你可以接受你的初学者试用程序不需要达到生产质量并且现在只使用 scanf
。 (您可以安全地使用 scanf
,如另一个答案所示,但在我看来,这比使用 fgets
更麻烦。)
此外,我强烈建议 C 初学者不要担心他们分配 10 个字节还是 100 个字节。使用PC学习编程,然后就没有关系了。优化内存消耗是一个高级主题,您将在后面学习。