友好请求:感谢您访问 control-flag GitHub 仓库!如果您觉得 control-flag 有用,我们很感谢您能给我们留言(发送至 niranjan.hasabnis@intel.com)。当然,我们也非常欢迎您的推荐!
-- ControlFlag 团队
linux 构建和测试 linux 样式检查 macos 构建和测试 macos 样式检查 GitHub 许可证
ControlFlag:一个自监督的特异模式检测系统,用于软件控制结构
ControlFlag 是一个自监督的特异模式检测系统,它通过挖掘开源仓库(在 GitHub 和其他版本控制系统上)中的模式,学习高级编程语言(如 C/C++)控制结构中出现的典型模式。然后,它将学习到的模式应用于检测用户代码中的异常模式。
简要技术描述
ControlFlag 的模式异常检测系统可用于各种问题,如拼写错误检测、标记缺失的 NULL 检查等。这个概念验证演示了 ControlFlag 在拼写错误检测中的应用。
下图显示了 ControlFlag 的两个主要阶段:(1)模式挖掘阶段,和(2)扫描异常模式阶段。模式挖掘阶段是一个"训练阶段",它从用户提供的 GitHub 仓库中挖掘典型模式,然后从挖掘的模式构建决策树。另一方面,扫描阶段将挖掘的模式应用于标记用户指定目标仓库中的异常表达式。
更多详细信息可以在我们的 MAPS 论文中找到(https://arxiv.org/abs/2011.03616)。
目录结构(不断演进)
src
:ControlFlag 用于拼写错误检测系统的源代码scripts
:用于模式挖掘和扫描异常的脚本quick_start
:运行快速启动测试的脚本github
:用于下载 GitHub 仓库的脚本和数据tests
:单元测试
安装
ControlFlag 可以在 Linux 和 MacOS 上构建。
要求
- CMake 3.4.3 或更高版本
- 兼容 C++17 的编译器
- Tree-sitter 解析器(作为 cmake 的一部分自动下载)
- GNU parallel (可选,如果你想生成自己的训练数据)
在基于 Linux 的系统上测试过的构建配置
- CentOS-7.6/Ubuntu-20.04 with g++-v10.2.0 for x86_64
在 MacOS 上测试过的构建配置
- MacOS Mojave v10.14.6 with clang-1001.0.46.4 (Apple LLVM version 10.0.1) for x86_64 (从命令行工具包获得)
构建
$ cd control-flag
$ cmake .
$ make -j
$ make test
make test
中的所有测试都应该通过。
使用 ControlFlag
快速开始
使用从多个 GitHub 仓库获得的模式来扫描你选择的仓库
根据你设备的内存限制,下载你感兴趣的语言的训练数据。但请注意,使用较小的数据集可能会导致 ControlFlag 产生的结果准确性降低,并可能增加它生成的假阳性数量。
语言 | 数据集名称 | 磁盘大小 | 内存要求 | 直接链接 | MD5 校验和 |
---|---|---|---|---|---|
C | 小型 | ~100MB | ~400MB | 链接 | 2825f209aba0430993f7a21e74d99889 |
C | 中型 | ~450MB | ~1.3GB | 链接 | aab2427edebe9ed4acab75c3c6227f24 |
C | 大型 | ~9GB | ~13GB | 链接 | 1ba954d9716765d44917445d3abf8e85 |
C++ | 小型 | ~200MB | ~500MB | 链接 | f954486e20961f0838ac08e5d4dbf312 |
C++ | 中型 | ~500MB | ~1.3GB | 链接 | a5c18ea1cdbe354b93aabf9ecaa5b07a |
C++ | 大型 | ~1.2GB | ~3GB | 链接 | 4f5ffc1ab942eaba399cafd5be8bb45f |
PHP | 小型 | ~120MB | ~1GB | 链接 | 5a1cc4c24a20de7dad1b9f40661d517a |
$ 从上面的链接下载 <tgz_file>。
$ (可选) md5sum <tgz_file>
$ tar -zxf <tgz_file>
要扫描你选择的 C 代码,使用以下命令:
$ scripts/scan_for_anomalies.sh -d <要扫描异常的目录> -t <训练数据>.ts -o <存储日志文件的输出目录> -l 1
要扫描你选择的 C++ 代码,使用以下命令:
$ scripts/scan_for_anomalies.sh -d <要扫描异常的目录> -t <训练数据>.ts -o <存储日志文件的输出目录> -l 4
一旦运行完成(这可能需要一些时间,取决于你的系统和 ControlFlag 可以扫描的仓库中的程序数量),请参考下面的章节来理解扫描输出。
从一个小型仓库挖掘模式并将其应用到另一个小型仓库
在这个 C 语言程序的测试中,我们将从 GitHub 的 Glb-director 项目中挖掘模式,并将它们应用于标记 GitHub 的 brubeck 项目中的异常。
只需运行以下命令:
cd quick_start && ./test1_c.sh
如果一切顺利,你可以在 test1_scan_output
目录中看到扫描器的输出。通过 grep "Potential anomaly" -C 5 *.log
查找其中的 "Potential anomaly" 标签,你应该看到类似下面的输出:
thread_6.log-Level:TWO Expression:(parenthesized_expression (binary_expression ("==") (identifier) (non_terminal_expression))) found in training dataset:
Source file: brubeck/src/server.c:266:5:(s == sizeof(fdsi))
thread_6.log-Autocorrect search took 0.000 secs
thread_6.log:Potential anomaly
thread_6.log-Did you mean:(parenthesized_expression (binary_expression ("==") (identifier) (non_terminal_expression))) with editing cost:0 and occurrences: 1
thread_6.log-Did you mean:(parenthesized_expression (binary_expression ("==") (identifier) (null))) with editing cost:1 and occurrences: 25
thread_6.log-Did you mean:(parenthesized_expression (binary_expression ("==") (identifier) (identifier))) with editing cost:1 and occurrences: 5
thread_6.log-Did you mean:(parenthesized_expression (binary_expression (">=") (identifier) (non_terminal_expression))) with editing cost:1 and occurrences: 3
thread_6.log-Did you mean:(parenthesized_expression (binary_expression ("==") (non_terminal_expression) (non_terminal_expression))) with editing cost:1 and occurrences: 2
在 brubeck/src/server.c
文件的第 266 行标记了异常。
详细步骤
- 模式挖掘阶段 (如果你想自己生成训练数据)
如果你不想自己生成训练数据,请转到下面的评估步骤。
在这个阶段,我们挖掘出现在高级语言(如 C 语言)控制结构中的特异模式。这个概念验证从 C 程序中出现的 if
语句中挖掘模式。
如果你想使用自己的仓库来挖掘模式,请跳到步骤 1.2。 1.1 下载星标数超过100的C语言GitHub仓库
以下步骤展示如何下载星标数超过100的C语言GitHub仓库(c100.txt
)并生成训练数据。training_repo_dir
是一个目录,下面的命令将在其中克隆所有仓库。
$ cd github
$ python download_repos.py -f c100.txt -o <training_repo_dir> -m clone -p 5
1.2 从下载的仓库中挖掘模式
你可以使用自己的仓库来挖掘表达式,只需将其替换<training_repo_dir>即可。
mine_patterns.sh
脚本可以帮助完成这个任务。其用法如下:
用法: ./mine_patterns.sh -d <用于挖掘模式的目录> -o <存储训练数据的输出文件>
可选参数:
[-n 用于挖掘的进程数] (默认值: 系统CPU数)
[-l 源语言编号] (默认值: 1 (C), 支持: 1 (C), 2 (Verilog), 3 (PHP), 4 (C++))
[-g github仓库ID] (默认值: 0) GitHub仓库的唯一标识符(如果有)
我们这样使用它:
$ scripts/mine_patterns.sh -d <training_repo_dir> -o <training_data_file> -l 1
<training_dat_file>
包含在指定GitHub仓库中找到的C语言条件表达式及其AST(抽象语法树)表示。如果你想查看,可以将其作为文本文件打开。
评估(或扫描异常)
我们可以运行scan_for_anomalies.sh
脚本来扫描感兴趣的目标目录。其用法如下:
用法: ./scan_for_anomalies.sh -t <训练数据> -d <用于扫描异常模式的目录>
可选参数:
[-c 自动纠正的最大成本] (默认值: 2)
[-n 自动纠正的最大结果数] (默认值: 5)
[-j 扫描线程数] (默认值: 系统CPU数)
[-o 输出日志目录] (默认值: /tmp)
[-l 源语言编号] (默认值: 1 (C), 支持: 1 (C), 2 (Verilog), 3 (PHP), 4 (C++))
[-a 异常阈值] (默认值: 3.0)
作为扫描异常的一部分,如果条件表达式被标记为异常,ControlFlag还会建议可能的纠正。25
是纠正的max_cost
——建议的纠正应该与可能错误输入的表达式有多接近。增加max_cost
会导致提出更多纠正建议。如果你觉得报告的异常数量过高,可以考虑将anomaly_threshold
降低到1.0
或更低。
理解扫描输出
在output_log_dir
下,你会找到多个日志文件,对应不同扫描线程的输出。潜在异常会以"Potential anomaly"作为标签报告。以下命令将报告至少包含一个异常的日志文件。
$ grep "Potential anomaly" <output_log_dir>/thread_*.log
一个示例异常报告如下所示:
级别:<ONE or TWO> 表达式: <异常表达式的AST>
源文件和行号: <包含异常的源代码表达式及行号>
潜在异常
您是否指的是...
"您是否指的是"后面的文本显示了对异常表达式可能的纠正。
成功案例
为了服务社区,我们经常使用ControlFlag扫描开源软件包。我们在各种开源项目中发现了几个编程错误。以下列出了一些已被各自开发者确认的错误。
问题链接 | 语言 | 错误表达式 | 备注 |
---|---|---|---|
https://github.com/curl/curl/pull/6193 | C | if (s->keepon > TRUE) | 使用> 比较变量和布尔值 |
https://github.com/vrpn/vrpn/issues/263 | C | (l_inbuf[2] | 1) , if (l_inbuf[3] | 1) | 错误使用| 而非& |
https://github.com/vlm/asn1c/issues/443 | C | if(!saved_aid && 0) | 死代码 |
https://github.com/shoes/shoes3/issues/468 | C | if ((attr == 39) || (attr = 49)) | 错误使用= 而非== |
https://github.com/IoLanguage/io/issues/455 | C | if (UArray_greaterThan_(self, other) | UArray_equals_(self, other)) | 低效使用| 而非|| |
https://github.com/IoLanguage/io/issues/455 | C | if( ln = (SFG_Node *)node->Next ) , if( ln = (SFG_Node *)node->Prev ) | 缺少括号 |
https://github.com/elua/elua/issues/170 | C | if (Protection_Level_1_Register &= FMI_Sector_Mask) | 缺少括号 |