当我通过 Jim Trevor为 PL 课程编写的“ Cyclone: A safe dialect of C ”时,我正在尝试掌握 C 和 Cyclone。Trevor 给出了一个不安全的 go-to 语句的例子:
int z;
{ int x = 0xBAD; goto L; }
{ int *y = &z;
L: *y = 3; // Possible segfault
}
Trevor 对上述代码中的安全问题解释如下:
许多编译器在进入块时堆栈分配块的局部变量,并在块退出时释放(弹出)存储(尽管这不是 C 标准强制要求的)。如果示例以这种方式编译,那么当程序进入第一个块时,x 的空间会在堆栈上分配,并使用值 0xBAD 进行初始化。goto 跳转到第二个块的中间,直接对指针 y 的内容进行赋值。由于 y 是在第二个块中声明的第一个(唯一)变量,因此赋值期望 y 位于堆栈的顶部。不幸的是,这正是 x 被分配的位置,因此程序尝试写入位置 0xBAD,可能会触发分段错误。
我不明白为什么go to
这里的声明是一个问题。似乎问题是来自未初始化的指针 Z 的不可预测的行为。在第二个块的开头,int * y
用 Z 的地址填充。Z 未初始化,因此它将int* y
用堆栈上的位模式填充Z 引用的内存。我不明白为什么 Trevor 的论文暗示 Z 和 X 都引用了 0xBAD。C 不会为第一个块创建一个新的堆栈帧(如 Trevor 所述):因此将 0xBAD 写入内存中的新帧(而不是 Z 引用的内存位置)?