@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.
Files changed (70) hide show
  1. package/README.md +169 -0
  2. package/SKILL.md +518 -0
  3. package/bin/install.js +165 -0
  4. package/config/rules.yaml +445 -0
  5. package/config/whitelist.yaml +52 -0
  6. package/package.json +40 -0
  7. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_001.md +275 -0
  8. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_002.md +273 -0
  9. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_003.md +305 -0
  10. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_004.md +350 -0
  11. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_005.md +301 -0
  12. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_006.md +320 -0
  13. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_007.md +432 -0
  14. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_008.md +394 -0
  15. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_009.md +425 -0
  16. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_010.md +472 -0
  17. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_011.md +204 -0
  18. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_012.md +210 -0
  19. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_013.md +226 -0
  20. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_014.md +222 -0
  21. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_015.md +256 -0
  22. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_016.md +269 -0
  23. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_017.md +222 -0
  24. package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_018.md +336 -0
  25. package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_001.md +414 -0
  26. package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_002.md +335 -0
  27. package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_003.md +284 -0
  28. package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_004.md +313 -0
  29. package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_005.md +364 -0
  30. package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_001.md +142 -0
  31. package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_002.md +222 -0
  32. package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_003.md +383 -0
  33. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_001.md +258 -0
  34. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_002.md +131 -0
  35. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_003.md +220 -0
  36. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_004.md +224 -0
  37. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_005.md +250 -0
  38. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_006.md +153 -0
  39. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_007.md +169 -0
  40. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_008.md +153 -0
  41. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_009.md +144 -0
  42. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_010.md +152 -0
  43. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_011.md +221 -0
  44. package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_012.md +318 -0
  45. package/references/InitializationOrder/StabilityCodeReview_InitializationOrder_001.md +411 -0
  46. package/references/Lifecycle/StabilityCodeReview_Lifecycle_001.md +255 -0
  47. package/references/Lifecycle/StabilityCodeReview_Lifecycle_002.md +177 -0
  48. package/references/MemoryStability/StabilityCodeReview_MemoryStability_001.md +332 -0
  49. package/references/MemoryStability/StabilityCodeReview_MemoryStability_002.md +261 -0
  50. package/references/MemoryStability/StabilityCodeReview_MemoryStability_003.md +428 -0
  51. package/references/MemoryStability/StabilityCodeReview_MemoryStability_004.md +400 -0
  52. package/references/MemoryStability/StabilityCodeReview_MemoryStability_005.md +364 -0
  53. package/references/MemoryStability/StabilityCodeReview_MemoryStability_006.md +359 -0
  54. package/references/MemoryStability/StabilityCodeReview_MemoryStability_007.md +279 -0
  55. package/references/PROBLEM_TEMPLATE.md +65 -0
  56. package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_001.md +380 -0
  57. package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_002.md +437 -0
  58. package/references/REPORT_TEMPLATE.csv +5 -0
  59. package/references/REPORT_TEMPLATE.md +132 -0
  60. package/references/RULE_DEVELOPMENT_GUIDE.md +711 -0
  61. package/references/RULE_INDEX.md +101 -0
  62. package/references/RULE_TEMPLATE.md +192 -0
  63. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_001.md +334 -0
  64. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_002.md +425 -0
  65. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_003.md +420 -0
  66. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_004.md +409 -0
  67. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_005.md +445 -0
  68. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_006.md +384 -0
  69. package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_007.md +395 -0
  70. package/scripts/add-rule.py +423 -0
@@ -0,0 +1,445 @@
1
+ ---
2
+ rule_id: "StabilityCodeReview_ResourceManagement_005"
3
+ name: "文件描述符泄漏"
4
+ category: "资源管理"
5
+ severity: "HIGH"
6
+ language: ["cpp", "c", "c++"]
7
+ author: "OH-Department7 Stability Team"
8
+ ---
9
+
10
+ # 文件描述符泄漏
11
+
12
+ ## 问题描述
13
+
14
+ 文件描述符fd资源需要确保申请和释放一一对应。遗漏释放会造成fd泄露,重复释放会造成double free。文件描述符是操作系统的重要资源,每个进程可打开的文件描述符数量有限,泄漏会导致进程无法打开新文件。
15
+
16
+ ## 检测示例
17
+
18
+ ### ❌ 问题代码
19
+
20
+ ```cpp
21
+ // 场景1:异常分支fd泄漏
22
+ int read_config(const char* path) {
23
+ int fd = open(path, O_RDONLY); // 打开文件描述符
24
+ if (fd < 0) {
25
+ return -1;
26
+ }
27
+
28
+ char buf[1024];
29
+ ssize_t n = read(fd, buf, sizeof(buf));
30
+ if (n < 0) {
31
+ return -1; // 错误:fd泄漏
32
+ }
33
+
34
+ // 处理数据...
35
+ close(fd);
36
+ return 0;
37
+ }
38
+
39
+ // 场景2:多分支fd泄漏
40
+ int process_file(const char* path, int mode) {
41
+ int fd = open(path, mode);
42
+ if (fd < 0) {
43
+ return -1;
44
+ }
45
+
46
+ if (mode == O_RDONLY) {
47
+ char buf[100];
48
+ if (read(fd, buf, sizeof(buf)) < 0) {
49
+ return -1; // 错误:fd泄漏
50
+ }
51
+ } else if (mode == O_WRONLY) {
52
+ char data[] = "hello";
53
+ if (write(fd, data, sizeof(data)) < 0) {
54
+ return -1; // 错误:fd泄漏
55
+ }
56
+ } else {
57
+ return -1; // 错误:fd泄漏
58
+ }
59
+
60
+ close(fd);
61
+ return 0;
62
+ }
63
+
64
+ // 场景3:socket fd泄漏
65
+ int connect_server(const char* host, int port) {
66
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
67
+ if (sock < 0) {
68
+ return -1;
69
+ }
70
+
71
+ struct sockaddr_in addr;
72
+ addr.sin_family = AF_INET;
73
+ addr.sin_port = htons(port);
74
+ inet_pton(AF_INET, host, &addr.sin_addr);
75
+
76
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
77
+ return -1; // 错误:sock泄漏
78
+ }
79
+
80
+ return sock; // 调用者需要关闭
81
+ }
82
+
83
+ // 场景4:double close
84
+ void process_data(int fd) {
85
+ char buf[100];
86
+ read(fd, buf, sizeof(buf));
87
+ close(fd);
88
+ // ... 一些代码
89
+ close(fd); // 错误:double close!
90
+ }
91
+
92
+ // 场景5:pipe fd泄漏
93
+ int create_pipe(int* read_fd, int* write_fd) {
94
+ int fds[2];
95
+ if (pipe(fds) < 0) {
96
+ return -1;
97
+ }
98
+
99
+ *read_fd = fds[0];
100
+ *write_fd = fds[1];
101
+
102
+ if (*read_fd < 0) {
103
+ return -1; // 错误:fds[0]和fds[1]都泄漏
104
+ }
105
+
106
+ return 0;
107
+ }
108
+ ```
109
+
110
+ ### ✅ 修复方案
111
+
112
+ ```cpp
113
+ // 修复场景1:使用RAII封装
114
+ class FdGuard {
115
+ public:
116
+ explicit FdGuard(int fd) : fd_(fd) {}
117
+ ~FdGuard() { if (fd_ >= 0) close(fd_); }
118
+ int get() const { return fd_; }
119
+ int release() { int tmp = fd_; fd_ = -1; return tmp; }
120
+ private:
121
+ int fd_;
122
+ };
123
+
124
+ int read_config(const char* path) {
125
+ int fd = open(path, O_RDONLY);
126
+ if (fd < 0) {
127
+ return -1;
128
+ }
129
+ FdGuard guard(fd); // RAII管理
130
+
131
+ char buf[1024];
132
+ ssize_t n = read(guard.get(), buf, sizeof(buf));
133
+ if (n < 0) {
134
+ return -1; // FdGuard自动关闭
135
+ }
136
+
137
+ return 0;
138
+ }
139
+
140
+ // 修复场景2:统一清理
141
+ int process_file(const char* path, int mode) {
142
+ int fd = open(path, mode);
143
+ if (fd < 0) {
144
+ return -1;
145
+ }
146
+
147
+ int ret = -1;
148
+
149
+ if (mode == O_RDONLY) {
150
+ char buf[100];
151
+ if (read(fd, buf, sizeof(buf)) >= 0) {
152
+ ret = 0;
153
+ }
154
+ } else if (mode == O_WRONLY) {
155
+ char data[] = "hello";
156
+ if (write(fd, data, sizeof(data)) >= 0) {
157
+ ret = 0;
158
+ }
159
+ }
160
+
161
+ close(fd); // 统一清理
162
+ return ret;
163
+ }
164
+
165
+ // 修复场景3:使用unique_fd
166
+ using unique_fd = std::unique_ptr<int, decltype(&close_fd)>;
167
+
168
+ int safe_connect(const char* host, int port) {
169
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
170
+ if (sock < 0) {
171
+ return -1;
172
+ }
173
+
174
+ unique_fd fd_ptr(sock, close_fd);
175
+
176
+ struct sockaddr_in addr;
177
+ addr.sin_family = AF_INET;
178
+ addr.sin_port = htons(port);
179
+ inet_pton(AF_INET, host, &addr.sin_addr);
180
+
181
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
182
+ return -1; // unique_fd自动关闭
183
+ }
184
+
185
+ return fd_ptr.release(); // 转移所有权
186
+ }
187
+
188
+ // 修复场景4:避免double close
189
+ void process_data(int fd) {
190
+ char buf[100];
191
+ read(fd, buf, sizeof(buf));
192
+ close(fd);
193
+ fd = -1; // 设为无效值,防止误用
194
+ // ... 一些代码
195
+ // 不再调用close(fd)
196
+ }
197
+
198
+ // 修复场景5:pipe正确处理
199
+ int create_pipe(int* read_fd, int* write_fd) {
200
+ int fds[2];
201
+ if (pipe(fds) < 0) {
202
+ return -1;
203
+ }
204
+
205
+ *read_fd = fds[0];
206
+ *write_fd = fds[1];
207
+
208
+ // 检查有效性
209
+ if (*read_fd < 0 || *write_fd < 0) {
210
+ close(fds[0]);
211
+ close(fds[1]);
212
+ return -1;
213
+ }
214
+
215
+ return 0;
216
+ }
217
+
218
+ // 修复场景6:goto统一清理(C风格)
219
+ int read_file_goto(const char* path, char* buf, size_t size) {
220
+ int fd = -1;
221
+ ssize_t n = -1;
222
+
223
+ fd = open(path, O_RDONLY);
224
+ if (fd < 0) {
225
+ goto error;
226
+ }
227
+
228
+ n = read(fd, buf, size);
229
+ if (n < 0) {
230
+ goto error;
231
+ }
232
+
233
+ close(fd);
234
+ return 0;
235
+
236
+ error:
237
+ if (fd >= 0) {
238
+ close(fd);
239
+ }
240
+ return -1;
241
+ }
242
+ ```
243
+
244
+ ## 检测范围
245
+
246
+ 检查以下模式:
247
+
248
+ - `open/openat/socket/accept/creat` 打开的fd
249
+ - `pipe/pipe2` 创建的管道fd
250
+ - `epoll_create/epoll_create1` 创建的epoll fd
251
+ - `inotify_init/signalfd/timerfd_create/eventfd` 等特殊fd
252
+ - 异常分支未关闭fd
253
+ - 重复关闭同一个fd
254
+
255
+ ### OpenHarmony特有fd类型
256
+
257
+ OpenHarmony系统中有以下特殊的文件描述符类型:
258
+
259
+ - **Ashmem fd**:匿名共享内存文件描述符
260
+ - 创建:`AshmemCreate()` 或通过Parcel传递
261
+ - 释放:需要调用专门的释放函数,不是简单的close
262
+ - 示例:`int ashmemFd = AshmemCreate("name", size);`
263
+
264
+ - **Parcel fd**:IPC数据传输中的文件描述符
265
+ - 通过Parcel传递的fd,所有权可能转移
266
+ - 接收方需要明确是否负责关闭
267
+ - 示例:`int fd = parcel.ReadFileDescriptor();`
268
+
269
+ - **socketpair fd**:用于IPC通信的socket对
270
+ - 创建:`socketpair()`,返回两个fd
271
+ - 注意两个fd都需要正确管理
272
+ - 示例:`int fds[2]; socketpair(AF_UNIX, SOCK_STREAM, 0, fds);`
273
+
274
+ - **SyncFence fd**:图形系统同步栅栏
275
+ - 通常通过sptr<SyncFence>管理,fd封装在对象内
276
+ - 不应直接操作内部的fd
277
+ - 示例:`sptr<SyncFence> fence = new SyncFence(fd);`
278
+
279
+ ### 实际代码示例(从graphic_graphic_2d)
280
+
281
+ ```cpp
282
+ // 示例1:使用RAII管理dlopen handle
283
+ void LoadAndUseLibrary() {
284
+ void* handle = dlopen("libname.so", RTLD_NOW);
285
+ if (!handle) {
286
+ ROSEN_LOGE("dlopen failed");
287
+ return;
288
+ }
289
+
290
+ // 使用库函数
291
+ auto func = dlsym(handle, "function_name");
292
+ if (!func) {
293
+ dlclose(handle); // 异常分支正确关闭
294
+ ROSEN_LOGE("dlsym failed");
295
+ return;
296
+ }
297
+
298
+ // 正常使用
299
+ dlclose(handle); // 正常关闭
300
+ }
301
+
302
+ // 示例2:socketpair的正确管理
303
+ bool CreateCommunicationChannel(int& readFd, int& writeFd) {
304
+ int fds[2];
305
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
306
+ ROSEN_LOGE("socketpair failed");
307
+ return false;
308
+ }
309
+
310
+ readFd = fds[0];
311
+ writeFd = fds[1];
312
+
313
+ // 如果后续操作失败,需要关闭两个fd
314
+ if (!SetupChannel(fds[0], fds[1])) {
315
+ close(fds[0]);
316
+ close(fds[1]);
317
+ return false;
318
+ }
319
+
320
+ return true; // 调用者负责关闭readFd和writeFd
321
+ }
322
+
323
+ // 示例3:Ashmem fd管理
324
+ void UseAshmem() {
325
+ int ashmemFd = AshmemCreate("shared_memory", 4096);
326
+ if (ashmemFd < 0) {
327
+ ROSEN_LOGE("AshmemCreate failed");
328
+ return;
329
+ }
330
+
331
+ void* addr = mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
332
+ if (addr == MAP_FAILED) {
333
+ close(ashmemFd); // mmap失败,关闭fd
334
+ ROSEN_LOGE("mmap failed");
335
+ return;
336
+ }
337
+
338
+ // 使用共享内存
339
+ munmap(addr, 4096);
340
+ close(ashmemFd); // 使用完成,关闭fd
341
+ }
342
+ ```
343
+
344
+ ## 检测要点
345
+
346
+ 1. 识别fd申请操作(open, socket, pipe等)
347
+ 2. 追踪fd变量到函数结束
348
+ 3. 检查异常返回分支是否关闭fd
349
+ 4. 检查是否存在double close
350
+ 5. 排除使用RAII封装的情况
351
+
352
+ ## 风险流分析(RiskFlow)
353
+
354
+ - **RISK_SOURCE**:打开的文件描述符fd
355
+ - **RISK_TYPE**:资源泄漏/双重释放
356
+ - **RISK_PATH**:open -> 异常返回 -> 未close -> fd泄漏 或 close -> close -> double free
357
+ - **IMPACT_POINT**:系统资源耗尽、进程崩溃
358
+
359
+ ## 影响分析(ImpactAnalysis)
360
+
361
+ - **Trigger**:异常分支返回时未关闭fd,或同一fd被关闭两次
362
+ - **Propagation**:文件描述符泄漏累积,或无效内存访问
363
+ - **Consequence**:进程无法打开新文件、系统资源耗尽、程序崩溃
364
+ - **Mitigation**:使用RAII封装(FdGuard/unique_fd)或确保所有分支都调用close
365
+
366
+ ## 误报排除
367
+
368
+ | 场景 | 识别特征 | 处理方式 |
369
+ |------|----------|----------|
370
+ | 使用RAII封装 | FdGuard/unique_fd/AutoCloseFd | 不报 |
371
+ | 有close释放 | 异常分支前有close调用 | 不报 |
372
+ | fd作为返回值 | return fd变量 | 不报 |
373
+ | NOPROTECT标记 | 有 // NOPROTECT 注释 | 不报 |
374
+ | 第三方库 | 位于 third_party 目录 | 白名单排除 |
375
+ ## 测试用例
376
+
377
+ ### 触发用例(应该报)
378
+
379
+ ```cpp
380
+ // test_ResourceManagement_005_trigger.cpp
381
+ int trigger_bad_1(const char* path) {
382
+ int fd = open(path, O_RDONLY); // 应该报:fd泄漏
383
+ if (fd < 0) return -1;
384
+ char buf[100];
385
+ if (read(fd, buf, sizeof(buf)) < 0) {
386
+ return -1; // fd泄漏
387
+ }
388
+ close(fd);
389
+ return 0;
390
+ }
391
+
392
+ int trigger_bad_2(const char* host, int port) {
393
+ int sock = socket(AF_INET, SOCK_STREAM, 0); // 应该报:sock泄漏
394
+ if (sock < 0) return -1;
395
+ struct sockaddr_in addr;
396
+ // ...
397
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
398
+ return -1; // sock泄漏
399
+ }
400
+ return sock;
401
+ }
402
+
403
+ void trigger_bad_3(int fd) {
404
+ close(fd);
405
+ close(fd); // 应该报:double close
406
+ }
407
+ ```
408
+
409
+ ### 安全用例(不应该报)
410
+
411
+ ```cpp
412
+ // test_ResourceManagement_005_safe.cpp
413
+ int safe_good_1(const char* path) {
414
+ int fd = open(path, O_RDONLY);
415
+ if (fd < 0) return -1;
416
+ FdGuard guard(fd); // 安全:RAII管理
417
+ char buf[100];
418
+ if (read(guard.get(), buf, sizeof(buf)) < 0) {
419
+ return -1;
420
+ }
421
+ return 0;
422
+ }
423
+
424
+ int safe_good_2(const char* path) {
425
+ int fd = open(path, O_RDONLY);
426
+ if (fd < 0) return -1;
427
+ char buf[100];
428
+ if (read(fd, buf, sizeof(buf)) < 0) {
429
+ close(fd); // 安全:手动关闭
430
+ return -1;
431
+ }
432
+ close(fd);
433
+ return 0;
434
+ }
435
+
436
+ // NOPROTECT: 测试代码
437
+ int noprotect_case(const char* path) {
438
+ int fd = open(path, O_RDONLY);
439
+ if (read(fd, nullptr, 0) < 0) {
440
+ return -1;
441
+ }
442
+ close(fd);
443
+ return 0;
444
+ }
445
+ ```