@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,261 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_MemoryStability_002"
|
|
3
|
+
name: "指针解引用前判空"
|
|
4
|
+
category: "内存稳定性"
|
|
5
|
+
severity: "CRITICAL"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 指针解引用前判空
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
对指针进行解引用操作(*ptr、ptr->、ptr[index])前未进行空指针检查,可能导致空指针解引用崩溃(SIGSEGV)。这是OpenHarmony系统中最常见的稳定性问题之一,会导致服务崩溃、用户数据丢失。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:函数参数指针未判空直接解引用
|
|
22
|
+
void ProcessData(Data* data)
|
|
23
|
+
{
|
|
24
|
+
data->Process(); // 错误:未判空直接使用成员
|
|
25
|
+
data->value = 100;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 场景2:返回值指针未判空
|
|
29
|
+
void HandleResult()
|
|
30
|
+
{
|
|
31
|
+
Result* result = GetResult();
|
|
32
|
+
result->Print(); // 错误:GetResult可能返回nullptr
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 场景3:数组指针未判空访问元素
|
|
36
|
+
void ProcessArray(int* arr, int size)
|
|
37
|
+
{
|
|
38
|
+
for (int i = 0; i < size; i++) {
|
|
39
|
+
arr[i] = i * 2; // 错误:arr可能为nullptr
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 场景4:链式指针解引用
|
|
44
|
+
void ChainAccess(Node* node)
|
|
45
|
+
{
|
|
46
|
+
node->next->value = 100; // 错误:node或node->next可能为nullptr
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 场景5:智能指针get()后未判空
|
|
50
|
+
void SmartPtrGet()
|
|
51
|
+
{
|
|
52
|
+
std::unique_ptr<Data> ptr = GetData();
|
|
53
|
+
Data* raw = ptr.get();
|
|
54
|
+
raw->Process(); // 错误:ptr可能为空,raw为nullptr
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 场景6:条件分支遗漏判空
|
|
58
|
+
void ConditionalAccess(Data* data, bool flag)
|
|
59
|
+
{
|
|
60
|
+
if (flag) {
|
|
61
|
+
data->Process(); // 错误:即使flag=true,data可能仍为nullptr
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### ✅ 修复方案
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
// 修复场景1:函数参数判空
|
|
70
|
+
void ProcessData(Data* data)
|
|
71
|
+
{
|
|
72
|
+
if (data == nullptr) { // 正确:入口判空
|
|
73
|
+
LOGE("data is nullptr");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
data->Process();
|
|
77
|
+
data->value = 100;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 修复场景2:返回值判空
|
|
81
|
+
void HandleResult()
|
|
82
|
+
{
|
|
83
|
+
Result* result = GetResult();
|
|
84
|
+
if (result == nullptr) { // 正确:判空后使用
|
|
85
|
+
LOGE("GetResult failed");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
result->Print();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 修复场景3:数组指针判空
|
|
92
|
+
void ProcessArray(int* arr, int size)
|
|
93
|
+
{
|
|
94
|
+
if (arr == nullptr || size <= 0) { // 正确:判空+边界检查
|
|
95
|
+
LOGE("Invalid array parameters");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (int i = 0; i < size; i++) {
|
|
99
|
+
arr[i] = i * 2;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 修复场景4:链式指针逐级判空
|
|
104
|
+
void ChainAccess(Node* node)
|
|
105
|
+
{
|
|
106
|
+
if (node == nullptr) { // 正确:第一级判空
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (node->next == nullptr) { // 正确:第二级判空
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
node->next->value = 100;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 修复场景5:智能指针直接使用
|
|
116
|
+
void SmartPtrGet()
|
|
117
|
+
{
|
|
118
|
+
std::unique_ptr<Data> ptr = GetData();
|
|
119
|
+
if (!ptr) { // 正确:智能指针判空
|
|
120
|
+
LOGE("GetData returned empty");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
ptr->Process(); // 直接使用智能指针,无需get()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 修复场景6:条件分支中判空
|
|
127
|
+
void ConditionalAccess(Data* data, bool flag)
|
|
128
|
+
{
|
|
129
|
+
if (data == nullptr) { // 正确:先判空
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (flag) {
|
|
133
|
+
data->Process(); // 安全:已判空
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 修复场景7:使用assert断言(调试阶段)
|
|
138
|
+
void DebugAssert(Data* data)
|
|
139
|
+
{
|
|
140
|
+
assert(data != nullptr && "data must not be nullptr");
|
|
141
|
+
data->Process(); // 注意:assert在生产环境可能被禁用
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 修复场景8:使用引用代替指针
|
|
145
|
+
void UseReference(Data& data)
|
|
146
|
+
{
|
|
147
|
+
data.Process(); // 安全:引用不能为空
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 检测范围
|
|
152
|
+
|
|
153
|
+
检查以下模式:
|
|
154
|
+
|
|
155
|
+
- 指针参数在函数入口未判空直接解引用
|
|
156
|
+
- 返回值指针未判空直接解引用
|
|
157
|
+
- 数组指针未判空直接访问元素
|
|
158
|
+
- 链式指针解引用(ptr->member->subMember)
|
|
159
|
+
- 智能指针get()返回的裸指针未判空
|
|
160
|
+
- 条件分支中遗漏的判空
|
|
161
|
+
|
|
162
|
+
## 检测要点
|
|
163
|
+
|
|
164
|
+
1. 识别指针变量(通过类型声明或推断)
|
|
165
|
+
2. 检测解引用操作(->、*、[index])
|
|
166
|
+
3. 向上搜索是否有判空语句(if ptr == nullptr 等)
|
|
167
|
+
4. 检查是否在判空保护范围内
|
|
168
|
+
5. 排除NOPROTECT标记和引用类型
|
|
169
|
+
|
|
170
|
+
## 风险流分析(RiskFlow)
|
|
171
|
+
|
|
172
|
+
- **RISK_SOURCE**:可能为nullptr的指针变量
|
|
173
|
+
- **RISK_TYPE**:空指针解引用(SIGSEGV)
|
|
174
|
+
- **RISK_PATH**:指针为nullptr -> 未判空解引用 -> 内存访问违规 -> 程序崩溃
|
|
175
|
+
- **IMPACT_POINT**:进程崩溃、服务中断、系统不稳定
|
|
176
|
+
|
|
177
|
+
## 影响分析(ImpactAnalysis)
|
|
178
|
+
|
|
179
|
+
- **Trigger**:指针值为nullptr时执行解引用操作
|
|
180
|
+
- **Propagation**:访问非法内存地址(0x0或附近)
|
|
181
|
+
- **Consequence**:SIGSEGV信号导致进程终止,服务崩溃
|
|
182
|
+
- **Mitigation**:解引用前强制判空,或使用引用/智能指针
|
|
183
|
+
|
|
184
|
+
## 误报排除
|
|
185
|
+
|
|
186
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
187
|
+
|------|----------|----------|
|
|
188
|
+
| 已判空 | 之前有 if (ptr == nullptr) return | 不报 |
|
|
189
|
+
| 引用类型 | 参数类型为 Type& 或 Type& | 不报 |
|
|
190
|
+
| 智能指针直接用 | unique_ptr/shared_ptr->method() | 不报 |
|
|
191
|
+
| 确信非空指针 | assert(ptr) 或 constexpr | 上下文分析 |
|
|
192
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
193
|
+
| this指针 | this->member | 不报(this不能为空) |
|
|
194
|
+
| 成员函数内 | 在类成员函数内访问成员 | 上下文分析 |
|
|
195
|
+
## 测试用例
|
|
196
|
+
|
|
197
|
+
### 触发用例(应该报)
|
|
198
|
+
|
|
199
|
+
```cpp
|
|
200
|
+
// test_MemoryStability_002_trigger.cpp
|
|
201
|
+
void trigger_bad_1(Data* data)
|
|
202
|
+
{
|
|
203
|
+
data->Process(); // 应该报:参数指针未判空
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
void trigger_bad_2()
|
|
207
|
+
{
|
|
208
|
+
Node* node = GetNode();
|
|
209
|
+
node->value = 100; // 应该报:返回值未判空
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
void trigger_bad_3(int* arr)
|
|
213
|
+
{
|
|
214
|
+
arr[0] = 1; // 应该报:数组指针未判空
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
void trigger_bad_4(Node* node)
|
|
218
|
+
{
|
|
219
|
+
node->next->value = 100; // 应该报:链式解引用未逐级判空
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 安全用例(不应该报)
|
|
224
|
+
|
|
225
|
+
```cpp
|
|
226
|
+
// test_MemoryStability_002_safe.cpp
|
|
227
|
+
void safe_good_1(Data* data)
|
|
228
|
+
{
|
|
229
|
+
if (data == nullptr) { // 安全:入口判空
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
data->Process();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
void safe_good_2()
|
|
236
|
+
{
|
|
237
|
+
Node* node = GetNode();
|
|
238
|
+
if (node != nullptr) { // 安全:判空后使用
|
|
239
|
+
node->value = 100;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
void safe_good_3(Data& data)
|
|
244
|
+
{
|
|
245
|
+
data.Process(); // 安全:使用引用,不能为空
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void safe_good_4()
|
|
249
|
+
{
|
|
250
|
+
auto ptr = std::make_unique<Data>();
|
|
251
|
+
if (ptr) { // 安全:智能指针判空
|
|
252
|
+
ptr->Process();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// NOPROTECT: 特殊场景
|
|
257
|
+
void noprotect_case(Data* data)
|
|
258
|
+
{
|
|
259
|
+
data->Process(); // NOPROTECT标记,不报
|
|
260
|
+
}
|
|
261
|
+
```
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_MemoryStability_003"
|
|
3
|
+
name: "异常分支内存未及时释放"
|
|
4
|
+
category: "内存稳定性"
|
|
5
|
+
severity: "HIGH"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 异常分支内存未及时释放
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
在代码的异常分支(如提前return、break、continue等)退出前,已分配的内存(malloc/new)未及时释放,导致内存泄漏。这类问题在复杂的控制流中容易被忽略,是内存泄漏的常见原因之一。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:条件分支提前return未释放
|
|
22
|
+
void ProcessData(int size)
|
|
23
|
+
{
|
|
24
|
+
char* buffer = (char*)malloc(size);
|
|
25
|
+
if (buffer == nullptr) {
|
|
26
|
+
return; // 错误:分配失败,无内存可释放,此处正确
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (size > MAX_SIZE) {
|
|
30
|
+
return; // 错误:提前return未释放buffer
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 处理数据
|
|
34
|
+
free(buffer);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 场景2:错误处理分支未释放
|
|
38
|
+
int LoadConfig(const char* path)
|
|
39
|
+
{
|
|
40
|
+
Config* config = new Config();
|
|
41
|
+
|
|
42
|
+
if (!FileExists(path)) {
|
|
43
|
+
return -1; // 错误:config未释放
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!ParseConfig(config, path)) {
|
|
47
|
+
return -2; // 错误:config未释放
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
delete config;
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 场景3:循环中break未释放
|
|
55
|
+
void ProcessItems(int count)
|
|
56
|
+
{
|
|
57
|
+
int* items = (int*)malloc(count * sizeof(int));
|
|
58
|
+
|
|
59
|
+
for (int i = 0; i < count; i++) {
|
|
60
|
+
if (items[i] < 0) {
|
|
61
|
+
break; // 错误:break前未释放items
|
|
62
|
+
}
|
|
63
|
+
ProcessItem(items[i]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
free(items);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 场景4:嵌套条件多个退出点
|
|
70
|
+
int HandleRequest(Request* req)
|
|
71
|
+
{
|
|
72
|
+
char* temp = (char*)malloc(1024);
|
|
73
|
+
|
|
74
|
+
if (req == nullptr) {
|
|
75
|
+
return -1; // 错误:temp未释放
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (req->type == TYPE_INVALID) {
|
|
79
|
+
return -2; // 错误:temp未释放
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (req->size > MAX_SIZE) {
|
|
83
|
+
return -3; // 错误:temp未释放
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 处理请求
|
|
87
|
+
free(temp);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 场景5:switch case中提前return
|
|
92
|
+
int ProcessCommand(int cmd)
|
|
93
|
+
{
|
|
94
|
+
char* buffer = (char*)malloc(256);
|
|
95
|
+
|
|
96
|
+
switch (cmd) {
|
|
97
|
+
case CMD_EXIT:
|
|
98
|
+
return -1; // 错误:buffer未释放
|
|
99
|
+
case CMD_RESET:
|
|
100
|
+
return -2; // 错误:buffer未释放
|
|
101
|
+
case CMD_PROCESS:
|
|
102
|
+
// 处理
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
free(buffer);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 场景6:异常检查后多处返回
|
|
111
|
+
bool ValidateData(Data* data)
|
|
112
|
+
{
|
|
113
|
+
char* temp = new char[512];
|
|
114
|
+
|
|
115
|
+
if (data == nullptr) {
|
|
116
|
+
return false; // 错误:temp未释放
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (data->size <= 0) {
|
|
120
|
+
return false; // 错误:temp未释放
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (data->type == TYPE_UNKNOWN) {
|
|
124
|
+
return false; // 错误:temp未释放
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
delete[] temp;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### ✅ 修复方案
|
|
133
|
+
|
|
134
|
+
```cpp
|
|
135
|
+
// 修复场景1:提前return前释放内存
|
|
136
|
+
void ProcessData(int size)
|
|
137
|
+
{
|
|
138
|
+
char* buffer = (char*)malloc(size);
|
|
139
|
+
if (buffer == nullptr) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (size > MAX_SIZE) {
|
|
144
|
+
free(buffer); // 正确:return前释放
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 处理数据
|
|
149
|
+
free(buffer);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 修复场景2:使用智能指针(RAII)
|
|
153
|
+
int LoadConfig(const char* path)
|
|
154
|
+
{
|
|
155
|
+
std::unique_ptr<Config> config = std::make_unique<Config>();
|
|
156
|
+
|
|
157
|
+
if (!FileExists(path)) {
|
|
158
|
+
return -1; // 安全:智能指针自动释放
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!ParseConfig(config.get(), path)) {
|
|
162
|
+
return -2; // 安全:智能指针自动释放
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return 0; // 安全:智能指针自动释放
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 修复场景3:使用goto集中释放(C风格)
|
|
169
|
+
void ProcessItems(int count)
|
|
170
|
+
{
|
|
171
|
+
int* items = (int*)malloc(count * sizeof(int));
|
|
172
|
+
if (items == nullptr) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (int i = 0; i < count; i++) {
|
|
177
|
+
if (items[i] < 0) {
|
|
178
|
+
goto cleanup; // 跳转到释放点
|
|
179
|
+
}
|
|
180
|
+
ProcessItem(items[i]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
cleanup:
|
|
184
|
+
free(items);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 修复场景4:使用do-while(0)模式
|
|
188
|
+
int HandleRequest(Request* req)
|
|
189
|
+
{
|
|
190
|
+
char* temp = (char*)malloc(1024);
|
|
191
|
+
int result = 0;
|
|
192
|
+
|
|
193
|
+
do {
|
|
194
|
+
if (req == nullptr) {
|
|
195
|
+
result = -1;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (req->type == TYPE_INVALID) {
|
|
200
|
+
result = -2;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (req->size > MAX_SIZE) {
|
|
205
|
+
result = -3;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 处理请求
|
|
210
|
+
} while (0);
|
|
211
|
+
|
|
212
|
+
free(temp);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 修复场景5:switch中使用统一出口
|
|
217
|
+
int ProcessCommand(int cmd)
|
|
218
|
+
{
|
|
219
|
+
char* buffer = (char*)malloc(256);
|
|
220
|
+
int result = 0;
|
|
221
|
+
|
|
222
|
+
switch (cmd) {
|
|
223
|
+
case CMD_EXIT:
|
|
224
|
+
result = -1;
|
|
225
|
+
break; // 正确:跳转到统一释放点
|
|
226
|
+
case CMD_RESET:
|
|
227
|
+
result = -2;
|
|
228
|
+
break; // 正确:跳转到统一释放点
|
|
229
|
+
case CMD_PROCESS:
|
|
230
|
+
// 处理
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
free(buffer);
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 修复场景6:使用std::vector代替手动管理
|
|
239
|
+
bool ValidateData(Data* data)
|
|
240
|
+
{
|
|
241
|
+
std::vector<char> temp(512); // RAII自动管理
|
|
242
|
+
|
|
243
|
+
if (data == nullptr) {
|
|
244
|
+
return false; // 安全:vector自动释放
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (data->size <= 0) {
|
|
248
|
+
return false; // 安全:vector自动释放
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (data->type == TYPE_UNKNOWN) {
|
|
252
|
+
return false; // 安全:vector自动释放
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 修复场景7:使用lambda封装资源
|
|
259
|
+
void ProcessFile(const char* path)
|
|
260
|
+
{
|
|
261
|
+
auto cleanup = [](char* p) { if (p) free(p); };
|
|
262
|
+
char* buffer = (char*)malloc(1024);
|
|
263
|
+
std::unique_ptr<char, decltype(cleanup)> ptr(buffer, cleanup);
|
|
264
|
+
|
|
265
|
+
if (!FileExists(path)) {
|
|
266
|
+
return; // 安全:unique_ptr调用cleanup
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 处理文件
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## 检测范围
|
|
274
|
+
|
|
275
|
+
检查以下模式:
|
|
276
|
+
|
|
277
|
+
1. **malloc/calloc/realloc** 分配后在分支退出未释放
|
|
278
|
+
2. **new** 分配后在分支退出未释放(非智能指针)
|
|
279
|
+
3. **条件分支提前return** 前内存未释放
|
|
280
|
+
4. **循环中break/continue** 前内存未释放
|
|
281
|
+
5. **switch case中return** 前内存未释放
|
|
282
|
+
|
|
283
|
+
## 检测要点
|
|
284
|
+
|
|
285
|
+
1. 识别内存分配函数调用(malloc、calloc、realloc、new)
|
|
286
|
+
2. 跟踪分配变量名,检查是否存储到容器或成员变量
|
|
287
|
+
3. 检测分支退出语句(return、break、continue)
|
|
288
|
+
4. 在分配点和退出点之间检查是否存在释放操作
|
|
289
|
+
5. 排除智能指针(unique_ptr、shared_ptr)使用场景
|
|
290
|
+
6. 排除NOPROTECT标记场景
|
|
291
|
+
|
|
292
|
+
## 风险流分析(RiskFlow)
|
|
293
|
+
|
|
294
|
+
- **RISK_SOURCE**:malloc/new分配的内存块
|
|
295
|
+
- **RISK_TYPE**:内存泄漏
|
|
296
|
+
- **RISK_PATH**:内存分配 → 异常分支退出 → 跳过释放 → 孤儿内存块 → 内存泄漏累积
|
|
297
|
+
- **IMPACT_POINT**:系统内存资源耗尽,服务不可用,OOM崩溃
|
|
298
|
+
|
|
299
|
+
## 影响分析(ImpactAnalysis)
|
|
300
|
+
|
|
301
|
+
- **Trigger**:异常条件触发分支退出,跳过内存释放代码
|
|
302
|
+
- **Propagation**:分配的内存块成为孤儿内存,无法被任何指针引用
|
|
303
|
+
- **Consequence**:内存泄漏累积导致可用内存减少,最终OOM崩溃
|
|
304
|
+
- **Mitigation**:使用RAII智能指针,或确保每个退出点都有释放代码
|
|
305
|
+
|
|
306
|
+
## 误报排除
|
|
307
|
+
|
|
308
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
309
|
+
|------|----------|----------|
|
|
310
|
+
| 使用智能指针 | unique_ptr/shared_ptr/make_unique | 不报 |
|
|
311
|
+
| 存入容器 | vector/push_back/insert | 不报 |
|
|
312
|
+
| 赋值给成员变量 | this->/m_xxx | 不报 |
|
|
313
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
314
|
+
| 全局/静态变量 | static/global | 不报 |
|
|
315
|
+
| 测试代码 | 文件名包含 _test.cpp | 白名单排除 |
|
|
316
|
+
## 测试用例
|
|
317
|
+
|
|
318
|
+
### 触发用例(应该报)
|
|
319
|
+
|
|
320
|
+
```cpp
|
|
321
|
+
// test_MemoryStability_003_trigger.cpp
|
|
322
|
+
#include <cstdlib>
|
|
323
|
+
|
|
324
|
+
void trigger_bad_1(int size)
|
|
325
|
+
{
|
|
326
|
+
char* buffer = (char*)malloc(1024);
|
|
327
|
+
if (size > 100) {
|
|
328
|
+
return; // 应该报:malloc后提前return未释放
|
|
329
|
+
}
|
|
330
|
+
free(buffer);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
void trigger_bad_2()
|
|
334
|
+
{
|
|
335
|
+
int* data = new int[100];
|
|
336
|
+
if (data == nullptr) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (condition) {
|
|
340
|
+
return; // 应该报:new后提前return未释放
|
|
341
|
+
}
|
|
342
|
+
delete[] data;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
void trigger_bad_3(int count)
|
|
346
|
+
{
|
|
347
|
+
int* items = (int*)malloc(count * sizeof(int));
|
|
348
|
+
for (int i = 0; i < count; i++) {
|
|
349
|
+
if (items[i] < 0) {
|
|
350
|
+
break; // 应该报:break前未释放
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
free(items);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
int trigger_bad_4(int cmd)
|
|
357
|
+
{
|
|
358
|
+
char* buffer = (char*)malloc(256);
|
|
359
|
+
switch (cmd) {
|
|
360
|
+
case 1:
|
|
361
|
+
return -1; // 应该报:switch case return未释放
|
|
362
|
+
case 2:
|
|
363
|
+
return -2; // 应该报:switch case return未释放
|
|
364
|
+
}
|
|
365
|
+
free(buffer);
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 安全用例(不应该报)
|
|
371
|
+
|
|
372
|
+
```cpp
|
|
373
|
+
// test_MemoryStability_003_safe.cpp
|
|
374
|
+
#include <cstdlib>
|
|
375
|
+
#include <memory>
|
|
376
|
+
#include <vector>
|
|
377
|
+
|
|
378
|
+
void safe_good_1(int size)
|
|
379
|
+
{
|
|
380
|
+
char* buffer = (char*)malloc(1024);
|
|
381
|
+
if (size > 100) {
|
|
382
|
+
free(buffer); // 安全:return前释放
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
free(buffer);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
void safe_good_2()
|
|
389
|
+
{
|
|
390
|
+
std::unique_ptr<int[]> data = std::make_unique<int[]>(100);
|
|
391
|
+
if (condition) {
|
|
392
|
+
return; // 安全:智能指针自动释放
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
void safe_good_3()
|
|
397
|
+
{
|
|
398
|
+
std::vector<int> items(100);
|
|
399
|
+
if (condition) {
|
|
400
|
+
return; // 安全:vector自动释放
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void safe_good_4(int count)
|
|
405
|
+
{
|
|
406
|
+
int* items = (int*)malloc(count * sizeof(int));
|
|
407
|
+
int result = 0;
|
|
408
|
+
|
|
409
|
+
do {
|
|
410
|
+
if (count <= 0) {
|
|
411
|
+
result = -1;
|
|
412
|
+
break; // 安全:跳转到统一释放点
|
|
413
|
+
}
|
|
414
|
+
} while (0);
|
|
415
|
+
|
|
416
|
+
free(items);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// NOPROTECT: 特殊场景,内存由调用者管理
|
|
420
|
+
void noprotect_case()
|
|
421
|
+
{
|
|
422
|
+
char* buffer = (char*)malloc(256);
|
|
423
|
+
if (condition) {
|
|
424
|
+
return; // NOPROTECT: 不报
|
|
425
|
+
}
|
|
426
|
+
free(buffer);
|
|
427
|
+
}
|
|
428
|
+
```
|