即使我将字符串大小设置为1,我输入的任何字符串都会被完全打印,为什么会发生这种情况?我认为多余的元素可以忽略。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
int main ()
{
int i;
char * buffer;
printf ("How long do you want the string? ");
scanf ("%d", &i);
buffer = (char*) malloc (i+1);
if (buffer==NULL) exit (1);
printf("n enter string");
scanf("%s",buffer);
printf ("n string: %sn",buffer);
free (buffer);
return 0;
}
因为缓冲区溢出,调用了未定义的行为。任何事情都有可能发生。可能发生的一件事是字符串被完全打印出来。其他可能发生的事情是,您的程序崩溃,或者黑客攻击用户的计算机使用这种未定义的行为来闯入。
这就是为什么初学者会评论缓冲区溢出"如果你运气不好,你的程序会崩溃",而更有经验的程序员会说"如果你运气好,你的程序会崩溃"。你并不幸运。
C不执行边界检查。在数组末尾以外的地方写入只会产生未定义的行为。在您对C运行时的一个特定实现的经验测试中,您的字符串长度产生与正确行为相同的东西。不保证。
(在实践中,在大多数现代架构中:如果你访问一个你绝对没有权利访问的地址,那将引发一个异常;然而,对于这样的检查只有一个广泛的分辨率,malloc
试图紧紧地打包分配的空间,以尽量减少浪费的RAM;假设没有边界检查,当你做数组访问所有发生的是,你正在存储和随后从内存中检索字节你的进程拥有;但不能保证你在一般情况下拥有它,即使你在你的特定架构上拥有它,你很可能会覆盖其他东西;因此,你所做的是依赖于未定义的行为)
除了已经给出的优秀答案之外,下面是一个实际的缓冲区溢出的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
int i;
char * buffer;
static char * oops = "oops!";
printf ("How long do you want the string? ");
scanf ("%d", &i);
buffer = (char*) malloc (i+1);
if (buffer==NULL) exit (1);
char * other_variable = (char*) malloc (strlen(oops) + 1);
printf ("n memory location of buffer: %un", (unsigned int)&buffer[0]);
printf ("n memory location of other variable: %un", (unsigned int)&other_variable[0]);
printf("n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
scanf("%s",buffer);
memcpy(other_variable, oops, strlen(oops) + 1);
printf ("n string: %sn",buffer);
free (buffer);
return 0;
}
示例输出:
How long do you want the string? 1
memory location of buffer: 3671064576
memory location of other variable: 3671064592
enter string without spaces longer than 16 characters>aaaaaaaaaaaaaaaaaaaaaaaaaaa
string: aaaaaaaaaaaaaaaaoops!
相反,下面的示例显示了缓冲区中的缓冲区溢出如何覆盖另一个重要变量:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
int i;
char * buffer;
static char * important_command = "Important Command!";
printf ("How long do you want the string? ");
scanf ("%d", &i);
buffer = (char*) malloc (i+1);
if (buffer==NULL) exit (1);
char * other_variable = (char*) malloc (strlen(important_command) + 1);
memcpy(other_variable, important_command, strlen(important_command) + 1);
printf ("n memory location of buffer: %un", (unsigned int)&buffer[0]);
printf ("n memory location of other variable: %un", (unsigned int)&other_variable[0]);
printf("n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
scanf("%s",buffer);
printf ("n Contents of buffer: %sn", buffer);
printf ("n Contents of other variable (should be '%s'): %sn",important_command, other_variable);
free (buffer);
return 0;
}
示例输出:
How long do you want the string? 1
memory location of buffer: 1766850560
memory location of other variable: 1766850576
enter string without spaces longer than 16 characters>aaaaaaaaaaaaaaaaaaaaaa
Contents of buffer: aaaaaaaaaaaaaaaaaaaaaa
Contents of other variable (should be 'Important Command!'): aaaaaa
现在你可以想象,如果这是一个SQL命令或类似的东西,那么这可能会对应用程序的操作造成灾难性的影响,并会导致重大的安全风险。
你的问题的真正答案是:你应该使用fgets()
函数代替(然后用sscanf()
解析它,如果你需要)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
#define num_length 10
int i;
char * buffer;
buffer = (char*) malloc (num_length);
printf ("How long do you want the string? ");
fgets(buffer, num_length, stdin);
sscanf (buffer, "%d", &i);
free(buffer);
buffer = (char*) malloc (i+1);
if (buffer==NULL) exit (1);
printf("n enter string less than %i characters>", i);
fgets(buffer, i+1, stdin);
printf ("n string: %sn",buffer);
free (buffer);
return 0;
}