Skip to content

vg_lite_hal: 修复空闲块合并不校验地址相邻 + 分配遍历加防死循环护栏 - #72

Open
zylugl wants to merge 1 commit into
RT-Thread-Studio:mainfrom
zylugl:fix/vg-lite-hal-heap-merge
Open

vg_lite_hal: 修复空闲块合并不校验地址相邻 + 分配遍历加防死循环护栏#72
zylugl wants to merge 1 commit into
RT-Thread-Studio:mainfrom
zylugl:fix/vg-lite-hal-heap-merge

Conversation

@zylugl

@zylugl zylugl commented Jul 25, 2026

Copy link
Copy Markdown

在 Edgi-Talk(PSE84)上启用 LVGL VGLite GPU 渲染(GC555)后,遇到 UI 线程无声卡死(无断言、无 HardFault)。逐层定位到 vg_lite_hal_allocate_contiguous() 中堆链表遍历死循环,根本原因是本文件堆管理实现的两处缺陷:

缺陷 1:vg_lite_hal_free_contiguous() 合并不相邻空闲块

原实现合并前/后节点时只判断 status == 0(空闲),不校验地址是否相邻,并用 min() 调整 offset:

if (pos->status == 0) {
    node->size += pos->size;
    if (node->offset > pos->offset)
        node->offset = pos->offset;   /* 两个不相邻块被合并成一个"假象块" */
    ...
}

当相邻链表节点对应的显存块并不连续时,会合并出一个覆盖空洞的"假象空闲块",其 size/offset 与实际不符;该块后续被分配出去即形成 use-after-free,进而写坏链表指针。

缺陷 2:vg_lite_hal_allocate_contiguous() 遍历无护栏

链表一旦被破坏(哪怕由其他原因,如越界写),反向遍历会沿损坏的 prev 指针无限循环或访问野指针,表现为整个线程卡死且无任何日志。

修复内容

  1. 合并前校验地址相邻:仅当邻居空闲且 node->offset + node->size == pos->offset(后邻)/ pos->offset + pos->size == node->offset(前邻)时才合并,同时把两个 for 循环简化为单次判断(原循环体末尾无条件 break,实际只执行一次)。
  2. 分配遍历加护栏:检测 NULL 指针、自环,并以 256 步为上限,发现链表损坏时打印错误并返回 VG_LITE_OUT_OF_RESOURCES,不再挂死调用线程。

验证

  • 硬件:KIT_PSE84_EVAL_EPC2,RT-Thread 5.0.2,LVGL 9.2 + VGLite(LV_USE_DRAW_VG_LITE=1
  • 修复前:首帧渲染即挂死(栈溢出诱发堆链表损坏后,分配遍历死循环)
  • 修复后:UI 正常渲染(填充、圆角、渐变、文本均走 GPU),长时间运行无异常;故意破坏链表时返回错误而非挂死

在 Edgi-Talk(PSE84)上启用 LVGL VGLite GPU 渲染(GC555)后,遇到 UI 线程无声卡死(无断言、无 HardFault)。逐层定位到 `vg_lite_hal_allocate_contiguous()` 中堆链表遍历死循环,根本原因是本文件堆管理实现的两处缺陷:

### 缺陷 1:`vg_lite_hal_free_contiguous()` 合并不相邻空闲块

原实现合并前/后节点时只判断 `status == 0`(空闲),**不校验地址是否相邻**,并用 `min()` 调整 offset:

```c
if (pos->status == 0) {
    node->size += pos->size;
    if (node->offset > pos->offset)
        node->offset = pos->offset;   /* 两个不相邻块被合并成一个"假象块" */
    ...
}
```

当相邻链表节点对应的显存块并不连续时,会合并出一个覆盖空洞的"假象空闲块",其 size/offset 与实际不符;该块后续被分配出去即形成 use-after-free,进而写坏链表指针。

### 缺陷 2:`vg_lite_hal_allocate_contiguous()` 遍历无护栏

链表一旦被破坏(哪怕由其他原因,如越界写),反向遍历会沿损坏的 `prev` 指针无限循环或访问野指针,表现为整个线程卡死且无任何日志。

## 修复内容

1. **合并前校验地址相邻**:仅当邻居空闲且 `node->offset + node->size == pos->offset`(后邻)/ `pos->offset + pos->size == node->offset`(前邻)时才合并,同时把两个 for 循环简化为单次判断(原循环体末尾无条件 `break`,实际只执行一次)。
2. **分配遍历加护栏**:检测 NULL 指针、自环,并以 256 步为上限,发现链表损坏时打印错误并返回 `VG_LITE_OUT_OF_RESOURCES`,不再挂死调用线程。

## 验证

- 硬件:KIT_PSE84_EVAL_EPC2,RT-Thread 5.0.2,LVGL 9.2 + VGLite(`LV_USE_DRAW_VG_LITE=1`)
- 修复前:首帧渲染即挂死(栈溢出诱发堆链表损坏后,分配遍历死循环)
- 修复后:UI 正常渲染(填充、圆角、渐变、文本均走 GPU),长时间运行无异常;故意破坏链表时返回错误而非挂死
@Rbb666

Rbb666 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

不建议直接修改英飞凌官方库文件的代码,如果有问题可以去英飞凌官方的仓库提交PR,谢谢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants