0

请帮助我理解这个 C 语言示例程序。

#include <stdio.h>
int i;
int *tmp;
void anotherFunction(void);
void destroyStack(int);
void main(void)
{
 anotherFunction();
 fprintf(stderr,
 "We will never reach this far\n");
}
void anotherFunction(void)
{
 destroyStack(4);
 fprintf(stderr,"In another function\n");
}
void destroyStack(int param)
{
 tmp = &param;
 for(i = -200; i < 10; i++)
 /* overwrite part of stack*/
 printf("%d\n",param), tmp[i] = 0;
}

AFAIKtmp是一个指向 int 的指针,它被视为一个数组,为什么会这样?作者试图用这个名为“破坏堆栈”的例子来说明什么?什么时候使用像数组这样的指针是个好主意?这样编程是否严格合法?

4

5 回答 5

2
tmp = &param;
tmp[i] = 0;

导致写入不属于自己的内存位置。这会导致未定义的行为。正如作者所说,这可能会破坏堆栈或工作得很好。它只是不是一个有效的 C 程序。

作者试图用这个名为“破坏堆栈”的例子来说明什么?

显然,S/He 试图证明破坏堆栈。其意图似乎是超出内存范围进行写入,以便可能破坏堆栈。但是,这是UB,它可能会也可能不会导致这种情况。

于 2013-05-25T17:25:58.713 回答
1

在这里,我们主要对 3 件事感兴趣

一个 int i的声明,一个指向 int tmp的指针,作为全局变量(不在堆栈中)

int i;
int *tmp;

以参数为参数调用函数destroyStack4

destroyStack(4);

以及函数destroyStack本身

void destroyStack(int param) 
{
  tmp = &param;
  for(i = -200; i < 10; i++)
  /* overwrite part of stack*/
  printf("%d\n",param), tmp[i] = 0;
}

堆栈在其生命周期内方便且暂时地(通常)为函数的参数、局部变量和返回地址保留相对较低的空间。
有一个内部堆栈指针(位于 CPU 中的寄存器中),它告诉我们在给定时间在堆栈中的位置 - 为该用途保留的内存空间。
当堆栈内存被“借用”时,例如在函数调用期间,堆栈指针会增加(为简单起见 - 实际上在 i386 上它会减少),并且当函数返回时堆栈指针返回到调用之前的位置。这很方便,因为1.不需要昂贵的动态分配(例如通过malloc2.编译器在编译时就知道参数、局部变量和返回地址在哪里——它们都与堆栈指针(+x 或 -x)相关。

那么这里发生了什么,当destroyStack被调用时

  • 堆栈指针被移动以为返回地址(destroyStack)和4参数(无局部变量)腾出空间
  • 然后处理器“跳转”到destroyStack的代码
  • tmp (global) 获取param的地址(稍后显示它的值)
  • 然后,只有 int 大小的 param 被 210 个int淹没” (即它可以容纳的 210 倍......)。这是可能的,因为 C 只需要一个指针(tmp)来处理一个值范围(如数组)。
  • 因此,堆栈空间从 -200 到 param 之下的 int 大小param之上10 倍-包括参数- 都填充为 0。

发生的情况是,您应该在printf显示4之前多次看到它0参数在for循环中被 0 覆盖)-并且由于destroyStack另一个函数的返回地址(在 printf 之前)很可能也被覆盖,CPU 将希望“跳转”到由零组成的地址 - 通常是保留区域,或者无论如何,进程不可访问/不可访问 - 并生成异常(崩溃)。

作者使用itmp作为全局变量(不是局部变量),这样它们就不会在堆栈中被destroyStack覆盖,即销毁可以按计划进行!

于 2013-05-25T18:28:52.340 回答
0

tmp is a pointer to an int and it gets treated like an array, why is it so?

tmp is effectively a single element integer array. The array element access operator can be seen as syntactic sugar for pointer arithmetics and dereferencing. As such a[b] is equivalent to *(a + b), and as a corollary b[a]. This is legal and perfectly fine unless invalid memory locations are being accessed: You shall not access memory regions that were not allocated for your variables.

What is the author trying to illustrate with this example called "destroying the stack" ?

The author tries to demonstrate what may happens when invalid memory regions are accessed. Technically, anything can happen. Nothing special included.

When is it a good idea to use a pointer like an array? Is it strictly legal to program like that?

Yes of course, this is a very common operation. You just have to know what array indices are valid (usually 0 to size-1). (You may need to pass the size alongside the pointer to the array: struct my_arr { int *arr; size_t size; };.)


(I don't think that the author wanted to demonstrate that the access violations in the lower memory regions are practically not a problem because that space on the stack wasn't used so far, but in contrast the access violations in the higher memory regions would overwrite besides the variable param, the return address of the function called and trigger stack corruption protection mechanisms (e.g., canaries) and thereby trigger a segmentation fault. But for anyone interested here's the obligatory link to Smashing The Stack For Fun And Profit by Aleph One.)

于 2013-05-25T18:03:27.703 回答
0
  • AFAIK tmp 是一个指向 int 的指针,它被视为一个数组,为什么会这样?这样编程是否严格合法?

这是合法的。在 C[]中是执行加法和取消引用的运算符。所以

a[i]

等于

*(a + i)

你甚至可以写5[a]而不是a[5]因为 *(a + 5) 等于 *(5 + a)。

于 2013-05-25T18:09:03.920 回答
0

传递给函数的“参数”将通过堆栈。它是如何工作的:C 存储一个返回地址(一些地址来显示处理器在执行一个函数后去哪里)和调用函数之前的堆栈参数。通过使用 & 获取“param”的地址,我们只需获取堆栈的头部。

在 deployStack 函数中,我们用零来回清除堆栈。我们可以做到,为什么不呢。

但是当 deployStack 结束时,处理器会排列一个地址以返回到 main。它不会得到它,因为我们只是用 0 擦除它。

接下来发生的事情在很大程度上取决于处理器架构,甚至与 C 标准无关。有一件事是肯定的——我们不会回到主要的。正如那里直接所说的那样。

于 2013-05-25T17:57:19.430 回答