Page 129 - 《软件学报》2026年第7期
P. 129
2814 软件学报 2026 年第 37 卷第 7 期
划分为 TPL 模块, 并且同时兼顾“#include”和“extern”等多种指令分析模块间的直接和间接的依赖关系, 为全面安
全漏洞检测和许可证合规审查提供数据支持 (步骤 3).
3.1 构建 TPL 特征库
3.1.1 TPL 数据集构建
针对 C/C++ TPL 分散在不同托管仓库且存在重复的问题, CAnalyzer 多仓库联动的动态收集方法, 其核心思
想是: 先通过仓库间信息整合避免重复下载, 再通过“滚雪球”式迭代逐步扩大收集范围, 从而在节省存储空间的同
时覆盖尽可能多的 C/C++ TPL, 具体实施分为 3 个步骤.
步骤 1: 多仓库数据整合与去重. 根据已有研究 [29] 的发现, C/C++ TPL 通常托管于 3 类仓库 (见表 1). 因此, 本
文从这 3 类仓库中选取了 15 个具有代表性的流行托管源来收集 C/C++ TPL. 对于每个 TPL, 我们统一提取基本元
数据信息: TPL 名称、版本号、源代码存储库地址等. 由于同一个 TP 可能出现在不同的托管仓库中 (例如 OpenSSL
同时存在于 GitHub 与 Ubuntu 仓库), 为了避免重复收集, 本文首先采用自动化方式进行初步去重: 如果不同托管
仓库中 TPL 的源代码仓库地址一致, 则判定为同一 TPL 并合并记录. 该过程无需人工判定, 能够高效地消除大部
分冗余条目, 从而生成一个“索引库”, 相当于为每个 TPL 创建了一份“身份证目录”. 然而, 值得注意的是, 仅凭源代
码仓库地址一致性并不能完全覆盖所有情况. 一些 TPL 可能托管于不同的源代码仓库地址, 但本质上仍然指向同
一个 TPL, 对于这类情况, 在后续阶段 (参见第 3.1.3 节) 进一步通过函数级别的相似性分析来判定归属, 从而实现
更精确的 TPL 去重.
步骤 2: 起始 TPL 集合选取. 由于 C/C++生态规模庞大且边界模糊, 直接收集所有可能的 TPL 在可行性和效
率上都存在困难. 因此, 本文首先选取一批具有代表性的“种子库”作为切入点逐步扩展收集. 具体而言, 我们在
GitHub 上筛选出满足以下条件的 C/C++ TPL: (a) 星标数不少于 100 (受欢迎程度高); (b) 过去 1 年内有提交或版
本更新 (可维护性好); (c) 在所有满足条件的项目中, 根据综合指标 (主要为星标数和贡献者数) 进行排序, 选取前
1 000 个 TPL 作为起始集合, 能够覆盖大多数在实际工程和科研场景中被频繁依赖的核心 TPL, 例如 libpq (数据库
连接库)、OpenCV (图像处理库)、Boost (通用库集合) 以及 gRPC (远程过程调用框架) 等.
步骤 3: 动态迭代收集 TPL. 从种子库出发, 采用“解析依赖-搜索-获取”的循环机制逐步扩大收集范围 (后文图 4(a)).
(1) 依赖解析: 使用 Ccscaner 工具 [29] 分析种子库的依赖关系, 找出它们需要调用的其他库 (如配置文件里声明的依
赖项、代码中包含的头文件等). (2) 跨平台检索: 根据发现的依赖项名称, 同时在两个渠道查找具体代码: 自建的
索引库 (快速匹配已知库); GitHub/GitLab/Gitee 仓库 (搜索相关存储库). (3) 去重下载: 对找到的依赖项仓库, 通过
比对目录结构, 确保不重复下载相同的库. 新获得的库会加入收集列表, 成为下一轮解析的起点. 通过这种“滚雪球”式
的收集方式, CAnalyzer 实现了存储效率与覆盖广度的均衡, 既收录了主流常用 C/C++ TPL, 也通过依赖挖掘发现
了许多非热门但被实际项目引用的长尾库 (指的是使用频率较低、关注度不高、但在特定场景中仍被实际项目引
用的小众第三方库, 如嵌入式领域专用算法库).
3.1.2 TPL 特征提取
TPL 的特征是其代码及多种属性的集合, 用于支持与目标代码的匹配. 我们利用 Ctags [30] 工具解析 TPL, 提取
函数、类、变量等代码元素的名称、文件位置和行号等信息. 开源软件更新时, 并非所有源代码都会重新开发. 不
同版本之间存在公共部分. 传统的 SCA 工具会为每个版本存储一次函数记录 (如图 5(a) 所示). 这不仅浪费存储空
间, 还导致多版本之间的公共部分同目标代码重复比较.为减少冗余比较并降低存储和计算复杂度, 我们采用了增
量式存储策略 [13] . 具体来说, 我们只存储跨版本重复函数的单一记录, 并附加版本和路径信息. 最终, TPL 特征库
的状态如图 4(b) 所示, TPL 存储结构如图 5(b) 所示. 每个 TPL 主要包含以下特征.
● 源码摘要. 去除空格、换行符和注释后, 计算函数的 TLSH [31] 哈希值, 记录版本及路径, 用于匹配目标代码.
● 版本信息. 记录所有版本的时间和名称, 用于评估存储库的活跃程度.
● 存储库地址. 包含下载地址, 支持使用 Git 命令克隆与更新.
● 存储库目录. 简化目录结构为字符串, 便于识别目录相似的存储库是否为同一 TPL.

