Codeql规则性能优化
字数 1263 2025-08-11 08:35:57

CodeQL规则性能优化指南

1. 性能问题背景

在CodeQL规则开发中,性能问题经常出现。例如一个检查硬编码的规则,原本设计为取相邻4行的字符串,分别命中正则和长度条件时认为是硬编码,但在线上运行时却发现执行时间长达一两小时,比预期慢很多。

2. 性能分析方法

2.1 性能日志获取

CodeQL提供了性能分析工具,主要通过evaluator log来查看执行时间消耗:

codeql database analyze --evaluator-log --tuple-counting
codeql generate log-summary --format=text -q evaluator.log evaluator.txt

在VSCode中也可以通过图形界面查看evaluator log信息。

2.2 日志关键信息

evaluator log包含两个核心信息:

  1. 谓词时间消耗:显示各个谓词执行的时间占比
  2. 元组计数:显示每一步语句执行产生的结果数量

通过这些信息可以定位性能瓶颈所在。

3. 性能问题根源

通过分析发现,性能损耗主要来自:

  1. 不必要的表关联:特别是同文件字符串的join关联,会产生字符串数平方级的临时数据
  2. 编译优化导致的执行顺序改变:即使把关键条件放在谓词起始,优化后可能改变执行顺序
  3. 中间结果过大:谓词执行产生的临时表过大导致后续计算缓慢

4. 性能优化策略

4.1 减少关联

问题:同文件字符串的join关联会产生大量临时数据

解决方案

  • 将固定条件提前过滤
  • 例如secret长度为32是固定条件,可以先过滤出这些字符串再进行关联

效果:关联表量级直线下降,总耗时显著降低

4.2 编译注解控制

CodeQL提供了编译选项来控制优化行为:

  1. 关闭优化:使用pragma[noopt]禁用谓词内的编译优化

    • 需要将代码改为"简单的单步执行"形式
    • 通常不推荐,因为可能降低性能
  2. 内联编译:使用inline注解

    • 无内联编译:谓词单独计算,处理全量数据
    • 有内联编译:数据逐步筛选,元组不断减少
  3. 谓词缓存:使用cached注解

    • 可以缓存谓词计算结果供后续查询复用

4.3 逻辑调整优化

  1. 使用最小范围变量类型

    • 例如使用FunctionCall而非Call,减少元组数量
  2. 相似逻辑提炼为谓词

    • 将重复逻辑提取为单独谓词,复用临时结果
    • 特别是抽象逻辑相似的部分
  3. 相交结果复用

    • 将不同漏洞类型的相似部分提取为共享结果
    • 减少重复计算
  4. 控制谓词结果数量

    • 谓词结果越多,后续计算量越大
    • 尽量返回少量结果

4.4 减少元组数的方法

  1. 避免元组放大

    • 优化关联逻辑,减少关联产生的数据量
    • 例如将"同文件的字符串"相乘改为"字符串"和"同文件secret"相乘
  2. 大幅筛选条件优先

    • 将过滤效果好的条件前置
    • 注意编译优化可能改变顺序

5. 性能优化总结

  1. 优先使用evaluator log分析性能瓶颈
  2. 合理使用编译注解控制优化行为
  3. 通过逻辑重构减少中间结果量
  4. 建立性能监控机制定期优化规则

6. 最佳实践建议

  1. 避免编写会产生笛卡尔积的查询
  2. 尽量使用具体类型而非抽象类型
  3. 将过滤效果好的条件尽量前置
  4. 复用中间结果减少重复计算
  5. 对于复杂规则,定期进行性能测试和优化

通过以上优化方法,在实际扫描中可以实现显著的性能提升,有些场景甚至能达到一倍的效率提升。

CodeQL规则性能优化指南 1. 性能问题背景 在CodeQL规则开发中,性能问题经常出现。例如一个检查硬编码的规则,原本设计为取相邻4行的字符串,分别命中正则和长度条件时认为是硬编码,但在线上运行时却发现执行时间长达一两小时,比预期慢很多。 2. 性能分析方法 2.1 性能日志获取 CodeQL提供了性能分析工具,主要通过 evaluator log 来查看执行时间消耗: 在VSCode中也可以通过图形界面查看evaluator log信息。 2.2 日志关键信息 evaluator log包含两个核心信息: 谓词时间消耗 :显示各个谓词执行的时间占比 元组计数 :显示每一步语句执行产生的结果数量 通过这些信息可以定位性能瓶颈所在。 3. 性能问题根源 通过分析发现,性能损耗主要来自: 不必要的表关联 :特别是同文件字符串的join关联,会产生字符串数平方级的临时数据 编译优化导致的执行顺序改变 :即使把关键条件放在谓词起始,优化后可能改变执行顺序 中间结果过大 :谓词执行产生的临时表过大导致后续计算缓慢 4. 性能优化策略 4.1 减少关联 问题 :同文件字符串的join关联会产生大量临时数据 解决方案 : 将固定条件提前过滤 例如secret长度为32是固定条件,可以先过滤出这些字符串再进行关联 效果 :关联表量级直线下降,总耗时显著降低 4.2 编译注解控制 CodeQL提供了编译选项来控制优化行为: 关闭优化 :使用 pragma[noopt] 禁用谓词内的编译优化 需要将代码改为"简单的单步执行"形式 通常不推荐,因为可能降低性能 内联编译 :使用 inline 注解 无内联编译:谓词单独计算,处理全量数据 有内联编译:数据逐步筛选,元组不断减少 谓词缓存 :使用 cached 注解 可以缓存谓词计算结果供后续查询复用 4.3 逻辑调整优化 使用最小范围变量类型 例如使用 FunctionCall 而非 Call ,减少元组数量 相似逻辑提炼为谓词 将重复逻辑提取为单独谓词,复用临时结果 特别是抽象逻辑相似的部分 相交结果复用 将不同漏洞类型的相似部分提取为共享结果 减少重复计算 控制谓词结果数量 谓词结果越多,后续计算量越大 尽量返回少量结果 4.4 减少元组数的方法 避免元组放大 优化关联逻辑,减少关联产生的数据量 例如将"同文件的字符串"相乘改为"字符串"和"同文件secret"相乘 大幅筛选条件优先 将过滤效果好的条件前置 注意编译优化可能改变顺序 5. 性能优化总结 优先使用evaluator log分析性能瓶颈 合理使用编译注解控制优化行为 通过逻辑重构减少中间结果量 建立性能监控机制定期优化规则 6. 最佳实践建议 避免编写会产生笛卡尔积的查询 尽量使用具体类型而非抽象类型 将过滤效果好的条件尽量前置 复用中间结果减少重复计算 对于复杂规则,定期进行性能测试和优化 通过以上优化方法,在实际扫描中可以实现显著的性能提升,有些场景甚至能达到一倍的效率提升。