什么是堆栈粉碎,我该如何解决?

What is stack smashing and how do I fix it?

该程序的目的是通过测试其与前 11 个素数整数的整除性来确定 1 到 1000 之间的数是否为素数。该程序在大多数输入下都能正常运行。但是,当我输入一个整数(例如 468)时,检测到堆栈粉碎。什么是堆栈粉碎以及如何解决该问题?

我尝试研究堆栈粉碎,但找不到与我的程序相关的具体示例。我不知道我可以尝试修改程序的替代方法,因为我对使用 C 编程还比较陌生。

char divStatement[] = " is divisible by ";

if ((userInput % 31) == 0) {
    div31 = true;
    strcat(divStatement, "31, ");
}

if (div2 || div3 || div5 || div7 || div11 || div13 || div17 || div19 || div23 || div29 || div31) {
        divStatement[strlen(divStatement) - 2] = '.';
        divStatement[strlen(divStatement) - 1] = '\n';
        printf("%d%s", userInput, divStatement);
        printf("%d is not prime.\n", userInput);
}
else {
        printf("%d is prime.\n", userInput);
}

输出实际上工作正常。但是,在程序结束时,终端输出:

***stack smashing detected ***: ./a.out terminated 
Aborted
char divStatement[] = " is divisible by ";

if ((userInput % 31) == 0) {
    div31 = true;
    strcat(divStatement, "31, ");
}

divStatement 是一个 char 数组,大小刚好足以容纳 " is divisible by " 加上一个 [=14=] 终止符。您不能将 "31, " 附加到它,因为它没有任何额外的余量 space.

一个简单的解决方法是为数组指定一个明确的长度,该长度足以处理您的程序可能追加的任何内容。

char divStatement[1000] = " is divisible by ";

这不是您问题的答案,而是您应该提出的问题的部分答案。我太累了想出一个正确的解释。但是您的代码中某处需要这样的东西:

if ((userInput % n) == 0)
  {
    printf("%d is divisible by %d\n", userInput, n);
    is_prime == 0;
  }

拥有所有 11 个第一个素数的数组可能也是一个好主意:

const int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};

我不会通过告诉您如何将其包装在一个循环中以及如何 declare/initialize nis_prime 来破坏所有 7 个乐趣。享受你的家庭作业。我希望截止日期不会太早:-)

Stack Smashing 的定义,发布于:https://www.techopedia.com/definition/16157/stack-smashing

定义 - Stack Smashing 是什么意思?

*Stack smashing 是一种漏洞形式,其中计算机应用程序或 OS 的堆栈被迫溢出。这可能会导致颠覆 program/system 并使其崩溃。

栈是一种先进后出电路,是一种缓冲区,用于在其中保存操作的中间结果。为简化起见,堆栈粉碎将比堆栈容量更多的数据放入堆栈中。熟练的黑客可以故意将过多的数据引入堆栈。过多的数据可能存储在其他堆栈变量中,包括函数 return 地址。当函数 returns 时,它会跳转到堆栈上的恶意代码,这可能会破坏整个系统。堆栈上的相邻数据受到影响并迫使程序崩溃。*

关于:

char divStatement[] = " is divisible by ";

strcat(divStatement, "31, ");

strcat() 的调用试图将字符串“31,”附加到一个数组,该数组的大小仅足以容纳字符串:“可被”整除。结果是数组溢出了。这种溢出是未定义的行为。在这种情况下,它破坏了堆栈,可能就在某些堆栈框架或其他链接所在的位置

由于 memory protection mechanisms used by the compiler. You could disable this feature but this would lead to vulnerabilities that are exploited by the so called stack buffer overflow attack (see this computerfile's video on youtube),返回堆栈粉碎错误。

您没有声明具有特定大小的字符数组,而是因为您使用 string literal the size of the divStatement character array is set to 18 bytes (17 for the characters in " is divisible by " and 1 for the "[=12=]", the so-called null character used for string termination). From the C99 standard 6.4.5/5 "String Literals - Semantics" (as quoted in an answer to this question):

对其进行了初始化

The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence.

粗略地说,编译器知道在内存中应该只为这个特定的字符数组保留 18 个字节,因此写入数组不应修改第 18 个字节之后的数据(出于安全原因)。当您连接字符串时,您实际上是在尝试写入超过 18 个字节,这会触发安全机制,进而导致错误。

由于您只测试 1 到 1000 之间的数字,并且仅测试前 11 个质数,因此您知道质数的长度最多为 3 个字符。与 ", " 一起添加额外的 5 个字符,因此总共 18 + 5 = 23 个字符。因此,通过将 divStatement 显式声明为大小为 23 的字符数组,即 divStatement[23] = " is divisible by ",您的问题很容易解决。至于获得对您的代码的更一般的反馈,您应该查看 code review community.