试图理解C指针,块和转到语句的问题

ber*_*436 3 c

我正在努力处理C和Cyclone,因为我通过Jim Trevor的" Cyclone:C语言的安全方言 "来完成PL课程.Trevor给出了一个不安全的首要声明的例子:

int z;
{ int x = 0xBAD; goto L; }
{ int *y = &z;
L: *y = 3; // Possible segfault
}
Run Code Online (Sandbox Code Playgroud)

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中引用的内存中的位置)?

vla*_*adr 5

我不明白为什么去声明是一个问题.

goto L绕过初始化y(y不会被设置为&z),因此分配给who-knows-where-it-pointing时会出现问题*y.

似乎问题是未初始化的指针Z的不可预测的行为

不.指针&z实际上是有效的.该intz未初始化,但这并不重要,因为您从未尝试过读取它; 你实际上是想覆盖它.

在第二个块的开头,int*y用Z的地址填充.

这就是重点. goto L绕过那个.

我不明白为什么Trevor的论文暗示Z和X都以某种方式引用0xBAD

我认为Trevor在这里暗示了第二个潜在的问题,尽管我不确定有多少编译器(如果有的话)会实际展示它.当离开块时goto,堆栈指针(例如,ESP在x86上)理论上可以不递减.通过跳过初始化y,堆栈指针也可以不递增.因此,如果编译器是指使用堆栈指针(而不是一个帧指针,例如当地人EBP在x86),这样的编译器可能误理论上xy,好像int* y = 0xBAD没有发生过.