内存安全是嵌入式的生死线。缓冲区溢出、内存泄漏、栈溢出、结构体设计失误------每一个都能让你的板子"随机崩溃"。
一、为什么内存安全是"死机级"第一层?

回顾第二篇的架构,14个审查技能按领域分为五组。其中内存安全类 是优先级最高的一组,对应AI评审8层扫描清单的第1层 ,风险等级标注为**🔴 致命**。
为什么是它排第一?因为内存问题有一个共同特征:不一定会立刻暴露,但一旦爆发就是灾难级的。
| 内存问题 | 表面现象 | 真实后果 |
|---|---|---|
| 缓冲区溢出 | 偶尔数据错乱 | 覆盖相邻变量/返回地址,随机死机 |
| 内存泄漏 | 系统运行正常 | 几小时/几天后堆耗尽,malloc返回NULL |
| 栈溢出 | 某函数偶尔崩溃 | 破坏任务栈/TCB,系统频繁重启 |
| 结构体对齐错误 | 读取的寄存器值不对 | 硬件操作全部失效 |
更麻烦的是,这类问题极难调试 :可能是偶发(一万次出现一次)、可能是海森堡bug(加了日志就不复现)、可能只出现在特定环境。所以嵌入式的铁律是------预防远比调试重要。
内存安全四件套,就是把这四类最致命的风险,固化成四个专项审查技能:
| 技能 | 检查项数 | 核心战场 |
|---|---|---|
buffer_overflow_check |
10项 | 缓冲区溢出、数组越界、危险函数 |
memory_leak_check |
12项 | 泄漏、堆碎片、野指针、Double Free |
stack_overflow_check |
10项 | 局部大数组、递归、RTOS栈、ISR栈 |
struct_best_practice_check |
18种用法 | 结构体设计、对齐、寄存器映射 |
下面是内存安全四件套的整体架构与优先级关系:
#mermaid-svg-bTAMkugAY5NVlw7S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bTAMkugAY5NVlw7S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bTAMkugAY5NVlw7S .error-icon{fill:#552222;}#mermaid-svg-bTAMkugAY5NVlw7S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bTAMkugAY5NVlw7S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bTAMkugAY5NVlw7S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bTAMkugAY5NVlw7S .marker.cross{stroke:#333333;}#mermaid-svg-bTAMkugAY5NVlw7S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bTAMkugAY5NVlw7S p{margin:0;}#mermaid-svg-bTAMkugAY5NVlw7S .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bTAMkugAY5NVlw7S .cluster-label text{fill:#333;}#mermaid-svg-bTAMkugAY5NVlw7S .cluster-label span{color:#333;}#mermaid-svg-bTAMkugAY5NVlw7S .cluster-label span p{background-color:transparent;}#mermaid-svg-bTAMkugAY5NVlw7S .label text,#mermaid-svg-bTAMkugAY5NVlw7S span{fill:#333;color:#333;}#mermaid-svg-bTAMkugAY5NVlw7S .node rect,#mermaid-svg-bTAMkugAY5NVlw7S .node circle,#mermaid-svg-bTAMkugAY5NVlw7S .node ellipse,#mermaid-svg-bTAMkugAY5NVlw7S .node polygon,#mermaid-svg-bTAMkugAY5NVlw7S .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bTAMkugAY5NVlw7S .rough-node .label text,#mermaid-svg-bTAMkugAY5NVlw7S .node .label text,#mermaid-svg-bTAMkugAY5NVlw7S .image-shape .label,#mermaid-svg-bTAMkugAY5NVlw7S .icon-shape .label{text-anchor:middle;}#mermaid-svg-bTAMkugAY5NVlw7S .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bTAMkugAY5NVlw7S .rough-node .label,#mermaid-svg-bTAMkugAY5NVlw7S .node .label,#mermaid-svg-bTAMkugAY5NVlw7S .image-shape .label,#mermaid-svg-bTAMkugAY5NVlw7S .icon-shape .label{text-align:center;}#mermaid-svg-bTAMkugAY5NVlw7S .node.clickable{cursor:pointer;}#mermaid-svg-bTAMkugAY5NVlw7S .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bTAMkugAY5NVlw7S .arrowheadPath{fill:#333333;}#mermaid-svg-bTAMkugAY5NVlw7S .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bTAMkugAY5NVlw7S .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bTAMkugAY5NVlw7S .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bTAMkugAY5NVlw7S .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bTAMkugAY5NVlw7S .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bTAMkugAY5NVlw7S .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bTAMkugAY5NVlw7S .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bTAMkugAY5NVlw7S .cluster text{fill:#333;}#mermaid-svg-bTAMkugAY5NVlw7S .cluster span{color:#333;}#mermaid-svg-bTAMkugAY5NVlw7S div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bTAMkugAY5NVlw7S .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bTAMkugAY5NVlw7S rect.text{fill:none;stroke-width:0;}#mermaid-svg-bTAMkugAY5NVlw7S .icon-shape,#mermaid-svg-bTAMkugAY5NVlw7S .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bTAMkugAY5NVlw7S .icon-shape p,#mermaid-svg-bTAMkugAY5NVlw7S .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bTAMkugAY5NVlw7S .icon-shape .label rect,#mermaid-svg-bTAMkugAY5NVlw7S .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bTAMkugAY5NVlw7S .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bTAMkugAY5NVlw7S .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bTAMkugAY5NVlw7S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AI评审8层扫描清单
第1层:内存安全类 🔴 致命
buffer_overflow_check
缓冲区溢出审查 · 10项
memory_leak_check
内存泄漏审查 · 12项
stack_overflow_check
栈溢出审查 · 10项
struct_best_practice_check
结构体最佳实践 · 18种用法
随机死机
堆耗尽 · 慢性死亡
系统频繁重启
硬件操作失效
预防远比调试重要
二、buffer_overflow_check:缓冲区溢出审查

定位 :AI评审第1层,对应"内存安全"的缓冲区维度。只要代码里出现memcpy、strcpy、sprintf、数组写入,就自动触发。
2.1 核心原则:交叉验证 + 追踪传播
这个技能的设计灵魂,是一条反直觉的原则:
看到memcpy使用了一个长度变量,但前面没有对该变量的边界检查 → 这就是问题,必须报告。
通用AI审查缓冲区溢出,只会看"有没有用strncpy"。而嵌入式专家会追踪长度变量的来源和传播路径:
- 长度变量是否先校验再使用?(检查-使用时序)
- 检查和使用之间是否可能被篡改?(TOCTOU竞态)
- 循环索引
i < len中的len是否可能超出数组容量? - 长度是否来自外部输入或共享变量?
2.2 三大致命陷阱
陷阱1:长度未校验直接使用
c
// ❌ 错误:len可能超出dst容量!
void process_data(uint8_t *dst, uint8_t *src, uint16_t len) {
memcpy(dst, src, len);
}
// ✅ 正确:先校验后使用
if (len > sizeof(dst)) {
ESP_LOGE(TAG, "长度超限: %u > %u", len, sizeof(dst));
return -1;
}
memcpy(dst, src, len);
这是嵌入式里最常见的bug:调用方传了个长度进来,函数直接memcpy,完全没想过这个长度可能大于目标缓冲区。
陷阱2:先用后检查(时序错误)
c
// ❌ 错误:已经溢出了才想起来检查!
memcpy(dst, src, len);
if (len > MAX_SIZE) return -1; // 为时已晚
// ✅ 正确:先检查后使用
if (len > MAX_SIZE) return -1;
memcpy(dst, src, len);
陷阱3:TOCTOU竞态(Check-Then-Use)
这是嵌入式独有的陷阱,通用AI几乎识别不了:
c
// ❌ 错误:检查和使用之间,中断可能修改共享变量
volatile uint16_t g_data_len; // ISR可能修改
void process(void) {
if (g_data_len <= MAX_SIZE) { // T1: 检查时合法
// 此时ISR触发,g_data_len被改为超大值
memcpy(buf, data, g_data_len); // T2: 使用时已非法
}
}
// ✅ 正确:缓存到局部变量(原子读取一次)
void process(void) {
uint16_t len = g_data_len; // 缓存,后续使用不受ISR影响
if (len <= MAX_SIZE) {
memcpy(buf, data, len);
}
}
TOCTOU的全称是Time-Of-Check-To-Time-Of-Use,即"检查时刻到使用时刻"。中断会在检查和使用之间改变共享变量,让"看似安全的代码"实际溢出。
下面是缓冲区溢出审查的核心流程------交叉验证与追踪传播:
#mermaid-svg-i1stM6zgdGcRzmc5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-i1stM6zgdGcRzmc5 .error-icon{fill:#552222;}#mermaid-svg-i1stM6zgdGcRzmc5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-i1stM6zgdGcRzmc5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-i1stM6zgdGcRzmc5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-i1stM6zgdGcRzmc5 .marker.cross{stroke:#333333;}#mermaid-svg-i1stM6zgdGcRzmc5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-i1stM6zgdGcRzmc5 p{margin:0;}#mermaid-svg-i1stM6zgdGcRzmc5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster-label text{fill:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster-label span{color:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster-label span p{background-color:transparent;}#mermaid-svg-i1stM6zgdGcRzmc5 .label text,#mermaid-svg-i1stM6zgdGcRzmc5 span{fill:#333;color:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 .node rect,#mermaid-svg-i1stM6zgdGcRzmc5 .node circle,#mermaid-svg-i1stM6zgdGcRzmc5 .node ellipse,#mermaid-svg-i1stM6zgdGcRzmc5 .node polygon,#mermaid-svg-i1stM6zgdGcRzmc5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-i1stM6zgdGcRzmc5 .rough-node .label text,#mermaid-svg-i1stM6zgdGcRzmc5 .node .label text,#mermaid-svg-i1stM6zgdGcRzmc5 .image-shape .label,#mermaid-svg-i1stM6zgdGcRzmc5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-i1stM6zgdGcRzmc5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-i1stM6zgdGcRzmc5 .rough-node .label,#mermaid-svg-i1stM6zgdGcRzmc5 .node .label,#mermaid-svg-i1stM6zgdGcRzmc5 .image-shape .label,#mermaid-svg-i1stM6zgdGcRzmc5 .icon-shape .label{text-align:center;}#mermaid-svg-i1stM6zgdGcRzmc5 .node.clickable{cursor:pointer;}#mermaid-svg-i1stM6zgdGcRzmc5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-i1stM6zgdGcRzmc5 .arrowheadPath{fill:#333333;}#mermaid-svg-i1stM6zgdGcRzmc5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-i1stM6zgdGcRzmc5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-i1stM6zgdGcRzmc5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i1stM6zgdGcRzmc5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-i1stM6zgdGcRzmc5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i1stM6zgdGcRzmc5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster text{fill:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 .cluster span{color:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-i1stM6zgdGcRzmc5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-i1stM6zgdGcRzmc5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-i1stM6zgdGcRzmc5 .icon-shape,#mermaid-svg-i1stM6zgdGcRzmc5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-i1stM6zgdGcRzmc5 .icon-shape p,#mermaid-svg-i1stM6zgdGcRzmc5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-i1stM6zgdGcRzmc5 .icon-shape .label rect,#mermaid-svg-i1stM6zgdGcRzmc5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-i1stM6zgdGcRzmc5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-i1stM6zgdGcRzmc5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-i1stM6zgdGcRzmc5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部输入
共享变量
函数参数
否
是
是
否
发现危险函数调用
memcpy / strcpy / sprintf
长度变量来源
🔴 高风险
必须校验
🔴 TOCTOU风险
缓存到局部变量
先校验再使用?
🔴 报告:长度未校验
检查与使用之间
可能被篡改?
🔴 报告:TOCTOU竞态
✅ 安全
对照危险函数表替换
strncpy + 强制结尾'\\0'
2.3 危险函数对照表

技能里内置了一张危险函数对照表,AI照着替换即可:
| 危险函数 | 替代方案 | 注意事项 |
|---|---|---|
strcpy |
strncpy + 强制结尾'\0' |
目标缓冲区必须足够大 |
strcat |
strncat + 强制结尾'\0' |
计算剩余空间 |
sprintf |
snprintf |
检查返回值是否截断 |
gets |
fgets |
禁止使用gets |
scanf("%s") |
scanf("%31s", buf) |
限制最大读取长度 |
memcpy |
先校验len | len ≤ sizeof(dst) |
注意一个容易被忽略的坑:strncpy在src长度≥目标缓冲区时不会自动加'\0',必须手动补:
c
// ❌ strncpy后未强制结尾,字符串可能不带'\0'
strncpy(buf, src, sizeof(buf));
// ✅ 正确:strncpy + 强制结尾
strncpy(buf, src, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
三、memory_leak_check:内存泄漏审查
定位:AI评审第6层(资源与状态机),但归入内存安全四件套,因为泄漏是嵌入式"慢性死亡"的头号元凶。
触发条件 :出现malloc/free、pvPortMalloc/vPortFree(FreeRTOS)、heap_caps_malloc(ESP32)、new/delete、内存池操作。
3.1 12项检查的三大类别
12项检查按风险等级分为三类,我们各看最核心的几项:
致命级:一失足成千古恨
① malloc后未判空直接使用
c
// ❌ 未判空,分配失败时直接崩溃
char *buffer = malloc(size);
memcpy(buffer, data, size); // malloc失败时buffer=NULL,崩溃!
// ✅ 判空后立即返回
char *buffer = malloc(size);
if (buffer == NULL) {
log_error("Memory allocation failed");
return;
}
嵌入式系统没有MMU保护,访问0地址不会像PC那样抛异常,而是直接崩溃。而且malloc失败是间歇性的------堆碎片严重时才触发,极其难排查。
② 分配的内存未释放(经典泄漏)
c
// ❌ 每次调用泄漏1024字节
void send_message(const char *msg) {
char *buffer = malloc(1024);
if (buffer == NULL) return;
sprintf(buffer, "MSG: %s", msg);
uart_send(buffer, strlen(buffer));
// 忘记free(buffer)!
}
// ✅ 成对释放
void send_message(const char *msg) {
char *buffer = malloc(1024);
if (buffer == NULL) return;
sprintf(buffer, "MSG: %s", msg);
uart_send(buffer, strlen(buffer));
free(buffer);
}
③ 重复释放同一内存(Double Free)
c
// ❌ 两个指针指向同一内存,被释放两次
char *buf1 = malloc(128);
char *buf2 = buf1;
...
free(buf1); // 第一次释放
...
free(buf2); // 重复释放!堆结构被破坏
// ✅ 释放后立即置NULL
free(buf1);
buf1 = NULL;
buf2 = NULL;
④ 释放后继续使用(Use After Free)
c
// ❌ 释放后还在用msg->type
free(msg);
log_info("Message type: %d", msg->type); // 释放后使用!
// ✅ 使用完再释放,释放后置NULL
log_info("Message type: %d", msg->type);
free(msg);
msg = NULL;
高危级:温水煮青蛙
⑤ 循环内频繁malloc/free导致堆碎片
c
// ❌ 每次分配不同大小,立即释放 → 产生碎片
for (int i = 0; i < 1000; i++) {
int size = get_packet_size(i);
char *buf = malloc(size); // 频繁分配不同大小
process_packet(buf, size);
free(buf); // 立即释放
}
// ✅ 方案1:预分配固定大小,循环结束后释放
char *buf = malloc(MAX_PACKET_SIZE);
for (int i = 0; i < 1000; i++) {
int size = get_packet_size(i);
if (size > MAX_PACKET_SIZE) continue;
process_packet(buf, size);
}
free(buf);
// ✅ 方案2:使用内存池(固定大小对象的正道)
memory_pool_init(&g_packet_pool, 1024, 10); // 10个1024字节块
char *buf = memory_pool_alloc(&g_packet_pool);
process_packet(buf, 1024);
memory_pool_free(&g_packet_pool, buf);
堆碎片是嵌入式特有的"隐形杀手":总空闲内存充足,但没有连续的大块,导致大分配失败。高频小对象分配,应该用内存池而不是malloc。
中危级:细节决定成败
⑥ 嵌套结构体未按逆序释放
c
// ❌ 先释放外层,内层指针泄漏
free(pkt); // pkt->name和pkt->data都泄漏了
// ✅ 逆序释放:先内后外
free(pkt->data);
free(pkt->name);
free(pkt);
⑦ realloc使用不当
c
// ❌ 失败时buffer=NULL,原内存泄漏!
buffer = realloc(buffer, 1024);
// ✅ 用临时指针接收,成功后再更新
char *new_buf = realloc(buffer, 1024);
if (new_buf == NULL) {
free(buffer); // realloc失败,原buffer仍然有效
return;
}
buffer = new_buf;
下面是内存泄漏 12 项检查的三大类别全景图:
#mermaid-svg-NmyF37umOrIYMSKe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NmyF37umOrIYMSKe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NmyF37umOrIYMSKe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NmyF37umOrIYMSKe .error-icon{fill:#552222;}#mermaid-svg-NmyF37umOrIYMSKe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NmyF37umOrIYMSKe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NmyF37umOrIYMSKe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NmyF37umOrIYMSKe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NmyF37umOrIYMSKe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NmyF37umOrIYMSKe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NmyF37umOrIYMSKe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NmyF37umOrIYMSKe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NmyF37umOrIYMSKe .marker.cross{stroke:#333333;}#mermaid-svg-NmyF37umOrIYMSKe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NmyF37umOrIYMSKe p{margin:0;}#mermaid-svg-NmyF37umOrIYMSKe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NmyF37umOrIYMSKe .cluster-label text{fill:#333;}#mermaid-svg-NmyF37umOrIYMSKe .cluster-label span{color:#333;}#mermaid-svg-NmyF37umOrIYMSKe .cluster-label span p{background-color:transparent;}#mermaid-svg-NmyF37umOrIYMSKe .label text,#mermaid-svg-NmyF37umOrIYMSKe span{fill:#333;color:#333;}#mermaid-svg-NmyF37umOrIYMSKe .node rect,#mermaid-svg-NmyF37umOrIYMSKe .node circle,#mermaid-svg-NmyF37umOrIYMSKe .node ellipse,#mermaid-svg-NmyF37umOrIYMSKe .node polygon,#mermaid-svg-NmyF37umOrIYMSKe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NmyF37umOrIYMSKe .rough-node .label text,#mermaid-svg-NmyF37umOrIYMSKe .node .label text,#mermaid-svg-NmyF37umOrIYMSKe .image-shape .label,#mermaid-svg-NmyF37umOrIYMSKe .icon-shape .label{text-anchor:middle;}#mermaid-svg-NmyF37umOrIYMSKe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NmyF37umOrIYMSKe .rough-node .label,#mermaid-svg-NmyF37umOrIYMSKe .node .label,#mermaid-svg-NmyF37umOrIYMSKe .image-shape .label,#mermaid-svg-NmyF37umOrIYMSKe .icon-shape .label{text-align:center;}#mermaid-svg-NmyF37umOrIYMSKe .node.clickable{cursor:pointer;}#mermaid-svg-NmyF37umOrIYMSKe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NmyF37umOrIYMSKe .arrowheadPath{fill:#333333;}#mermaid-svg-NmyF37umOrIYMSKe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NmyF37umOrIYMSKe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NmyF37umOrIYMSKe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NmyF37umOrIYMSKe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NmyF37umOrIYMSKe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NmyF37umOrIYMSKe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NmyF37umOrIYMSKe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NmyF37umOrIYMSKe .cluster text{fill:#333;}#mermaid-svg-NmyF37umOrIYMSKe .cluster span{color:#333;}#mermaid-svg-NmyF37umOrIYMSKe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NmyF37umOrIYMSKe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NmyF37umOrIYMSKe rect.text{fill:none;stroke-width:0;}#mermaid-svg-NmyF37umOrIYMSKe .icon-shape,#mermaid-svg-NmyF37umOrIYMSKe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NmyF37umOrIYMSKe .icon-shape p,#mermaid-svg-NmyF37umOrIYMSKe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NmyF37umOrIYMSKe .icon-shape .label rect,#mermaid-svg-NmyF37umOrIYMSKe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NmyF37umOrIYMSKe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NmyF37umOrIYMSKe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NmyF37umOrIYMSKe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} memory_leak_check
12项检查
🔴 致命级
一失足成千古恨
🟠 高危级
温水煮青蛙
🟡 中危级
细节决定成败
① malloc后未判空
② 分配未释放
③ 重复释放 Double Free
④ 释放后使用 UAF
⑤ 循环内频繁malloc/free
堆碎片
⑥ 嵌套结构体未逆序释放
⑦ realloc使用不当
五条铁律
分配后立即判空
成对出现
释放后置NULL
栈 > 静态 > 动态
禁止中断/高频函数
3.2 强制规范:五条铁律
技能里固化了内存管理的强制规范:
- 分配后立即判空,未判空禁止合入
- 成对出现:一次分配对应一次释放
- 释放后置NULL :
free(ptr); ptr = NULL; - 优先级:栈内存 > 静态内存 > 动态内存
- 禁止场景:中断函数、高频函数(每秒调用>100次)
还有一条关键实践:统一释放路径 。多分支函数用goto cleanup模式,避免每个return前漏释放:
c
int process_file(const char *path) {
FILE *fp = NULL;
char *buffer = NULL;
int ret = 0;
fp = fopen(path, "r");
if (fp == NULL) { ret = -1; goto cleanup; }
buffer = malloc(1024);
if (buffer == NULL) { ret = -1; goto cleanup; }
int size = fread(buffer, 1, 1024, fp);
if (size < 0) { ret = -1; goto cleanup; }
cleanup:
if (buffer != NULL) free(buffer);
if (fp != NULL) fclose(fp);
return ret;
}
四、stack_overflow_check:栈溢出审查

定位 :AI评审第1层,嵌入式独有且极难排查的一类风险。栈溢出的特点:爆的时候已经晚了。
触发条件 :局部大数组、递归调用、RTOS任务定义(xTaskCreate)、ISR内局部变量、alloca()、可变参数函数。
4.1 嵌入式栈的残酷现实
先建立认知:栈空间有多小?
- ESP32默认任务栈仅4KB
- STM32主栈通常1-4KB
- ISR用主栈(MSP),空间更小
而一个char buf[1024]就吃掉4KB栈的25%。更残酷的是,栈溢出不报错------它悄悄覆盖相邻内存,破坏其他任务的数据或TCB,导致随机崩溃。
4.2 致命级检查项
① 局部大数组直接爆栈
技能给出了量化的检测规则:
局部数组 ≥ 512字节 → 🔴 致命(立即栈溢出)
局部数组 128-512字节 → 🟠 高危(多函数嵌套时溢出)
局部数组 64-128字节 → 🟡 中危(需评估调用链深度)
c
// ❌ 2KB局部数组!ESP32默认栈只有4KB
void wifi_parse_packet(void) {
char rx_buf[2048];
wifi_receive(rx_buf, 2048);
parse_data(rx_buf);
}
// ✅ 方案1:改为static(存.bss段,零栈消耗)
void wifi_parse_packet(void) {
static char rx_buf[2048]; // ✅ 不占用栈
wifi_receive(rx_buf, 2048);
parse_data(rx_buf);
}
② 递归无终止条件
c
// ❌ 解析JSON嵌套无深度限制,恶意数据可爆栈
void parse_json(json_node_t *node) {
if (node->type == JSON_OBJECT) {
for (int i = 0; i < node->child_count; i++) {
parse_json(&node->children[i]); // 无深度限制!
}
}
}
// ✅ 加深度限制
#define MAX_JSON_DEPTH 8
void parse_json(json_node_t *node, int depth) {
if (depth > MAX_JSON_DEPTH) {
log_error("JSON nested too deep");
return;
}
...
}
注意嵌入式里的递归有两重风险:一是栈不够,二是可被攻击者利用做DoS(构造深层嵌套数据触发崩溃)。
③ ISR内定义局部变量
c
// ❌ 中断栈极小,128B数组直接致命
void USART1_IRQHandler(void) {
uint8_t rx_buffer[128]; // ISR栈溢出 → HardFault
HAL_UART_Receive(&huart1, rx_buffer, 128, 100);
...
}
// ✅ 方案1:全局缓冲区
static uint8_t g_isr_rx_buffer[128];
void USART1_IRQHandler(void) {
HAL_UART_Receive(&huart1, g_isr_rx_buffer, 128, 100);
...
}
// ✅ 方案2(更优):ISR只收1字节进队列,解析移到任务
void USART1_IRQHandler(void) {
uint8_t byte;
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
byte = USART1->DR;
xQueueSendFromISR(uart_queue, &byte, NULL); // 只传1字节
}
}
`
下面是栈溢出检测与修复的决策流程:
```mermaid
flowchart TD
A["检测到栈风险"] --> B{"局部数组大小?"}
B -->|"≥ 512字节"| C["🔴 致命<br/>立即栈溢出"]
B -->|"128-512字节"| D["🟠 高危<br/>多函数嵌套时溢出"]
B -->|"64-128字节"| E["🟡 中危<br/>需评估调用链深度"]
C --> F["改static 或 动态分配"]
D --> F
E --> G{"递归调用?"}
G -->|"是"| H["加深度限制 ≤ 8层<br/>优先用循环替代"]
G -->|"否"| I{"ISR内定义变量?"}
I -->|"是"| J["全局缓冲区 或<br/>ISR只收1字节进队列"]
I -->|"否"| K["✅ 通过"]
F --> L["栈估算公式"]
H --> L
J --> L
L --> M["基础开销 + 调用链消耗 + 50%安全余量"]
``
4.3 栈估算公式
这是技能里最有工程价值的部分------量化评估:
任务栈大小 = 基础开销 + 最深调用链栈消耗 + 安全余量
- 基础开销:200-300B(RTOS上下文切换)
- 调用链消耗:逐层累加每个函数的局部变量
- 安全余量:50%(防止编译器优化差异)
// 示例:三层调用链
void task_main(void) { // 128B局部变量
char buf1[128];
process_data(buf1);
}
void process_data(char *data) { // 256B局部变量
char buf2[256];
parse_protocol(buf2);
}
void parse_protocol(char *data) { // 64B局部变量
uint32_t temp[16];
}
// 栈消耗 = 300(基础)+ 128 + 256 + 64 = 748B
// 实际分配 = 748 × 1.5 = 1122B → 建议2048B
强制规范:
- 单个函数栈消耗 < 256B;>128B的数组必须改static或动态分配
- 递归必须有明确深度限制(通常≤8层),优先用循环替代
- RTOS默认任务栈 ≥ 4096字节,启用
configCHECK_FOR_STACK_OVERFLOW - ISR栈消耗必须 < 64B
五、struct_best_practice_check:结构体最佳实践
定位:内存安全四件套里最特殊的一个------它不只是"查错误",更是"教架构"。18种高级用法,覆盖嵌入式C语言结构体设计的全部精华。
触发条件:结构体定义、寄存器映射、函数指针结构体、union组合、柔性数组、位域、对齐。
5.1 为什么结构体也属于"内存安全"?
因为结构体的设计失误,会直接导致内存错乱:
- 寄存器映射结构体漏了
volatile→ 编译器优化掉硬件读取 - 对齐方式不匹配 → 读取的寄存器值错位
- 柔性数组放错位置 → 覆盖相邻内存
- 位域取地址 → 编译错误或未定义行为
5.2 18种用法的四大分类
18种用法可归为四类,每类有对应的检查要点:
① 硬件操作类:寄存器映射与对齐
c
// 寄存器映射:必须volatile + 对齐匹配 + 保留位占位
#pragma pack(push, 4)
typedef struct {
volatile uint32_t CR1; // 控制寄存器1
volatile uint32_t CR2; // 控制寄存器2
volatile uint32_t SR; // 状态寄存器
volatile uint32_t DR; // 数据寄存器
} UART_TypeDef;
#pragma pack(pop)
#define USART1 ((UART_TypeDef *)0x40004400UL)
// 紧凑结构体(协议解析):必须1字节对齐 + 处理大小端
#pragma pack(push, 1)
typedef struct {
uint8_t head;
uint8_t dev_addr;
uint16_t cmd_code;
uint8_t data_len;
uint8_t data[8];
uint8_t crc8;
} uart_frame_t;
#pragma pack(pop)
// UNION+STRUCT组合:寄存器整体读写 + 单bit精准判断
typedef union {
uint16_t reg_val; // 整体读写
struct {
uint16_t conv_flag : 1;
uint16_t ready : 1;
uint16_t reserve : 14;
} bit; // 单bit判断
} adc_reg_t;
② 内存优化类:CONST进Flash + 柔性数组
c
// 只读配置加const,存入Flash,零RAM占用
static const led_cfg_t led_table[] = {
{GPIOA, GPIO_PIN_0, 1, "POWER_LED"},
{GPIOB, GPIO_PIN_5, 0, "RUN_LED"},
};
// 柔性数组:一次性申请头部+变长数据,零碎片
typedef struct {
uint8_t cmd_type;
uint16_t data_len;
uint8_t data[]; // 必须放最后!
} uart_msg_t;
uart_msg_t *msg = malloc(sizeof(uart_msg_t) + len);
memcpy(msg->data, buf, len);
free(msg); // 一次性释放
③ 架构设计类:函数指针解耦 + 表驱动
c
// 函数指针结构体:驱动解耦(面向对象)
typedef struct device {
const char *dev_name;
const dev_ops_t *ops; // const存入Flash
void *priv_data; // 私有配置,隔离实例
uint8_t dev_status;
} device_t;
// 配置表结构体:表驱动编程
static const led_cfg_t led_table[] = { ... };
④ 常见陷阱类:一个都不能踩
1. volatile不能省,否则编译器优化失效
2. 寄存器保留位必须占位
3. 对齐方式必须匹配MCU
4. 位域不能取地址(&status.online是错的)
5. 跨平台通信必须处理大小端
6. 柔性数组必须放最后
7. 回调函数调用前必须判空
六、四件套的设计共性:从技能看方法论
看完四个技能,你会发现它们的设计高度一致,这正是上一篇方法论的实际落地:
6.1 检查项分层
每个技能都按风险等级排列检查项,AI审查时按优先级处理:
致命级 → 必须立即修复(违反即崩溃/数据损坏)
高危级 → 强烈建议修复(可能导致严重问题)
中危级 → 建议修复(影响稳定性或性能)
低危级 → 代码风格或优化建议
6.2 四要素法贯穿
每项检查都是:错误示例 → 正确示例 → 原理分析 → 后果说明。AI不是"感觉有问题",而是"对照检查项第N条,发现违反XX原则,后果是XX,修复方案是XX"。
6.3 嵌入式专属知识
这四个技能之所以是"嵌入式专家"而不是通用审查,在于它们内置了大量通用AI不知道的知识:
- 无MMU保护,访问0地址直接崩溃
- 栈空间只有几KB,ISR栈更小
- DMA缓冲区不能在栈上
- 堆碎片是隐形杀手,要用内存池
- 中断会篡改共享变量(TOCTOU)
- 栈估算要加50%安全余量
- volatile在寄存器映射里不能省
这些知识,就是"专家"和"通用助手"的分水岭。
6.4 代码是跨平台的
每个技能都同时提供ESP32和STM32双平台示例 (heap_caps_malloc/pvPortMalloc、HAL库/ESP-IDF),确保技能在不同嵌入式项目间可复用。
下面是四件套设计共性的架构总览:
#mermaid-svg-ro2UgacPPqODMb2A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ro2UgacPPqODMb2A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ro2UgacPPqODMb2A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ro2UgacPPqODMb2A .error-icon{fill:#552222;}#mermaid-svg-ro2UgacPPqODMb2A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ro2UgacPPqODMb2A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ro2UgacPPqODMb2A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ro2UgacPPqODMb2A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ro2UgacPPqODMb2A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ro2UgacPPqODMb2A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ro2UgacPPqODMb2A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ro2UgacPPqODMb2A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ro2UgacPPqODMb2A .marker.cross{stroke:#333333;}#mermaid-svg-ro2UgacPPqODMb2A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ro2UgacPPqODMb2A p{margin:0;}#mermaid-svg-ro2UgacPPqODMb2A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ro2UgacPPqODMb2A .cluster-label text{fill:#333;}#mermaid-svg-ro2UgacPPqODMb2A .cluster-label span{color:#333;}#mermaid-svg-ro2UgacPPqODMb2A .cluster-label span p{background-color:transparent;}#mermaid-svg-ro2UgacPPqODMb2A .label text,#mermaid-svg-ro2UgacPPqODMb2A span{fill:#333;color:#333;}#mermaid-svg-ro2UgacPPqODMb2A .node rect,#mermaid-svg-ro2UgacPPqODMb2A .node circle,#mermaid-svg-ro2UgacPPqODMb2A .node ellipse,#mermaid-svg-ro2UgacPPqODMb2A .node polygon,#mermaid-svg-ro2UgacPPqODMb2A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ro2UgacPPqODMb2A .rough-node .label text,#mermaid-svg-ro2UgacPPqODMb2A .node .label text,#mermaid-svg-ro2UgacPPqODMb2A .image-shape .label,#mermaid-svg-ro2UgacPPqODMb2A .icon-shape .label{text-anchor:middle;}#mermaid-svg-ro2UgacPPqODMb2A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ro2UgacPPqODMb2A .rough-node .label,#mermaid-svg-ro2UgacPPqODMb2A .node .label,#mermaid-svg-ro2UgacPPqODMb2A .image-shape .label,#mermaid-svg-ro2UgacPPqODMb2A .icon-shape .label{text-align:center;}#mermaid-svg-ro2UgacPPqODMb2A .node.clickable{cursor:pointer;}#mermaid-svg-ro2UgacPPqODMb2A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ro2UgacPPqODMb2A .arrowheadPath{fill:#333333;}#mermaid-svg-ro2UgacPPqODMb2A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ro2UgacPPqODMb2A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ro2UgacPPqODMb2A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ro2UgacPPqODMb2A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ro2UgacPPqODMb2A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ro2UgacPPqODMb2A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ro2UgacPPqODMb2A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ro2UgacPPqODMb2A .cluster text{fill:#333;}#mermaid-svg-ro2UgacPPqODMb2A .cluster span{color:#333;}#mermaid-svg-ro2UgacPPqODMb2A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ro2UgacPPqODMb2A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ro2UgacPPqODMb2A rect.text{fill:none;stroke-width:0;}#mermaid-svg-ro2UgacPPqODMb2A .icon-shape,#mermaid-svg-ro2UgacPPqODMb2A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ro2UgacPPqODMb2A .icon-shape p,#mermaid-svg-ro2UgacPPqODMb2A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ro2UgacPPqODMb2A .icon-shape .label rect,#mermaid-svg-ro2UgacPPqODMb2A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ro2UgacPPqODMb2A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ro2UgacPPqODMb2A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ro2UgacPPqODMb2A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 设计共性
检查项分层
致命/高危/中危/低危
四要素法
错误示例→正确示例→原理→后果
嵌入式专属知识
无MMU/栈几KB/TOCTOU
双平台示例
ESP32 + STM32
内存安全四件套
buffer_overflow_check
memory_leak_check
stack_overflow_check
struct_best_practice_check
方法论落地
--
七、小结
内存安全四件套,是嵌入式智能体审查能力的第一道防线:
| 技能 | 检查项 | 核心防线 | 典型场景 |
|---|---|---|---|
| buffer_overflow_check | 10项 | 缓冲区/数组/字符串越界 | memcpy、strcpy、sprintf、TOCTOU |
| memory_leak_check | 12项 | 泄漏/碎片/野指针 | malloc、内存池、realloc、多分支释放 |
| stack_overflow_check | 10项 | 栈溢出/递归/ISR栈 | 局部大数组、RTOS任务、ISR |
| struct_best_practice_check | 18种用法 | 结构体/对齐/寄存器映射 | 协议解析、寄存器、驱动解耦 |
设计共性:风险分级 + 四要素法 + 嵌入式专属知识 + 双平台示例。
下一篇,我们进入审查技能库(中):硬件与系统安全------中断安全、DMA与Cache一致性、外设冲突、RTOS死锁、看门狗。这是嵌入式最硬核的部分,也是通用AI最容易翻车的领域。
内存是地基,地基塌了,楼再高也是白搭。