@ohos-graphics/stability-code-review 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -0
- package/SKILL.md +518 -0
- package/bin/install.js +165 -0
- package/config/rules.yaml +445 -0
- package/config/whitelist.yaml +52 -0
- package/package.json +40 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_001.md +275 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_002.md +273 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_003.md +305 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_004.md +350 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_005.md +301 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_006.md +320 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_007.md +432 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_008.md +394 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_009.md +425 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_010.md +472 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_011.md +204 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_012.md +210 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_013.md +226 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_014.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_015.md +256 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_016.md +269 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_017.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_018.md +336 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_001.md +414 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_002.md +335 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_003.md +284 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_004.md +313 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_005.md +364 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_001.md +142 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_002.md +222 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_003.md +383 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_001.md +258 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_002.md +131 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_003.md +220 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_004.md +224 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_005.md +250 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_006.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_007.md +169 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_008.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_009.md +144 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_010.md +152 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_011.md +221 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_012.md +318 -0
- package/references/InitializationOrder/StabilityCodeReview_InitializationOrder_001.md +411 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_001.md +255 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_002.md +177 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_001.md +332 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_002.md +261 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_003.md +428 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_004.md +400 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_005.md +364 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_006.md +359 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_007.md +279 -0
- package/references/PROBLEM_TEMPLATE.md +65 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_001.md +380 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_002.md +437 -0
- package/references/REPORT_TEMPLATE.csv +5 -0
- package/references/REPORT_TEMPLATE.md +132 -0
- package/references/RULE_DEVELOPMENT_GUIDE.md +711 -0
- package/references/RULE_INDEX.md +101 -0
- package/references/RULE_TEMPLATE.md +192 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_001.md +334 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_002.md +425 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_003.md +420 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_004.md +409 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_005.md +445 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_006.md +384 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_007.md +395 -0
- package/scripts/add-rule.py +423 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_MemoryStability_005"
|
|
3
|
+
name: "double-free问题"
|
|
4
|
+
category: "内存稳定性"
|
|
5
|
+
severity: "CRITICAL"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# double-free问题
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
同一指针在多条代码路径被多次释放,或释放后未置空导致后续再次释放,引发内存管理混乱。double-free会导致堆内存结构损坏、程序崩溃、潜在的安全漏洞,甚至可能被攻击者利用执行任意代码。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:多条路径重复释放同一内存
|
|
22
|
+
void ProcessData(Data* data)
|
|
23
|
+
{
|
|
24
|
+
if (data == nullptr) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!ValidateData(data)) {
|
|
29
|
+
free(data); // 第一次释放
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!ProcessDataInternal(data)) {
|
|
34
|
+
free(data); // 第二次释放(如果ValidateData失败路径已释放)
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
free(data); // 第三次释放(可能重复)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 场景2:释放后未置空导致后续释放
|
|
42
|
+
void HandleBuffer()
|
|
43
|
+
{
|
|
44
|
+
char* buffer = (char*)malloc(1024);
|
|
45
|
+
if (buffer == nullptr) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ProcessBuffer(buffer);
|
|
50
|
+
free(buffer); // 释放
|
|
51
|
+
|
|
52
|
+
if (errorCondition) {
|
|
53
|
+
free(buffer); // 错误:buffer已释放,再次释放导致double-free
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 场景3:条件分支重复释放
|
|
58
|
+
int ManageResource()
|
|
59
|
+
{
|
|
60
|
+
Resource* res = new Resource();
|
|
61
|
+
|
|
62
|
+
if (condition1) {
|
|
63
|
+
delete res; // 第一条路径释放
|
|
64
|
+
return -1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (condition2) {
|
|
68
|
+
delete res; // 第二条路径释放(如果condition1为true,已释放)
|
|
69
|
+
return -2;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
delete res; // 正常路径释放
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 场景4:循环中释放后继续使用指针
|
|
77
|
+
void ProcessItems(Item* items, int count)
|
|
78
|
+
{
|
|
79
|
+
for (int i = 0; i < count; i++) {
|
|
80
|
+
free(items[i].data);
|
|
81
|
+
items[i].data = nullptr; // 正确:置空
|
|
82
|
+
|
|
83
|
+
if (i > 0 && items[i].data == items[i-1].data) {
|
|
84
|
+
free(items[i].data); // 错误:可能释放同一块内存两次
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 场景5:智能指针get()后手动删除
|
|
90
|
+
void SmartPtrMisuse()
|
|
91
|
+
{
|
|
92
|
+
std::unique_ptr<Data> ptr = std::make_unique<Data>();
|
|
93
|
+
Data* raw = ptr.get();
|
|
94
|
+
|
|
95
|
+
delete raw; // 错误:unique_ptr会在析构时再次释放
|
|
96
|
+
|
|
97
|
+
// ptr析构时再次delete raw,导致double-free
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 场景6:成员指针多处释放
|
|
101
|
+
class Container {
|
|
102
|
+
public:
|
|
103
|
+
~Container() {
|
|
104
|
+
if (data_) {
|
|
105
|
+
delete data_; // 析构时释放
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void Clear() {
|
|
110
|
+
if (data_) {
|
|
111
|
+
delete data_; // Clear时释放
|
|
112
|
+
data_ = nullptr; // 正确:置空避免析构时重复释放
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private:
|
|
117
|
+
Data* data_;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// 错误使用:先Clear()后析构会double-free(如果未置空)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### ✅ 修复方案
|
|
124
|
+
|
|
125
|
+
```cpp
|
|
126
|
+
// 修复场景1:统一释放点,避免多路径重复释放
|
|
127
|
+
void ProcessData(Data* data)
|
|
128
|
+
{
|
|
129
|
+
if (data == nullptr) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
bool success = false;
|
|
134
|
+
|
|
135
|
+
if (ValidateData(data)) {
|
|
136
|
+
success = ProcessDataInternal(data);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
free(data); // 正确:统一释放点,只释放一次
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 修复场景2:释放后立即置空
|
|
143
|
+
void HandleBuffer()
|
|
144
|
+
{
|
|
145
|
+
char* buffer = (char*)malloc(1024);
|
|
146
|
+
if (buffer == nullptr) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ProcessBuffer(buffer);
|
|
151
|
+
free(buffer);
|
|
152
|
+
buffer = nullptr; // 正确:置空,后续释放安全
|
|
153
|
+
|
|
154
|
+
if (errorCondition) {
|
|
155
|
+
free(buffer); // 安全:buffer为nullptr,free(nullptr)安全
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 修复场景3:使用智能指针避免手动管理
|
|
160
|
+
int ManageResource()
|
|
161
|
+
{
|
|
162
|
+
std::unique_ptr<Resource> res = std::make_unique<Resource>();
|
|
163
|
+
|
|
164
|
+
if (condition1) {
|
|
165
|
+
return -1; // 安全:智能指针自动释放一次
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (condition2) {
|
|
169
|
+
return -2; // 安全:智能指针自动释放一次
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return 0; // 安全:智能指针自动释放一次
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 修复场景4:正确管理循环中的释放
|
|
176
|
+
void ProcessItems(Item* items, int count)
|
|
177
|
+
{
|
|
178
|
+
for (int i = 0; i < count; i++) {
|
|
179
|
+
if (items[i].data != nullptr) {
|
|
180
|
+
free(items[i].data);
|
|
181
|
+
items[i].data = nullptr; // 正确:立即置空
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 避免重复释放相同指针
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 修复场景5:不要混用智能指针和手动delete
|
|
189
|
+
void SmartPtrCorrect()
|
|
190
|
+
{
|
|
191
|
+
std::unique_ptr<Data> ptr = std::make_unique<Data>();
|
|
192
|
+
Data* raw = ptr.get();
|
|
193
|
+
|
|
194
|
+
// 使用raw指针,但不手动delete
|
|
195
|
+
raw->Process(); // 正确:只使用,不手动释放
|
|
196
|
+
|
|
197
|
+
// unique_ptr析构时自动释放
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 修复场景6:成员指针统一管理,确保置空
|
|
201
|
+
class Container {
|
|
202
|
+
public:
|
|
203
|
+
~Container() {
|
|
204
|
+
Clear(); // 正确:调用Clear统一管理
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
void Clear() {
|
|
208
|
+
if (data_) {
|
|
209
|
+
delete data_;
|
|
210
|
+
data_ = nullptr; // 正确:置空,析构时不再释放
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private:
|
|
215
|
+
Data* data_ = nullptr; // 初始化为nullptr
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 检测范围
|
|
220
|
+
|
|
221
|
+
检查以下模式:
|
|
222
|
+
|
|
223
|
+
1. 同一指针变量在多处调用 `free()` / `delete` / `delete[]`
|
|
224
|
+
2. 释放后未置空(`nullptr`),后续可能再次释放
|
|
225
|
+
3. 智能指针 `get()` 后手动 `delete`
|
|
226
|
+
4. 多个条件分支中重复释放同一指针
|
|
227
|
+
5. 循环中重复释放同一指针
|
|
228
|
+
6. 类成员指针在多处释放且未置空
|
|
229
|
+
|
|
230
|
+
## 检测要点
|
|
231
|
+
|
|
232
|
+
1. 识别指针变量及其释放操作(free、delete、delete[])
|
|
233
|
+
2. 统计同一指针的释放操作次数
|
|
234
|
+
3. 检查释放后是否有置空操作(`= nullptr`)
|
|
235
|
+
4. 分析控制流,判断是否存在多条路径到达释放点
|
|
236
|
+
5. 排除智能指针、RAII类等自动管理场景
|
|
237
|
+
6. 排除NOPROTECT标记
|
|
238
|
+
|
|
239
|
+
## 风险流分析(RiskFlow)
|
|
240
|
+
|
|
241
|
+
- **RISK_SOURCE**:手动管理的指针变量
|
|
242
|
+
- **RISK_TYPE**:double-free(重复释放)
|
|
243
|
+
- **RISK_PATH**:同一指针多次释放 → 堆内存结构损坏 → 未定义行为 → 程序崩溃或安全漏洞
|
|
244
|
+
- **IMPACT_POINT**:堆管理器损坏,程序崩溃,潜在任意代码执行
|
|
245
|
+
|
|
246
|
+
## 影响分析(ImpactAnalysis)
|
|
247
|
+
|
|
248
|
+
- **Trigger**:多条代码路径都执行释放操作,或释放后未置空再次释放
|
|
249
|
+
- **Propagation**:堆内存结构损坏,后续内存操作异常
|
|
250
|
+
- **Consequence**:程序崩溃、数据损坏、潜在安全漏洞(攻击者可利用堆损坏执行代码)
|
|
251
|
+
- **Mitigation**:使用智能指针、释放后置空、统一释放点
|
|
252
|
+
|
|
253
|
+
## 误报排除
|
|
254
|
+
|
|
255
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
256
|
+
|------|----------|----------|
|
|
257
|
+
| 使用智能指针 | unique_ptr/shared_ptr | 不报 |
|
|
258
|
+
| 释放后置空 | ptr = nullptr / ptr = NULL | 不报 |
|
|
259
|
+
| 不同指针变量 | 不同变量名 | 不报 |
|
|
260
|
+
| 成员指针正确管理 | Clear()置空后析构检查nullptr | 不报 |
|
|
261
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
262
|
+
| 测试代码 | 文件名包含 _test.cpp | 白名单排除 |
|
|
263
|
+
## 测试用例
|
|
264
|
+
|
|
265
|
+
### 触发用例(应该报)
|
|
266
|
+
|
|
267
|
+
```cpp
|
|
268
|
+
// test_MemoryStability_005_trigger.cpp
|
|
269
|
+
#include <cstdlib>
|
|
270
|
+
|
|
271
|
+
void trigger_bad_1(Data* data)
|
|
272
|
+
{
|
|
273
|
+
if (!ValidateData(data)) {
|
|
274
|
+
free(data); // 应该报:第一条路径释放
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
free(data); // 应该报:第二条路径可能重复释放
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
void trigger_bad_2()
|
|
282
|
+
{
|
|
283
|
+
char* buffer = (char*)malloc(1024);
|
|
284
|
+
free(buffer);
|
|
285
|
+
free(buffer); // 应该报:double-free,未置空
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
void trigger_bad_3()
|
|
289
|
+
{
|
|
290
|
+
std::unique_ptr<Data> ptr = std::make_unique<Data>();
|
|
291
|
+
Data* raw = ptr.get();
|
|
292
|
+
delete raw; // 应该报:智能指针get()后手动delete
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
void trigger_bad_4()
|
|
296
|
+
{
|
|
297
|
+
int* arr = new int[100];
|
|
298
|
+
if (condition1) {
|
|
299
|
+
delete[] arr; // 应该报:第一条路径释放
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (condition2) {
|
|
304
|
+
delete[] arr; // 应该报:第二条路径释放
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
delete[] arr; // 应该报:第三条路径释放
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 安全用例(不应该报)
|
|
313
|
+
|
|
314
|
+
```cpp
|
|
315
|
+
// test_MemoryStability_005_safe.cpp
|
|
316
|
+
#include <cstdlib>
|
|
317
|
+
#include <memory>
|
|
318
|
+
|
|
319
|
+
void safe_good_1(Data* data)
|
|
320
|
+
{
|
|
321
|
+
free(data); // 只释放一次
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
void safe_good_2()
|
|
325
|
+
{
|
|
326
|
+
char* buffer = (char*)malloc(1024);
|
|
327
|
+
free(buffer);
|
|
328
|
+
buffer = nullptr; // 安全:置空后不再释放
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
void safe_good_3()
|
|
332
|
+
{
|
|
333
|
+
std::unique_ptr<Data> ptr = std::make_unique<Data>();
|
|
334
|
+
ptr->Process(); // 安全:使用智能指针,不手动delete
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
void safe_good_4()
|
|
338
|
+
{
|
|
339
|
+
int* arr = new int[100];
|
|
340
|
+
int result = 0;
|
|
341
|
+
|
|
342
|
+
do {
|
|
343
|
+
if (condition1) {
|
|
344
|
+
result = -1;
|
|
345
|
+
break; // 安全:跳转到统一释放点
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (condition2) {
|
|
349
|
+
result = -2;
|
|
350
|
+
break; // 安全:跳转到统一释放点
|
|
351
|
+
}
|
|
352
|
+
} while (0);
|
|
353
|
+
|
|
354
|
+
delete[] arr; // 只释放一次
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// NOPROTECT: 特殊场景
|
|
358
|
+
void noprotect_case()
|
|
359
|
+
{
|
|
360
|
+
int* data = new int[100];
|
|
361
|
+
delete[] data;
|
|
362
|
+
delete[] data; // NOPROTECT标记,不报
|
|
363
|
+
}
|
|
364
|
+
```
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_MemoryStability_006"
|
|
3
|
+
name: "use-after-free问题"
|
|
4
|
+
category: "内存稳定性"
|
|
5
|
+
severity: "CRITICAL"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# use-after-free问题
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
指针被释放后,后续代码仍对其进行解引用、成员访问或传递给其他函数,导致访问已释放的内存,可能引发崩溃。use-after-free是严重的内存安全问题,可能导致程序崩溃、数据损坏,甚至被攻击者利用执行任意代码。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:释放后继续使用指针
|
|
22
|
+
void ProcessData(Data* data)
|
|
23
|
+
{
|
|
24
|
+
if (data == nullptr) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
free(data);
|
|
29
|
+
data->value = 100; // 错误:释放后访问已释放的内存
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 场景2:释放后传递给其他函数
|
|
33
|
+
void HandleBuffer()
|
|
34
|
+
{
|
|
35
|
+
char* buffer = (char*)malloc(1024);
|
|
36
|
+
if (buffer == nullptr) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ProcessBuffer(buffer);
|
|
41
|
+
free(buffer);
|
|
42
|
+
|
|
43
|
+
ValidateBuffer(buffer); // 错误:传递已释放的指针给其他函数
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 场景3:释放后读取指针值
|
|
47
|
+
int GetDataValue()
|
|
48
|
+
{
|
|
49
|
+
int* value = new int(100);
|
|
50
|
+
int result = *value;
|
|
51
|
+
|
|
52
|
+
delete value;
|
|
53
|
+
return *value; // 错误:释放后再次读取
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 场景4:释放后访问成员
|
|
57
|
+
void UpdateNode(Node* node)
|
|
58
|
+
{
|
|
59
|
+
if (node == nullptr) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Node* next = node->next;
|
|
64
|
+
free(node);
|
|
65
|
+
|
|
66
|
+
node->value = 100; // 错误:释放后访问成员
|
|
67
|
+
node->next = nullptr; // 错误:释放后访问成员
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 场景5:释放后调用方法
|
|
71
|
+
void UseObject()
|
|
72
|
+
{
|
|
73
|
+
Object* obj = new Object();
|
|
74
|
+
obj->Initialize();
|
|
75
|
+
|
|
76
|
+
delete obj;
|
|
77
|
+
obj->Process(); // 错误:释放后调用方法
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 场景6:数组释放后访问元素
|
|
81
|
+
void ProcessArray()
|
|
82
|
+
{
|
|
83
|
+
int* arr = new int[100];
|
|
84
|
+
for (int i = 0; i < 100; i++) {
|
|
85
|
+
arr[i] = i;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
delete[] arr;
|
|
89
|
+
int sum = 0;
|
|
90
|
+
for (int i = 0; i < 100; i++) {
|
|
91
|
+
sum += arr[i]; // 错误:释放后访问数组元素
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 场景7:链式释放后使用
|
|
96
|
+
void ProcessList(Node* head)
|
|
97
|
+
{
|
|
98
|
+
Node* current = head;
|
|
99
|
+
while (current != nullptr) {
|
|
100
|
+
Node* next = current->next;
|
|
101
|
+
free(current);
|
|
102
|
+
current->value = 100; // 错误:释放后使用
|
|
103
|
+
current = next; // 正确:使用保存的next指针
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 场景8:局部变量释放后返回引用
|
|
108
|
+
int& GetLocalValue()
|
|
109
|
+
{
|
|
110
|
+
int* value = new int(100);
|
|
111
|
+
int result = *value;
|
|
112
|
+
delete value;
|
|
113
|
+
return *value; // 错误:返回已释放内存的引用
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### ✅ 修复方案
|
|
118
|
+
|
|
119
|
+
```cpp
|
|
120
|
+
// 修复场景1:释放前完成所有操作,或释放后置空
|
|
121
|
+
void ProcessData(Data* data)
|
|
122
|
+
{
|
|
123
|
+
if (data == nullptr) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
data->value = 100; // 正确:释放前完成操作
|
|
128
|
+
free(data);
|
|
129
|
+
data = nullptr; // 正确:置空避免后续误用
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 修复场景2:释放后置空,避免传递
|
|
133
|
+
void HandleBuffer()
|
|
134
|
+
{
|
|
135
|
+
char* buffer = (char*)malloc(1024);
|
|
136
|
+
if (buffer == nullptr) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ProcessBuffer(buffer);
|
|
141
|
+
ValidateBuffer(buffer); // 正确:释放前完成验证
|
|
142
|
+
free(buffer);
|
|
143
|
+
buffer = nullptr; // 正确:置空
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 修复场景3:保存值后释放,不要再次访问
|
|
147
|
+
int GetDataValue()
|
|
148
|
+
{
|
|
149
|
+
int* value = new int(100);
|
|
150
|
+
int result = *value; // 正确:先保存值
|
|
151
|
+
|
|
152
|
+
delete value;
|
|
153
|
+
return result; // 正确:返回保存的值,不访问已释放内存
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 修复场景4:释放前保存需要的值,或释放后置空
|
|
157
|
+
void UpdateNode(Node* node)
|
|
158
|
+
{
|
|
159
|
+
if (node == nullptr) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Node* next = node->next; // 正确:释放前保存next
|
|
164
|
+
int value = node->value; // 正确:释放前保存value
|
|
165
|
+
|
|
166
|
+
free(node);
|
|
167
|
+
node = nullptr; // 正确:置空
|
|
168
|
+
|
|
169
|
+
// 使用保存的值
|
|
170
|
+
if (next != nullptr) {
|
|
171
|
+
next->value = 100;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 修复场景5:使用智能指针自动管理
|
|
176
|
+
void UseObject()
|
|
177
|
+
{
|
|
178
|
+
std::unique_ptr<Object> obj = std::make_unique<Object>();
|
|
179
|
+
obj->Initialize();
|
|
180
|
+
obj->Process(); // 正确:智能指针自动管理生命周期
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 修复场景6:使用容器代替手动数组管理
|
|
184
|
+
void ProcessArray()
|
|
185
|
+
{
|
|
186
|
+
std::vector<int> arr(100);
|
|
187
|
+
for (int i = 0; i < 100; i++) {
|
|
188
|
+
arr[i] = i;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
int sum = 0;
|
|
192
|
+
for (int i = 0; i < 100; i++) {
|
|
193
|
+
sum += arr[i]; // 正确:vector自动管理内存
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 修复场景7:正确遍历链表释放
|
|
198
|
+
void ProcessList(Node* head)
|
|
199
|
+
{
|
|
200
|
+
Node* current = head;
|
|
201
|
+
while (current != nullptr) {
|
|
202
|
+
Node* next = current->next; // 正确:释放前保存next
|
|
203
|
+
ProcessNode(current); // 正确:释放前处理
|
|
204
|
+
|
|
205
|
+
free(current);
|
|
206
|
+
current = next; // 正确:使用保存的next指针
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 修复场景8:不要返回局部内存的引用
|
|
211
|
+
int GetLocalValue()
|
|
212
|
+
{
|
|
213
|
+
int value = 100; // 正确:使用栈变量
|
|
214
|
+
return value; // 正确:返回值,不是引用
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## 检测范围
|
|
219
|
+
|
|
220
|
+
检查以下模式:
|
|
221
|
+
|
|
222
|
+
1. `free()` / `delete` / `delete[]` 后,指针被解引用(`*ptr`、`ptr->`)
|
|
223
|
+
2. 释放后,指针被传递给其他函数
|
|
224
|
+
3. 释放后,指针被作为返回值或赋值给其他变量
|
|
225
|
+
4. 释放后,数组指针被访问元素(`ptr[index]`)
|
|
226
|
+
5. 释放后,对象指针调用成员方法
|
|
227
|
+
6. 链式释放后使用已释放的节点
|
|
228
|
+
7. 返回已释放内存的引用
|
|
229
|
+
|
|
230
|
+
## 检测要点
|
|
231
|
+
|
|
232
|
+
1. 识别释放操作(free、delete、delete[])
|
|
233
|
+
2. 跟踪指针变量,检测释放后的使用
|
|
234
|
+
3. 检查释放后是否存在解引用、成员访问、函数调用等操作
|
|
235
|
+
4. 检查释放后是否有置空操作(`= nullptr`)
|
|
236
|
+
5. 分析控制流,判断释放后是否还有使用路径
|
|
237
|
+
6. 排除智能指针、RAII类等自动管理场景
|
|
238
|
+
7. 排除NOPROTECT标记
|
|
239
|
+
|
|
240
|
+
## 风险流分析(RiskFlow)
|
|
241
|
+
|
|
242
|
+
- **RISK_SOURCE**:已释放的指针变量
|
|
243
|
+
- **RISK_TYPE**:use-after-free(访问已释放内存)
|
|
244
|
+
- **RISK_PATH**:指针释放 → 内存回收 → 继续访问 → 未定义行为 → 崩溃或安全漏洞
|
|
245
|
+
- **IMPACT_POINT**:访问非法内存,程序崩溃,潜在任意代码执行
|
|
246
|
+
|
|
247
|
+
## 影响分析(ImpactAnalysis)
|
|
248
|
+
|
|
249
|
+
- **Trigger**:释放后继续解引用或使用指针
|
|
250
|
+
- **Propagation**:访问已回收的堆内存,可能被其他分配覆盖
|
|
251
|
+
- **Consequence**:数据损坏、程序崩溃(SIGSEGV)、潜在安全漏洞(攻击者可利用UAF执行代码)
|
|
252
|
+
- **Mitigation**:释放后置空、使用智能指针、释放前完成所有操作
|
|
253
|
+
|
|
254
|
+
## 误报排除
|
|
255
|
+
|
|
256
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
257
|
+
|------|----------|----------|
|
|
258
|
+
| 释放后置空 | ptr = nullptr / ptr = NULL | 不报(置空后使用是空指针问题) |
|
|
259
|
+
| 使用智能指针 | unique_ptr/shared_ptr | 不报 |
|
|
260
|
+
| 释放前保存值 | 使用临时变量保存 | 不报(如果不再访问原指针) |
|
|
261
|
+
| 不同指针变量 | 不同变量名(如current和next) | 不报 |
|
|
262
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
263
|
+
| 测试代码 | 文件名包含 _test.cpp | 白名单排除 |
|
|
264
|
+
## 测试用例
|
|
265
|
+
|
|
266
|
+
### 触发用例(应该报)
|
|
267
|
+
|
|
268
|
+
```cpp
|
|
269
|
+
// test_MemoryStability_006_trigger.cpp
|
|
270
|
+
#include <cstdlib>
|
|
271
|
+
|
|
272
|
+
void trigger_bad_1(Data* data)
|
|
273
|
+
{
|
|
274
|
+
free(data);
|
|
275
|
+
data->value = 100; // 应该报:释放后访问成员
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
void trigger_bad_2()
|
|
279
|
+
{
|
|
280
|
+
char* buffer = (char*)malloc(1024);
|
|
281
|
+
free(buffer);
|
|
282
|
+
ValidateBuffer(buffer); // 应该报:释放后传递给函数
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
void trigger_bad_3()
|
|
286
|
+
{
|
|
287
|
+
int* value = new int(100);
|
|
288
|
+
delete value;
|
|
289
|
+
int result = *value; // 应该报:释放后解引用
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
void trigger_bad_4()
|
|
293
|
+
{
|
|
294
|
+
int* arr = new int[100];
|
|
295
|
+
delete[] arr;
|
|
296
|
+
arr[0] = 1; // 应该报:释放后访问数组元素
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
void trigger_bad_5()
|
|
300
|
+
{
|
|
301
|
+
Object* obj = new Object();
|
|
302
|
+
delete obj;
|
|
303
|
+
obj->Process(); // 应该报:释放后调用方法
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 安全用例(不应该报)
|
|
308
|
+
|
|
309
|
+
```cpp
|
|
310
|
+
// test_MemoryStability_006_safe.cpp
|
|
311
|
+
#include <cstdlib>
|
|
312
|
+
#include <memory>
|
|
313
|
+
#include <vector>
|
|
314
|
+
|
|
315
|
+
void safe_good_1(Data* data)
|
|
316
|
+
{
|
|
317
|
+
data->value = 100; // 先使用
|
|
318
|
+
free(data);
|
|
319
|
+
data = nullptr; // 置空
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
void safe_good_2()
|
|
323
|
+
{
|
|
324
|
+
char* buffer = (char*)malloc(1024);
|
|
325
|
+
ProcessBuffer(buffer); // 释放前使用
|
|
326
|
+
free(buffer);
|
|
327
|
+
buffer = nullptr; // 置空
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
void safe_good_3()
|
|
331
|
+
{
|
|
332
|
+
std::unique_ptr<Object> obj = std::make_unique<Object>();
|
|
333
|
+
obj->Process(); // 智能指针自动管理
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
void safe_good_4()
|
|
337
|
+
{
|
|
338
|
+
std::vector<int> arr(100);
|
|
339
|
+
arr[0] = 1; // vector自动管理内存
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
void safe_good_5(Node* head)
|
|
343
|
+
{
|
|
344
|
+
Node* current = head;
|
|
345
|
+
while (current != nullptr) {
|
|
346
|
+
Node* next = current->next; // 保存next
|
|
347
|
+
free(current);
|
|
348
|
+
current = next; // 使用保存的next,不报
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// NOPROTECT: 特殊场景
|
|
353
|
+
void noprotect_case()
|
|
354
|
+
{
|
|
355
|
+
int* value = new int(100);
|
|
356
|
+
delete value;
|
|
357
|
+
int result = *value; // NOPROTECT标记,不报
|
|
358
|
+
}
|
|
359
|
+
```
|