资讯动态

Polyspace静态分析入门:排查嵌入式C/C++溢出与死代码

发布时间:2026/10/27 22:04:29 来源:尧图企业网站定制
简介《Polyspace工具使用说明》是一份面向嵌入式软件开发者、测试与代码审核人员的静态分析入门材料围绕C/C代码的运行时错误检测、编码规则一致性、静态度量与测试覆盖度展开帮助读者理解如何借助Polyspace提升代码鲁棒性与安全性。资源包内共1个PDF文件大小约828KB篇幅集中便于按主题查阅。内容覆盖Polyspace基本作用、新建工程与导入源文件、分析模块选择、配置项设置及检查运行流程并分别说明Bug Finder的静态检测与Code Prover的深层验证。对Output Summary、Dashboard、Result List、Source和Result Details等结果窗口的解读也有交代包括风险饼图、覆盖度柱状图、错误类型统计以及绿、红、橙、灰四级代码安全标识和风险成因定位并借示例源函数演示死代码、潜在溢出等问题的排查过程。目前已有2879人学习适合需要快速建立Polyspace使用框架、对照示例代码理解静态分析结果的读者。1. 什么是 Polyspace先把它和“调试”分开刚接触嵌入式软件的人常有个困惑代码明明编译通过了烧到板子上跑测试也没崩为什么老工程师还要拿一个工具再把源码从头扫一遍这有点像每年体检——你自己没觉得哪里疼验血报告还是会告诉你几项指标偏高。Polyspace 做的就是给 C/C 代码做体检这件事它不运行你的程序而是把源码逐行读一遍指出哪一行可能溢出、哪一段永远执行不到、哪条编码规则没守住。所以零基础入门 Polyspace第一件要建立的认识是——它是静态分析工具不是调试器也不是仿真器。它适合做嵌入式软件、汽车电子、工业控制这类“改一版就要重新烧一次板子”的方向如果你刚学 C 语言、想找个工具帮自己挑毛病它同样能给你一份很具体的清单。2. 为什么要学 Polyspace编译器不报错不代表代码没病2.1 编译器只管语法管不了你的值域很多人第一次看到静态分析报告都会愣一下这些代码我编译时一个警告都没有怎么到你这里就一堆问题原因是编译器和静态分析盯的根本不是同一件事。编译器关心的是语法对不对、类型搭不搭、函数有没有声明它不会去算signed char加到 127 再加 5 会变成什么也不会管你这个if分支是不是永远进不去。举个最典型的例子signed char在绝大多数嵌入式编译器上取值范围是 -128 到 127。你写res z 5如果z是 127结果就是 132装不进 8 位有符号数里实际落到变量里的值会绕回去变成负数。编译器不会拦你因为语法完全合法板子跑起来也未必立刻崩可能只是在某个极端工况下算出一个离谱的值然后你在现场花三天抓这个 bug。这种问题在 PC 上写业务代码的人可能一辈子碰不到几次但在嵌入式里是家常便饭。Polyspace 的价值就在这里它在你提交代码之前把这类“极端工况才炸”的路径先扫一遍。入门阶段不需要理解它内部用的抽象解释、值域分析这些词你只要记住一句话——它替你把所有可能的输入都想过一遍而不是像测试那样只跑了你写的那几组用例。2.2 两个组件分工Bug Finder 查缺陷Code Prover 查运行时Polyspace 里最容易被新手搞混的是它有两个看起来很像的分析模块。名字都带 Polyspace界面也长得差不多但查的东西和给出的结论差别很大。入门时先把这张表记住后面建工程选模块就不会选错。对比项Polyspace Bug FinderPolyspace Code Prover检查方式按缺陷模式和编码规则逐条匹配对变量的取值范围做推演验证运行时行为是否需要运行代码不需要不需要典型输出缺陷列表、规则违背项、代码度量按颜色标记每一行代码的安全等级结论口吻“这里可能有问题”“这里一定安全 / 一定出错 / 取决于外部输入”适合什么时候用日常提交前扫一遍卡编码规范关键模块交付前做深度验证简单说Bug Finder 更像一个经验丰富的审查员拿着检查清单一条条对你有没有用未初始化的变量、有没有数组越界、MISRA 规则里哪条没遵守、这个文件有多少行代码、圈复杂度多少。它输出的是“问题条目”一条一条列给你。Code Prover 则更像做数学证明它把每个变量的可能取值范围推出来然后判断某一行代码在这个范围下会不会出事。所以它的结果不是条目而是颜色——绿色表示在所有可能输入下都安全红色表示确定会出事橙色表示取决于外部条件灰色表示这行根本执行不到。这两种视角互补入门阶段建议先用 Bug Finder 建立感觉等你能看懂报告结构了再上 Code Prover 做深一层验证。提示两个模块的分析流程几乎一样都是建工程、导源码、选模块、配参数、跑分析。所以你只需要学会一遍流程切换模块就是换个选项的事。2.3 一段 19 行的小代码藏着四处隐患光讲概念容易飘直接看一段代码。这段代码短到你可以背下来但它同时包含了溢出、死代码、外部依赖三类问题是新手练习最合适的样本。/* fun_test.c一段不到 20 行的示例函数故意留了几处隐患 */ extern signed char extFunc(signed char y); /* 外部函数只有声明没有实现 */ signed char firstfun(void) { signed char x 1; /* 初值确定x 恒为 1 */ signed char y; signed char z; signed char res; y x 5; /* 因为 x 恒为 1所以 y 恒为 6 */ if (y 0) { /* 6 0 永远不成立这个分支进不去 */ y 0; } z extFunc(y); /* z 的值完全由外部函数决定工具看不到它内部 */ if (z 123) { res z 5; /* z 若取 127127 5 132超出 signed char 上限 */ } else { res z 6; /* 另一条分支同样存在加法越界 */ res res / 2; } return res; }逐点解释一下这段代码为什么会被工具盯上。第一处x被赋值为 1 之后没有再改过所以y x 5的结果恒为 6if (y 0)这个判断永远为假花括号里的y 0;是一次都执行不到的死代码。第二处z来自extFunc这个函数只有声明没有实现工具无法知道它返回什么只能按signed char的完整范围 -128 到 127 去估算。第三处res z 5在z取 123 到 127 时会超过 127 的上限发生有符号溢出。第四处else分支里的res z 6有同样的问题只是方向不同。这里有个参数值得单独说signed char的取值范围取决于编译目标的设置。有些嵌入式编译器默认char是无符号的这时候变量范围会变成 0 到 255溢出位置也跟着变。这不是玄学是编译选项决定的你在配置分析时会遇到这个开关到第 3 章再具体说。2.4 新手先盯哪几类告警第一次跑完分析报告里可能出现几十上百条结果。新手最容易犯的错是从第一条开始逐条抠抠到第十条就烦了然后关掉工具再也不打开。正确的顺序是按危害排优先级先看会真正让程序算错的再看影响可维护性的。我的建议是这样一个顺序先看数组越界、指针越界、除零、有符号溢出这几类它们会直接算出错误结果再看未初始化变量和返回值未检查这类问题在嵌入式里经常表现为偶发死机然后看死代码它往往说明你的逻辑写错了或者条件写反了最后才看编码规则一致性比如 MISRA 那一类它们更多是团队协作和可维护性的问题不会立刻让程序崩。至于静态度量比如代码行数、函数调用次数、圈复杂度那是给项目管理和代码评审用的参考数据入门阶段扫一眼知道有这回事就行不用纠结数值高低。后面第 4 章讲怎么看懂这些结果窗口第 5 章讲新手最容易在哪儿翻车。3. 第一次跑通从新建工程到看见第一条告警3.1 准备一个能被分析的最小工程不要一上手就拿公司项目的主控代码去分析几千个文件、几十个头文件路径光是编译配置就能卡你半天。第一次跑目标是“看见结果”不是“查出所有问题”。所以我建议你自己搭一个只有三个目录的小工程把第 2 章那段fun_test.c放进去。demo_project/ ├── src/ │ └── fun_test.c # 待分析的源文件只有这一个 ├── include/ # 头文件目录本例为空但目录要建着 └── polyspace/ # 分析工程文件放在这里和源码分开为什么要把分析工程单独放一个目录因为 Polyspace 会在工程目录下生成一堆中间文件和结果文件如果和工作源码混在一起以后你提交代码时很容易把这些几百兆的结果文件一起提交上去。这个习惯我从第一次用就养成了后面换项目也没改过。源文件就照抄第 2 章那段一个字都不用改。它足够短短到你跑完之后可以自己推一遍工具给的结论对不对这对建立信任感很重要——你要能验证工具说的对才会真的用它。3.2 五步建工程每一步在填什么打开 Polyspace 之后新建工程的入口在菜单里点进去会看到一个向导。向导里有五个地方需要你填下面按顺序说清楚每一处是干什么的以及第一次该怎么填。第一步新建工程。给它起个名字比如demo_first位置选到刚才那个polyspace/目录。这一步只是建一个容器还没有任何代码进去。第二步导入分析代码。这里有两个输入框最容易被新手忽略。一个是 Project Source Files填你要分析的源文件本例就是fun_test.c的完整路径另一个是 Project Include Folders填头文件搜索路径。本例的fun_test.c只声明了一个外部函数没有#include任何头文件所以这一栏可以空着但目录建议先建好因为一旦你的代码里出现#include xxx.h工具找不到这个头文件就会在分析开始时直接报错停下。第三步选择分析模块。向导会让你选这次跑哪个模块Bug Finder 还是 Code Prover。第一次建议选 Bug Finder原因很实际它出结果快报告结构是条目式的比一堆颜色更好理解。选完之后向导里对应会出现 Module Source Files、Configuration、Result 三个区域分别对应“分析哪些文件”“用什么配置”“结果存哪里”。第四步配置分析项。Configuration 这一栏里有一堆选项第一次全部保持默认一个都别改。默认配置已经能查出溢出和死代码够你跑通第一个例子了。等你能看懂报告了再回头调这些参数。第五步运行检查。点开始工具会先编译一遍你的源码然后开始分析。这里有个小选项是“是否生成新的结果文件”第一次勾上方便你跑完之后反复打开看不用重跑。注意分析前的编译和你平时用编译器编译不是一回事。工具内部会用自己的前端把源码解析一遍如果这一步失败后面的分析根本不会开始。所以看到红色的编译错误先解决它别急着怀疑分析功能。3.3 配置项别乱动先只关心这三处虽然建议第一次全用默认但有三处配置你迟早要碰早点知道它们在哪、影响什么后面少走弯路。第一处是目标编译器相关的设置特别是char是否有符号。前面说过signed char和unsigned char的取值范围差一倍直接影响溢出判断的结论。如果你的代码要跑在某个特定的嵌入式编译器上这里要选成和它一致的设置否则工具算出来的溢出位置和你实际运行时的位置对不上你会以为是误报。第二处是外部函数的处理方式。像extFunc这种只有声明没有实现的函数工具默认按返回类型的完整范围去估。你可以给它写一个简单的桩函数或者直接用约束语句告诉工具“这个函数返回值在 0 到 100 之间”橙色告警就会大幅减少。这个技巧在真实项目里非常有用因为嵌入式代码大量依赖硬件寄存器读取函数那些函数的返回值范围你其实是知道的。第三处是检查规则集。默认配置下 Bug Finder 会开一批通用缺陷检查MISRA 那类编码规则通常是可选项。第一次跑不要全开全开的结果是报告里几百条规则违背把真正危险的内存问题淹没了。3.4 点下运行之后第一眼看 Output Summary分析跑起来之后界面上会有一个输出摘要窗口实时刷进度和日志。这个窗口新手往往一扫而过其实它是最该先看的地方。如果分析正常结束这里会告诉你一共分析了几个文件、耗时多少、生成了什么结果。如果中途出问题这里会冒出红色或黄色的提示。红色通常意味着编译阶段就失败了比如文件路径写错、头文件找不到、语法有 tool 不认识的地方黄色一般是警告比如某个文件被跳过了、某个配置项取值不太对但还能继续跑。我第一次跑的时候就栽在这一步源文件路径里手写了一个中文目录名工具没找到文件输出窗口刷了一行黄色警告就继续跑下去了最后结果是空的。我盯着空报告看了十分钟以为是工具没装好。所以养成习惯——跑完先扫一眼这个窗口有没有红黄提示再去读结果。4. 看懂结果五个窗口按什么顺序读4.1 Output Summary 和 Dashboard先看颜色比例分析结束之后结果会分成几个窗口展示。第一个要看的是 Dashboard它把整体情况浓缩成几张图。最上面通常是风险数量的饼图红色代表高风险粉色代表低风险。你不需要记住每一条是什么先看比例如果红色占了一大半说明这份代码的问题密度很高可能连基本的初始化都没做好如果红色只有一两块那就可以逐条看了。旁边一般是分析覆盖度的柱状图它告诉你这次分析覆盖了多少函数、多少分支覆盖度低往往意味着有大量代码因为编译问题被跳过了这时候报告再漂亮也不可信。再旁边是错误类型和数量的分布图。这张图对新手特别友好它能一眼告诉你这份代码的主要问题集中在哪是溢出多还是未初始化多还是规则违背多。你按类型去处理比按文件顺序一条条抠效率高得多。4.2 Result List 和 Source从告警跳到那一行Dashboard 上的每一块颜色都可以点点完之后 Result List 窗口会列出对应的具体条目。每条记录一般包含几个信息问题类型、所在文件、行号、数量。拿第 2 章那段代码举例Result List 里大概率会出现这么几条一条是“死代码”位置在if (y 0)里面的y 0;那一行另外两条是“可能溢出”位置分别在res z 5和res z 6。这和我们自己推的结论是一致的正好可以拿来验证工具的判断。点中某一条Source 窗口会自动跳到对应的源码位置。这个窗口里有几个视觉标记值得记住行号左边会出现一个小圆点或圆圈标出问题所在行有问题的代码行会被染色如果问题是由前面几行推导出来的那些行下面会有小点标记鼠标移上去会弹出一段说明告诉你“因为 x 在这里被赋值为 1所以 y 恒为 6”。这个交互设计其实是整个工具里最有价值的部分。新手看报告最怕的就是“它说这里有问题但我看不出为什么”而那些小点就是把推理链条摊开给你看。我第一次用的时候就是靠这个功能才真正明白“值域推演”这四个字在做什么。4.3 Result Details把“为什么报”翻译成人话如果你点开某一条结果的详情会看到一个更详细的说明窗口。它一般会讲三件事这个问题属于哪一类、触发它的条件是什么、可能导致什么后果。比如溢出那条详情里会写清楚变量z的取值范围是 -128 到 127z 5的结果范围是 -123 到 132而目标类型signed char的上限是 127因此在z大于等于 123 时结果会被截断。这段话读起来有点绕但它其实就是在替你把数学推一遍。新手常见的误区是只看结论不看详情然后凭感觉改代码。比如看到溢出就把res改成int改完之后溢出没了但你可能因此改变了函数的返回类型调用方全部要跟着改问题从“算错”变成“编译不过”。先读详情搞清楚触发条件再决定是改类型、加范围约束还是在上游就把z限死。4.4 Code Prover 的四种颜色对应四种处理方式如果你切到 Code Prover 跑同一段代码结果呈现方式会完全不同Source 窗口里每一行代码都被染上了颜色。四种颜色的含义一定要记牢它们直接对应四种处理动作。绿色表示这行代码在所有可能的输入下都安全你不用管它。红色表示确定会出问题这种必须改没有商量余地。橙色表示取决于外部条件比如某个外部函数的返回值、某个全局变量的范围这类要逐个确认——如果你能证明实际运行时不会取到危险值可以通过加约束让它变绿如果证明不了就得加保护代码。灰色表示死代码永远执行不到要么是逻辑写错了要么是可以删掉的冗余。这四种颜色里新手最容易误解的是橙色。看到一片橙色会觉得“这工具是不是什么都报”其实橙色恰恰是最有价值的一类——它在告诉你“这里的正确性依赖一个你自己也没把握的假设”。把橙色一条条确认掉的过程就是把你脑子里那些模糊的假设变成明确约束的过程。5. 避坑与常见问题排查新手最容易翻车的五个地方5.1 一上来满屏红色编译错误现象点下运行之后Output Summary 里刷出一堆红色提示大意是找不到某个文件或者某个符号未定义分析直接中断一行结果都没有。原因绝大多数情况是头文件路径没配对。你的源码里写了#include xxx.h但这个头文件所在的目录没有加到 Include Folders 里工具解析到这一行就断了。另一种情况是源文件列表里漏了某个.c文件导致里面定义的函数在别处被调用时找不到实现。解决先看红色提示里点名的是哪个文件、哪一行顺着这个线索找它需要的头文件在哪个目录把那整个目录加进 Include Folders。如果是符号未定义检查 Source Files 列表是否完整。这个排查过程看起来笨但比重装工具快得多。5.2 结果全是橙色看着像全错现象Code Prover 跑完满屏橙色几乎没有绿色你开始怀疑自己写的代码是不是一无是处。原因橙色不是“错”是“不确定”。最常见的不确定来源是外部函数——硬件抽象层的读寄存器函数、通信收到的数据、从 flash 里读出来的配置这些值工具都看不到范围只能按类型全范围估。一旦某个变量范围被放大它参与的每一次运算都会传染成橙色。解决给关键的外部函数加返回值约束或者写一个简单的桩函数返回一个有界值。约束写完重跑你会发现一大片橙色变绿。这个过程其实是在逼你把“我假设这个寄存器读出来在 0 到 255 之间”这种口头假设写成代码长期看是好事。5.3 灰色死代码被当成误报现象报告里标了一片灰色说这段代码永远执行不到但你确信业务逻辑上它是有用的分支。原因死代码判定是基于值域推演得出的如果工具认为条件恒真或恒假就会把对应分支标灰。常见触发场景是某个标志位在你分析的这份代码里从来没被赋过其他值或者循环条件里的变量范围被推成了空集。还有一种情况是这段代码只在中断里被修改而工具的分析是单线程视角。解决先顺着灰色标记往上找看是哪个变量的范围导致条件恒成立。如果确认是中断或并发场景下才会改变那属于分析配置的问题需要在配置里说明这个变量的变化来源或者在分析时把它设为 volatile 并给出范围。别直接忽略灰色它十有八九在提示你某处逻辑和你以为的不一样。5.4 同一份代码两次跑结果不一样现象代码没改换了台机器或者隔天再跑告警数量变了多了几条也少了几乎。原因多半是配置没有跟着工程走。Polyspace 的工程文件会记录一部分配置但编译目标设置、规则集开关这类选项如果是在界面临时改的而没有保存进工程换台机器就回到默认值了。另一种可能是两次分析用的源码版本不同你以为没改其实同事提交了一版。解决把关键配置固定下来写进工程文件团队里统一用同一份工程配置。跑分析前确认代码版本分析后把结果文件和配置一起归档。这个习惯听起来麻烦但出问题回溯时能救命。5.5 第一次就开全规则报告几千条现象想着“既然要用就用到极致”把能开的检查规则全勾上跑完出来几千条结果看一眼就关掉了。原因编码规则类检查和缺陷类检查混在一起前者数量级远大于后者而且很多规则违背并不影响功能只是风格问题。报告被淹没之后真正危险的内存越界和溢出反而找不到了。解决分两轮跑。第一轮只开缺陷类检查把会算错结果的问题清干净第二轮再开规则集而且按模块分批处理一次只看一个文件的规则问题。这个过程我踩过坑第一次全开的结果就是放弃使用工具三个月。6. 下一步学什么从会跑例子到敢在项目里用跑通了第一个例子、能看懂红橙绿灰这已经足够让你在团队里接手静态分析这部分工作了。接下来怎么往上走我按三个台阶说。阶段目标具体做什么第一台阶把流程跑顺换三份不同的源码各跑一遍包括一份带多目录头文件的项目练到不用看文档就能建工程第二台阶会收敛结果学会给外部函数加约束、写桩函数把橙色压下去让报告里的每一条你都能解释第三台阶接进流程把分析配置固定成模板每次提交前跑一次 Bug Finder只关注新增的红色条目第一台阶的关键是换环境。你现在只在一个单文件工程上跑过真实项目的目录结构、头文件依赖、第三方库会复杂得多早点碰这些麻烦比晚点碰好。建议找一个有两三层目录、带几个#include的小项目练手专门练头文件路径配置这一项。第二台阶是分水岭。能不能把橙色收敛掉决定了你是在“用工具”还是“被工具用”。做法就是给每一个不确定的来源找答案这个寄存器读出来到底几位有效这个通信帧的长度字段有没有上限找到答案就写成约束写不了就加保护代码。这个过程走完一遍你会发现自己对代码的理解比写的时候清楚多了。第三台阶是把它变成习惯。一个人的时候可以靠自觉进了团队就得靠流程。把配置固化成模板把分析卡在提交之前只比对新增的红色条目老的存量问题单独排期处理。最后说个我自己的教训。刚学的时候我为了图快看报告只扫一眼红色条目橙色和灰色一概跳过觉得那是工具在瞎操心。结果有个模块上线后偶发死机回头一查正是当初被我跳过的一条橙色——某个外部函数的返回值我默认它不会超过 100实际现场有一批硬件返回了 200 多。从那以后我每次跑完分析橙色条目一律逐条过一遍哪怕当场判不了也要写个备注绝不留给“以后再说”。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑