CVE-2024-40979 in Linux
摘要
由 VulDB • 2026-06-01
在 ath12k 驱动中,当固件加载或恢复时,会通过 QMI 消息请求内存段。这些内存段的大小和数量由固件决定,并在 `ath12k_qmi_msg_mem_request_cb()` 中记录到 `ab->qmi.target_mem[]` 数组中。
问题出现在以下流程中:
1. **首次加载固件时**: - 固件请求两个大内存段,大小分别为 7077888 和 8454144。 - 驱动尝试分配这些内存,但分配失败(可能因为内存不足或碎片化)。 - 分配失败后,`ath12k_qmi_free_target_mem_chunk()` 被调用以清理已分配的段。 - 由于第一个段分配失败,其 `v.addr` 为 NULL,因此被跳过。 - 第二个段虽然分配成功,但其实际分配的大小是 524288(由日志中的 `qmi mem seg type 4 size 524288` 可知),而不是固件请求的 8454144。
2. **挂起/恢复时**: - 固件再次请求相同的两个大内存段。 - 驱动再次尝试分配,但同样失败。 - 在清理过程中,驱动尝试释放第二个段,但使用的是固件请求的大小 8454144,而不是实际分配的大小 524288。 - 这导致 `dma_free_coherent()` 被调用时传递了错误的大小,从而引发内核崩溃,因为内核检测到正在释放一块正在使用的内存。
### 根本原因
- **内存分配失败后的清理逻辑不正确**:当分配失败时,驱动没有正确记录实际分配的大小,而是使用了固件请求的大小。 - **内存段大小不一致**:固件请求的大小与实际分配的大小不一致,导致在释放内存时出现错误。
### 解决方案
1. **正确记录实际分配的大小**: - 在分配内存时,记录实际分配的大小,而不是使用固件请求的大小。 - 在释放内存时,使用实际分配的大小。
2. **改进清理逻辑**: - 在分配失败时,确保所有已分配的段都被正确释放,使用实际分配的大小。 - 避免跳过任何已分配的段,即使分配失败,也要确保清理逻辑正确。
3. **增加错误处理**: - 在分配失败时,增加更多的错误处理逻辑,确保不会留下未释放的内存。 - 在释放内存时,增加检查,确保释放的大小与实际分配的大小一致。
### 代码修改建议
```c // 在 ath12k_qmi_msg_mem_request_cb() 中 for (i = 0; i < ab->qmi.mem_seg_count; i++) {
struct ath12k_qmi_mem_seg *seg = &ab->qmi.target_mem[i];
seg->v.addr = dma_alloc_coherent(dev, seg->size, &seg->v.dma_addr, GFP_KERNEL); if (!seg->v.addr) {
// 分配失败,记录实际分配的大小为 0 seg->size = 0; break; } }
// 在 ath12k_qmi_free_target_mem_chunk() 中 for (i = 0; i < ab->qmi.mem_seg_count; i++) {
struct ath12k_qmi_mem_seg *seg = &ab->qmi.target_mem[i];
if (seg->v.addr && seg->size > 0) {
dma_free_coherent(dev, seg->size, seg->v.addr, seg->v.dma_addr); seg->v.addr = NULL; seg->size = 0; } } ```
通过上述修改,可以确保在分配失败时正确记录实际分配的大小,并在释放内存时使用正确的大小,从而避免内核崩溃。
If you want to get best quality of vulnerability data, you may have to visit VulDB.