ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CodeQL C++ 范围分析演进:SimpleRangeAnalysis 对无符号乘法、常量乘法的扩展与自定义规则机制

CodeQL C++ 范围分析演进:SimpleRangeAnalysis 对无符号乘法、常量乘法的扩展与自定义规则机制 静态分析SAST应用安全漏洞扫描代码质量【免费下载链接】codeqlCodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security项目地址https://gitcode.com/gh_mirrors/co/codeql点击查看免费下载本文围绕 CodeQL 仓库中 C 查询库的一次重要变更记录展开SimpleRangeAnalysis范围分析库新增了对无符号数乘法、常量乘法、引用类型函数参数、不等比较收窄范围以及if (x)布尔判零等语言构造的支持并且首次开放了通过SimpleRangeAnalysisExpr抽象类注入自定义范围规则的扩展点。读完本文你将理解这些能力背后的格lattice抽象解释原理、widening 终止性设计并能参考仓库中的现成示例为自己的查询添加范围分析扩展。1. 变更概览五项新语法支持与自定义规则能力该变更记录2020-09-29-range-analysis-rollup.md面向 lgtm 与 codescanning 两个平台其核心内容可以概括为两部分第一部分新支持的语言构造。SimpleRangeAnalysis库在此前不支持五类 C 语言构造本次变更全部补齐无符号数的乘法multiplication of unsigned numbers与常量相乘multiplication by a constant引用类型的函数参数reference-typed function parameters用“变量不等于其值域端点”的比较收窄范围——例如x ! c且c是x的当前下界时可将下界向前推进一格用if (x)或if (!x)这类布尔上下文测试“是否等于零”。变更说明同时指出这些改进主要影响三个查询cpp/constant-comparison、cpp/comparison-with-wider-type和cpp/integer-multiplication-cast-to-long。第二部分可扩展性。库从此可以接受用户自定义规则官方示例存放在变更记录所列的cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/目录下。需要说明的是在当前的仓库结构中这些示例已经迁移到了cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/而抽象接口则位于cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/。2. 原理基础在 QL 中实现范围分析的三个关键设计要理解这次变更的价值先看 SimpleRangeAnalysis.qll 文件头部注释对算法本身的描述。范围分析通常是对“值域格”的抽象解释——每个表达式的抽象值是一对浮点数(lowerBound, upperBound)。直接用格求最小不动点会遇到收敛过慢的问题文件注释中给出的经典反例是int count 0; for (; p; p p-next) { count count 1; }count的值域从(0,0)出发每轮不动点迭代只向上推进一格的边界(0,1)、(0,2)……直到maxInt格的高度几乎无界。库给出的对策有三点全部可以直接在源码中验证固定 widening 边界集。当某个定义“递归地依赖自身”通过defDependsOnDef的传递闭包检测见 SimpleRangeAnalysis.qll 中的defDependsOnDefTransitively与isRecursiveDef就把它的边界舍入到一个固定集合。下界集合是2, 1, 0, -1, -2, -8, -16, -128, -256, -32768, -65536, typeLowerBound, -Inf上界集合是对称的-2, -1, 0, 1, 2, 7, 15, 127, 255, 32767, 65535, typeUpperBound, InfwideningLowerBounds / wideningUpperBounds。注释明确说这与抽象解释中的 widening 技术类似但因为 QL 无法查看上一轮迭代的边界精度略低——例如 widening 可能推出下界-11而这里只能近似为-16。用浮点数表示边界。注释解释了动机浮点不会溢出回绕且可以通过“边界落在类型可表示范围之外”直接检测正/负溢出getTruncatedLowerBounds与getTruncatedUpperBounds分别处理了这种不可表示边界的截断见 SimpleRangeAnalysis.qll。先收集边界集合、后聚合。由于 QL 不允许在递归子表达式上做聚合aggregate递归阶段为每个表达式产生“一组下界”和“一组上界”最小下界与最大上界在递归结束后才计算。这条限制还引出了一个性能护栏BoundsEstimate模块在分析前估计边界数量一旦超过2^29的上限getBoundsLimitSimpleRangeAnalysis.qll就提前启用 widening注释说明该阈值是“经验性地选取到足以避免在特定客户数据库上超时”的值。理解这三点后本次变更的五项新支持就不再是“功能列表”而是格上每一类算术/控制流算子的边界传播规则被逐一补全的过程。3. 新支持语法逐一对照源码3.1 无符号数乘法下界相乘、上界相乘无符号乘法之所以值得单独建模是因为它的边界传播比有符号乘法更简单——两个操作数都非负所以“下界×下界、上界×上界”就是精确答案不存在符号翻转问题。源码中UnsignedMulExpr类要求结果类型是IntegralType且无符号并刻意排除已按常量乘处理的表达式以避免重叠SimpleRangeAnalysis.qllprivate class UnsignedMulExpr extends MulExpr { UnsignedMulExpr() { this.getType().(IntegralType).isUnsigned() and // Avoid overlap. It should be slightly cheaper to analyze // MulByConstantExpr. not this instanceof MulByConstantExpr } }对应地getLowerBoundsImpl对UnsignedMulExpr取xLow * yLowgetUpperBoundsImpl取xHigh * yHighSimpleRangeAnalysis.qll 与 L1352-L1357。x * y形式的UnsignedAssignMulExpr也按同样规则处理。变更前后对比的意义在于此前遇到unsigned乘法时表达式会落入“不可分析”分支下界直接退化为exprMinVal、上界退化为exprMaxVal所有依赖精确边界的下游判断都会失效现在这条路径能给出真实边界。3.2 与常量相乘把符号与量级分离处理“乘以一个常量”被拆成了四个协作的谓词/类核心思想是乘正数与乘负数对上下界的作用是相反的必须分开传播。MulByConstantExprSimpleRangeAnalysis.qll恰好一个操作数是常量的乘法表达式暴露出常量值getConstant()与非常量操作数getOperand()effectivelyMultipliesByPositive/effectivelyMultipliesByNegativeSimpleRangeAnalysis.qll除直接乘法外还覆盖“等效”乘常量的情形——一元x乘 1、一元-x乘 -1、逗号表达式取右操作数、StmtExpr取结果表达式这些是真实 C/C 代码里常见的惯用法AssignMulByConstantExpr及其正/负子类的划分用于处理x * c。边界传播规则体现在getLowerBoundsImpl/getUpperBoundsImpl中乘正数时下界 常量 × 操作数下界上界 常量 × 操作数上界乘负数时上下界交换来源用操作数的上界算下界、下界算上界。这套规则同样是x * c复合赋值语句的基础。3.3 引用类型函数参数变更记录列出的第三项是“引用类型函数参数”的支持。从当前源码结构看范围分析统一经由 SSA 信息工作变量访问e只要能在RangeSsaDefinition中找到对应定义e def.getAUse(v)即 analyzableExpr 中“允许变量访问前提是它有 SSA 信息”这一分支就可以通过getDefLowerBounds/getDefUpperBounds拿到边界。引用类型参数在底层同样有 SSA 定义可以推断该项改进正是让这类定义进入可分析集合的结果——具体到边界传播与按值参数走的是同一套规则。3.4 “不等于端点”收窄范围不等比较的 phi 节点第四项“比较变量不等于其值域端点从而把范围收窄一格”对应源码中的isNEPhi机制。getPhiLowerBounds中有一段精确实现了这个语义SimpleRangeAnalysis.qllexists(VariableAccess access, float neConstant, float lower | isNEPhi(v, phi, access, neConstant) and lower getTruncatedLowerBounds(access) and if lower neConstant then result lower 1 else result lower )含义是如果在某个分支上已知x ! c而该分支入口处x的下界恰好就是c那么在这个分支里x的下界可以推进到c 1。上界方向对称处理。这正是“narrowing the range by one”的出处它对循环不变量、数组下标验证这类场景尤为有用。3.5if (x)/if (!x)的判零语义第五项支持的是布尔上下文中“非零即真”的语义。C/C 中if (x)、!x、逻辑运算、(bool)x都隐式测试x 0。实现位于boolConversionLowerBound/boolConversionUpperBoundSimpleRangeAnalysis.qll下界若expr的值域覆盖 0下界为 0若能证明值域不含 0下界 0 或上界 0下界为 1若类型非算术类型如指针下界保守取 0上界若值域可能为 0上界取 0 与 1 都可能只有能证明“永远非零”下界 0 或上界 0时上界才确定为 1反之能证明“永远为零”时上界为 0。exprIsUsedAsBool谓词SimpleRangeAnalysis.qll先把“会被当作布尔用”的表达式集合圈定下来——逻辑运算的子表达式、一元逻辑操作数、条件表达式条件、bool转换——以避免对无关表达式做无谓的边界计算。这个机制让分析器能理解“进入if (x)分支意味着x ! 0、进入if (!x)分支意味着x 0”并据此在分支内收窄后续表达式。4. 自定义规则扩展机制SimpleRangeAnalysisExpr 与官方示例本次变更的另一半是开放扩展点。抽象接口定义在 SimpleRangeAnalysisExpr.qll/** * An expression for which a range can be deduced. Extend this class to add * functionality to the range analysis library. */ abstract class SimpleRangeAnalysisExpr extends Expr { /** Gets the lower bound of the expression. */ abstract float getLowerBounds(); /** Gets the upper bound of the expression. */ abstract float getUpperBounds(); /** * Holds if the range this expression depends on the definition srcDef * for StackVariable srcVar. ... */ predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() } /** * Holds if this expression depends on the range of its unconverted * subexpression child. ... * For example, when modeling a function call whose return value depends on * all of its arguments, implement this predicate as * child this.getAnArgument(). */ abstract predicate dependsOnChild(Expr child); }实现一个扩展规则需要让自定义类同时继承SimpleRangeAnalysisExpr和具体的Expr子类如FunctionCall、SubExpr用类匹配条件圈定要建模的表达式覆写getLowerBounds()/getUpperBounds()其中对子表达式取边界时必须通过getFullyConvertedLowerBounds/getFullyConvertedUpperBounds递归注释明确要求覆写dependsOnChild(Expr child)声明依赖了哪些未转换子表达式的范围——注释特别强调不声明周期依赖会导致“简单情况能跑、复杂代码上无限循环”。主库 SimpleRangeAnalysis.qll 在上下界计算的最后一支统一调用SimpleRangeAnalysisExpr的getLowerBounds()/getUpperBounds()且内置的边界传播规则明确以not expr instanceof SimpleRangeAnalysisExpr收尾即用户扩展优先于内置规则且不同扩展类之间互不干扰。变更记录中提到的示例目录现路径为 extensions 目录给出了两个可以直接参照的最小实现StrlenLiteralRangeExpr.qll对字面量字符串的strlen调用返回精确常量范围——getLowerBounds与getUpperBounds都等于字符串字面量长度dependsOnChild返回none()因为范围不依赖任何子表达式SubtractSelf.qll匹配x - x要求两侧显式转换后是同一个变量的访问排除myInt - (unsigned char)myInt这类类型不同的情形上下界都返回 0。该目录还有ConstantBitwiseAndExprRange.qll、ConstantShiftExprRange.qll、RangeNode.qll等更多示例覆盖了“子表达式为常量时结果可精确推断”这一最常见的扩展模式。5. 对下游查询的影响与验证路径变更记录说明这些改进“主要影响”三个查询它们在仓库中的位置与当前状态可以逐一核对cpp/comparison-with-wider-type实现在 ComparisonWithWiderType.qlid与name见文件头 L1-L15直接import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysisL20。窄类型与宽类型在循环条件中比较的问题其判定质量直接取决于范围分析能否给出循环变量的精确边界cpp/integer-multiplication-cast-to-long对应 IntMultToLong.ql同样构建在SimpleRangeAnalysis之上。整数乘法结果转换到long是否安全的判断本质上是“乘法上界是否落在 long 可表示范围内”的范围判断——无符号乘法与常量乘法的支持使这类判断从保守退化变为精确cpp/constant-comparison比较双方是否恒为常量或恒定大小关系的判定依赖上下界是否收敛为同一点因此五项新支持全部都会提升它的判定率。除这三个查询外当前仓库中SnprintfOverflow.ql、OverflowStatic.ql、ImproperArrayIndexValidation.ql、InconsistentLoopDirection.ql等也都在SimpleRangeAnalysis之上构建属于同一能力的受益面。库本身的回归测试位于 SimpleRangeAnalysis_tests.cpp可作为理解每类构造预期边界的参考。6. 小结这次变更解决的核心问题回到变更记录本身它解决的其实是两个层面的问题。第一层是精度——无符号乘法、常量乘法、引用参数、NE 收窄、布尔判零这五类构造补全后C 代码中大量原本落入“不可分析→边界退化为类型全范围”的表达式开始产生精确边界直接改善了三个核心查询的查全率与查准率。第二层是可扩展性——SimpleRangeAnalysisExpr把“我知道这个表达式的范围”从库的内置知识变成可注入的领域知识扩展者只需回答三个问题这个表达式是什么、它的上下界是多少、它依赖哪些子表达式的范围。对基于 CodeQL 编写 C 自定义查询的开发者而言这套机制是向范围分析库贡献项目特有算术语义的标准入口。赞分享静态分析SAST应用安全漏洞扫描代码质量【免费下载链接】codeqlCodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security项目地址https://gitcode.com/gh_mirrors/co/codeql点击查看免费下载相关推荐Alex.js高级用法自定义规则和扩展检测范围的完整教程Alex.js高级用法自定义规则和扩展检测范围的完整教程 Alex.js是一个强大的敏感语言检测工具能够帮助开发者发现写作中的不敏感、不周到表达。在前100开发工具LintFoundry 符号执行改进无分支饱和乘法与 checked-multiply guard 的形式化证明Foundry 符号执行改进无分支饱和乘法与 checked multiply guard 的形式化证明 本篇文章基于 Foundry 仓库 .changel区块链开发工具一文读懂SAM 3D BodyMeta最新3D人体网格恢复技术如何突破遮挡与复杂视角难题一文读懂SAM 3D BodyMeta最新3D人体网格恢复技术如何突破遮挡与复杂视角难题 SAM 3D Body是Meta推出的革命性3D人体网格恢复技术能上一篇如何通过Thorium浏览器获得3大性能优势终极编译优化与隐私保护指南下一篇告别乱码5分钟掌握GBK到UTF-8编码转换的必备技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表