有害的C源文件检查?

use*_*424 6 c static-analysis

有没有办法以编程方式检查单个C source file是否有潜在危害?

知道没有检查会产生100%的准确性 - 但至少有兴趣做一些基本的检查,如果找到一些表达式/关键字会引发一个红旗.有什么想法的想法?

注:我会检查文件是比较小的尺寸(最多的行数100S),实现所有的操作数值分析功能,在内存中.代码中不得使用外部库(math.h除外).此外,不应使用I/O(函数将与内存数组一起运行).

鉴于上述情况,我是否可以做一些程序性检查,至少尝试检测有害代码?

注意:因为我不期望任何I/O,如果代码执行I/O - 它被认为是有害的.

Ira*_*ter 8

是的,有一些程序化的方法来检测与您有关的条件.

在我看来,理想情况下,您需要一个静态分析工具来验证代码的预处理版本:

  1. 除了它定义的函数和标准库中的非I/O函数之外,不调用任何函数,
  2. 不用指针做任何坏事.

通过预处理,您可以摆脱检测宏,可能是错误的宏内容以及实际使用宏的问题.此外,您不希望涉及标准C头中的所有宏定义; 因为它们所包含的所有历史残余,它们会伤害你的灵魂.

如果代码只在标准库中调用自己的函数和可信函数,那么它就不会调用任何令人讨厌的函数.(注意:它可能是通过指针调用某个函数,所以这个检查要么需要函数点分析,要么间接函数调用是禁止的,这对于进行数值分析的代码来说实际上是合理的).

使用指针检查错误内容的目的是为了不滥用指针来制造令人讨厌的代码并将控制传递给它.第一个意思是"没有强制转换为int的指针",因为你不知道int的位置: - }

对于who-do-it-call检查,您需要解析代码和名称/类型解析每个符号,然后检查呼叫站点以查看它们的去向.如果你允许指针/函数指针,你需要一个完整的点分析.

标准静态分析器工具公司之一(Coverity,Klocwork)可能提供某种限制代码块可能调用的函数的方法.如果这不起作用,您将不得不依赖于更通用的分析机制,例如我们的DMS软件再造工具包 及其C前端.DMS提供可定制的机制来构建任意静态分析器,用于作为前端提供的语言描述.DMS可以配置为完全执行测试1)包括预处理步骤; 它还具有完整的分数和功能点,可用于分数检查.

对于2)"不使用指针恶意",标准静态分析工具公司再次提供一些指针检查.然而,在这里他们有一个更难的问题,因为他们静静地试图推理图灵机.他们的解决方案是错过案例或报告误报.我们的CheckPointer工具是一个动态分析,也就是说,它在运行时监视代码,如果有任何滥用指针的尝试,CheckPointer将立即报告有问题的位置.哦,对了,CheckPointer歹徒蒙上从整数到指针: - }所以CheckPointer不会提供静态诊断"这个代码可以欺骗",但你会得到一个诊断,如果它实际上试图作弊.CheckPointer具有相当高的开销(所有检查都需要花费一些成本),所以你可能希望用它来运行你的代码一段时间以获得一些不会发生任何坏事的信念,然后停止使用它.

编辑:另一张海报说,关于静态定义缓冲区的缓冲区覆盖,你可以做很多事情.CheckPointer将进行这些测试等.


unp*_*nic 3

如果您想确保它没有调用任何不允许的内容,请编译该代码段并检查它链接到的内容(例如 via nm)。由于您热衷于通过“编程”方法执行此操作,因此只需使用 python/perl/bash 进行编译,然后扫描目标文件的名称列表即可。

对于静态定义的缓冲区的缓冲区覆盖,您无能为力,但您可以链接到电子围栏类型的内存分配器,以防止动态分配的缓冲区溢出。

您还可以针对驱动程序编译和链接有问题的 C 文件,该驱动程序将在 valgrind 下运行时为其提供典型数据,这有助于检测编写不良或恶意编写的代码。

然而,最终,你总是会遇到“这个例程是否终止”的问题,这个问题因不可判定而闻名。解决这个问题的一种实用方法是编译您的程序并从驱动程序运行它,该驱动程序将alarm在一段合理的时间后退出。

编辑:示例显示使用nm

创建一个 C 代码片段定义函数foo,调用fopen

#include <stdio.h>
foo() {
   FILE *fp = fopen("/etc/passwd", "r");
}
Run Code Online (Sandbox Code Playgroud)

使用 进行编译-c,然后查看生成的目标文件:

$ gcc -c foo.c
$ nm foo.o
0000000000000000 T foo
                 U fopen
Run Code Online (Sandbox Code Playgroud)

在这里您将看到目标文件中有两个符号foo.o。第一个是定义的foo,我们编写的子例程的名称。其中一个是未定义的fopen,当目标文件与其他 C 文件和必要的库链接在一起时,它将链接到它的定义。使用此方法,您可以立即查看编译对象是否引用其自身定义之外的任何内容,并且根据您的规则,可以将其视为“坏”。