审查技能库(上):内存安全四件套

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

一、为什么内存安全是"死机级"第一层?

回顾第二篇的架构,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层,对应"内存安全"的缓冲区维度。只要代码里出现memcpystrcpysprintf、数组写入,就自动触发。

2.1 核心原则:交叉验证 + 追踪传播

这个技能的设计灵魂,是一条反直觉的原则:

看到memcpy使用了一个长度变量,但前面没有对该变量的边界检查 → 这就是问题,必须报告。

通用AI审查缓冲区溢出,只会看"有没有用strncpy"。而嵌入式专家会追踪长度变量的来源和传播路径

  1. 长度变量是否先校验再使用?(检查-使用时序)
  2. 检查和使用之间是否可能被篡改?(TOCTOU竞态)
  3. 循环索引i < len中的len是否可能超出数组容量
  4. 长度是否来自外部输入或共享变量?

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/freepvPortMalloc/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 强制规范:五条铁律

技能里固化了内存管理的强制规范:

  1. 分配后立即判空,未判空禁止合入
  2. 成对出现:一次分配对应一次释放
  3. 释放后置NULLfree(ptr); ptr = NULL;
  4. 优先级:栈内存 > 静态内存 > 动态内存
  5. 禁止场景:中断函数、高频函数(每秒调用>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最容易翻车的领域。

内存是地基,地基塌了,楼再高也是白搭。

相关推荐
XiHongShi201644 分钟前
cpuz分享,建议保存
单片机·学习
纪卓志George1 小时前
打破语言范式:在 Go 里用动态代理实现 AOP
架构·go
明达智控技术1 小时前
告别停机内卷!热插拔远程IO,解锁工控运维新范式
分布式·物联网·自动化
ZYJCSZKJ1 小时前
基于微服务架构的本地生活POI团购系统设计与高并发实践
微服务·架构·生活
kkai人工智能1 小时前
GPT-6 细节泄露:提示词工程已死,蜂群 Agent 正式接管 AI 架构
人工智能·gpt·架构
wtblszn2 小时前
城市内河在线监测系统方案
运维·物联网
rtu遥测终端机2 小时前
既要通信可靠,又要边缘算力:四信系列车载网关选型指南
物联网·自动驾驶
漂着的圆木2 小时前
MCP 2026-07-28 长任务改造:别再用 HTTP 超时判断任务失败
分布式·架构·状态模式·ai agent
LorryJovens2 小时前
【LAAP架构与安全伦理】LAAP框架与具身智能大脑:从认知架构到自主意识与人机共生社会--LAAP先导愿景片发布
人工智能·架构·agi