安全C编程

安全C编程,c,memory,C,Memory,我注意到我的C编译器(gcc)将允许我执行以下操作: #include <stdio.h> main(){ short m[32768]; short y = -1; short z = -1; printf("%u\n", y); m[y] = 12; printf("%d\n%d\n", y, m[z]); } 这对我来说似乎有点莫名其妙 首先,我运行这样的程序安全吗?我是否有可能意外地在操作系统上写东西(我正在运行OSX,以防相

我注意到我的C编译器(gcc)将允许我执行以下操作:

#include <stdio.h>
main(){
    short m[32768];
    short y = -1;
    short z = -1;
    printf("%u\n", y);
    m[y] = 12;
    printf("%d\n%d\n", y, m[z]);
}
这对我来说似乎有点莫名其妙

首先,我运行这样的程序安全吗?我是否有可能意外地在操作系统上写东西(我正在运行OSX,以防相关)

此外,我曾预计至少会出现类似于我过去遇到的某种segfault错误,但悄悄地忽略这样的错误确实让我感到害怕。为什么这个节目不适合我


最后,出于好奇(这可能是最愚蠢的问题),有没有一种方法可以消除这种疯狂?我能期望所有ANSI C编译器都以这种方式工作吗?不同平台上的gcc如何?内存的布局是否定义得很好,可以利用它(如果您打算编写跨平台的模糊代码的话)?

C语言将某些程序的行为定义为“未定义”。他们可以做任何事。我们认为这样的程序是错误的

其中一个是访问数组声明/分配边界之外的程序,您的程序会非常小心地执行此操作

你的程序是错误的;您的错误程序所做的事情就是您看到的:-}它可能“覆盖操作系统”;实际上,大多数现代操作系统都阻止您这样做,但您可以覆盖流程空间中的关键值,并且您的流程可能会崩溃、死亡或挂起

简单的回答是,“不要编写错误的程序”。然后,您看到的行为将具有“C”意义

在这个特定的例子中,对于特定的编译器,数组索引“排序”起作用:您在数组外部进行索引,它会获取一些值。分配给m的空间在堆栈帧中;m[0]位于堆栈帧中的某个位置,基于结合阵列地址和索引的机器算法的“m[-1]”也位于堆栈帧中的某个位置,因此不会发生segfault,并且会访问内存位置。这允许编译程序读取和写入该内存位置。。。作为一个错误的程序。基本上,编译的C程序不会检查数组访问是否超出范围

当我们的工具应用于这个程序时,它会告诉你数组索引在执行时是非法的。所以,您可以亲自查看程序,看看是否犯了错误,或者让CheckPointer告诉您何时犯了错误。我强烈建议你无论如何都要盯着我看

首先,我运行这样的程序安全吗

你的例子:不,绝对不是。你为什么还要尝试?你期望它做什么? 使用负索引的更一般的示例-只要它们解引用到合法内存中就可以了

此外,我还预计至少会出现像我现在这样的segfault错误 遇到了过去,却悄悄忽略了这样一个错误 我真的很害怕。为什么这个节目不适合我

运气不好。(事实上并非如此——正如Ira Baxter所解释的那样)

最后,出于好奇(这可能是最愚蠢的问题), 有什么方法可以消除这种疯狂吗

如果您在数组中设置指向内容的指针,那么负索引可能会起作用,但对于其他人来说,理解和维护它们将是一场噩梦我见过它在嵌入式系统中实现

我能期望所有ANSI C编译器都以这种方式工作吗

是的

不同平台上的gcc如何

是的

内存的布局是否定义得很好,它是可利用的(可能是 如果您打算编写跨平台的模糊代码)


是的-但我不确定您是否真的想依赖它。

如果程序请求内存中的合法地址(由于其行为未定义,m[-1]可能会这样做),您将不会得到SEGFULT…特别是如果您请求短地址,因为它将适合大多数字长。这是一个非常糟糕的主意,您可以很容易地覆盖磁盘上的内容,尽管内核是受保护的,除非您在启动时正确运行代码

我不知道你为什么打印出-1的地址。

  • “安全”,从可能损坏操作系统的角度来看——是的,大多数“现代”操作系统都实施某种形式的“存储保护”,以便任性或恶意程序不会对操作系统或其他程序造成不良影响。(但“大部分”表明,长度超过50行的软件都不是完美的,而且总有一个奇怪的机会,你会发现盔甲上有裂缝。)
  • 从程序“正常运行”的角度来看,“安全”?嗯,不,主要是。这里的“主要”是指知识渊博(我不会说“聪明”)的程序员偶尔会做一些“奇怪”的事情,比如使用负数组索引,当他们知道编译器和运行时如何运行,并且需要高效地完成一些超出标准的事情时。但是他们(希望)这样做是因为他们知道他们使用的“技巧”非常依赖于系统/编译器

但正如@jb所指出的,“安全C编程”是一种矛盾修饰法。

除上述内容外,我建议针对valgrind运行的程序比仅基于编译器和操作系统信任的程序更有可能被检测为错误。Valgrind不会捕获所有内容,但在检测对未初始化或越界内存的访问方面做了合理的工作。

您可能对INRIA感兴趣,INRIA是一种C语言的正式、数学上可验证和验证的实现。它的作者和那些著名的作家是一样的。还有另一个变量

我对它了解不多,但我知道法国的飞机工程师用它来为即将到来的飞机嵌入式计算机编程,所以至少在法国,它是一种官方认可的关键系统编程语言

最后,请注意,可正式验证的语言不同于安全语言

例如,据说MisraC是一种安全的C语言(尽管这是
4294967295
12
12