@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,301 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_BoundaryCondition_005"
|
|
3
|
+
name: "Parcel整数转枚举需校验有效性"
|
|
4
|
+
category: "边界条件"
|
|
5
|
+
severity: "MEDIUM"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Parcel整数转枚举需校验有效性
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
从Parcel中读取的整数不能直接转化为枚举类,需要校验值的有效性。未经验证的枚举值转换可能导致未定义行为、程序逻辑错误或安全漏洞。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 错误示例1:直接强制转换
|
|
22
|
+
enum class Color { RED = 0, GREEN = 1, BLUE = 2 };
|
|
23
|
+
|
|
24
|
+
Color ReadColor(Parcel& parcel)
|
|
25
|
+
{
|
|
26
|
+
int value = parcel.ReadInt32();
|
|
27
|
+
return static_cast<Color>(value); // 危险:value可能不在枚举范围内
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 错误示例2:隐式转换
|
|
31
|
+
enum Status { OK = 0, ERROR = 1, PENDING = 2 };
|
|
32
|
+
|
|
33
|
+
Status ReadStatus(Parcel& parcel)
|
|
34
|
+
{
|
|
35
|
+
int value = parcel.ReadInt32();
|
|
36
|
+
return static_cast<Status>(value); // 危险:未验证枚举范围
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 错误示例3:使用switch后仍未覆盖所有情况
|
|
40
|
+
enum class Type { TYPE_A = 0, TYPE_B = 1, TYPE_C = 2 };
|
|
41
|
+
|
|
42
|
+
Type ReadType(Parcel& parcel)
|
|
43
|
+
{
|
|
44
|
+
int value = parcel.ReadInt32();
|
|
45
|
+
Type type = static_cast<Type>(value); // 危险:未验证就转换
|
|
46
|
+
|
|
47
|
+
switch (type) {
|
|
48
|
+
case Type::TYPE_A:
|
|
49
|
+
HandleA();
|
|
50
|
+
break;
|
|
51
|
+
case Type::TYPE_B:
|
|
52
|
+
HandleB();
|
|
53
|
+
break;
|
|
54
|
+
default: // 可能落入无效值
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
return type;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 错误示例4:枚举值直接赋值
|
|
61
|
+
enum class Mode { MODE_READ = 0, MODE_WRITE = 1 };
|
|
62
|
+
|
|
63
|
+
void SetMode(Parcel& parcel)
|
|
64
|
+
{
|
|
65
|
+
Mode mode = static_cast<Mode>(parcel.ReadInt32()); // 危险:未验证
|
|
66
|
+
currentMode_ = mode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 错误示例5:位域枚举直接转换
|
|
70
|
+
enum class Flags : uint32_t {
|
|
71
|
+
FLAG_NONE = 0,
|
|
72
|
+
FLAG_A = 1 << 0,
|
|
73
|
+
FLAG_B = 1 << 1,
|
|
74
|
+
FLAG_C = 1 << 2
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Flags ReadFlags(Parcel& parcel)
|
|
78
|
+
{
|
|
79
|
+
uint32_t value = parcel.ReadUint32();
|
|
80
|
+
return static_cast<Flags>(value); // 危险:可能包含无效标志位
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### ✅ 修复方案
|
|
85
|
+
|
|
86
|
+
```cpp
|
|
87
|
+
// 正确示例1:验证后转换
|
|
88
|
+
enum class Color { RED = 0, GREEN = 1, BLUE = 2 };
|
|
89
|
+
|
|
90
|
+
Color ReadColor(Parcel& parcel)
|
|
91
|
+
{
|
|
92
|
+
int value = parcel.ReadInt32();
|
|
93
|
+
switch (value) {
|
|
94
|
+
case static_cast<int>(Color::RED):
|
|
95
|
+
case static_cast<int>(Color::GREEN):
|
|
96
|
+
case static_cast<int>(Color::BLUE):
|
|
97
|
+
return static_cast<Color>(value);
|
|
98
|
+
default:
|
|
99
|
+
LOGE("Invalid color value: %d", value);
|
|
100
|
+
return Color::RED; // 返回默认值
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 正确示例2:使用辅助函数验证
|
|
105
|
+
enum Status { OK = 0, ERROR = 1, PENDING = 2 };
|
|
106
|
+
|
|
107
|
+
bool IsValidStatus(int value)
|
|
108
|
+
{
|
|
109
|
+
return value == OK || value == ERROR || value == PENDING;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Status ReadStatus(Parcel& parcel)
|
|
113
|
+
{
|
|
114
|
+
int value = parcel.ReadInt32();
|
|
115
|
+
if (!IsValidStatus(value)) {
|
|
116
|
+
LOGE("Invalid status value: %d", value);
|
|
117
|
+
return OK; // 返回默认值
|
|
118
|
+
}
|
|
119
|
+
return static_cast<Status>(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 正确示例3:完整验证所有枚举值
|
|
123
|
+
enum class Type { TYPE_A = 0, TYPE_B = 1, TYPE_C = 2 };
|
|
124
|
+
|
|
125
|
+
bool IsValidType(int value)
|
|
126
|
+
{
|
|
127
|
+
return value >= static_cast<int>(Type::TYPE_A) &&
|
|
128
|
+
value <= static_cast<int>(Type::TYPE_C);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Type ReadType(Parcel& parcel)
|
|
132
|
+
{
|
|
133
|
+
int value = parcel.ReadInt32();
|
|
134
|
+
if (!IsValidType(value)) {
|
|
135
|
+
LOGE("Invalid type value: %d", value);
|
|
136
|
+
return Type::TYPE_A; // 返回默认值
|
|
137
|
+
}
|
|
138
|
+
return static_cast<Type>(value);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 正确示例4:枚举类模板验证
|
|
142
|
+
template<typename E>
|
|
143
|
+
bool IsValidEnumValue(int value, E minVal, E maxVal)
|
|
144
|
+
{
|
|
145
|
+
return value >= static_cast<int>(minVal) && value <= static_cast<int>(maxVal);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
enum class Mode { MODE_READ = 0, MODE_WRITE = 1 };
|
|
149
|
+
|
|
150
|
+
Mode ReadMode(Parcel& parcel)
|
|
151
|
+
{
|
|
152
|
+
int value = parcel.ReadInt32();
|
|
153
|
+
if (!IsValidEnumValue(value, Mode::MODE_READ, Mode::MODE_WRITE)) {
|
|
154
|
+
LOGE("Invalid mode value: %d", value);
|
|
155
|
+
return Mode::MODE_READ;
|
|
156
|
+
}
|
|
157
|
+
return static_cast<Mode>(value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 正确示例5:位域枚举验证
|
|
161
|
+
enum class Flags : uint32_t {
|
|
162
|
+
FLAG_NONE = 0,
|
|
163
|
+
FLAG_A = 1 << 0,
|
|
164
|
+
FLAG_B = 1 << 1,
|
|
165
|
+
FLAG_C = 1 << 2,
|
|
166
|
+
ALL_FLAGS = FLAG_A | FLAG_B | FLAG_C
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
Flags ReadFlags(Parcel& parcel)
|
|
170
|
+
{
|
|
171
|
+
uint32_t value = parcel.ReadUint32();
|
|
172
|
+
// 验证是否为有效的标志组合
|
|
173
|
+
if ((value & ~static_cast<uint32_t>(Flags::ALL_FLAGS)) != 0) {
|
|
174
|
+
LOGE("Invalid flags value: 0x%x", value);
|
|
175
|
+
return Flags::FLAG_NONE;
|
|
176
|
+
}
|
|
177
|
+
return static_cast<Flags>(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 正确示例6:使用映射表验证
|
|
181
|
+
enum class Command { CMD_START = 0, CMD_STOP = 1, CMD_PAUSE = 2, CMD_RESUME = 3 };
|
|
182
|
+
|
|
183
|
+
static const std::unordered_set<int> VALID_COMMANDS = {
|
|
184
|
+
static_cast<int>(Command::CMD_START),
|
|
185
|
+
static_cast<int>(Command::CMD_STOP),
|
|
186
|
+
static_cast<int>(Command::CMD_PAUSE),
|
|
187
|
+
static_cast<int>(Command::CMD_RESUME),
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
Command ReadCommand(Parcel& parcel)
|
|
191
|
+
{
|
|
192
|
+
int value = parcel.ReadInt32();
|
|
193
|
+
if (VALID_COMMANDS.find(value) == VALID_COMMANDS.end()) {
|
|
194
|
+
LOGE("Invalid command value: %d", value);
|
|
195
|
+
return Command::CMD_START;
|
|
196
|
+
}
|
|
197
|
+
return static_cast<Command>(value);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 检测范围
|
|
202
|
+
|
|
203
|
+
检查以下模式:
|
|
204
|
+
|
|
205
|
+
1. `static_cast<EnumType>(parcel.ReadInt32())`
|
|
206
|
+
2. `static_cast<EnumType>(parcel.ReadUint32())`
|
|
207
|
+
3. `(EnumType)parcel.ReadInt32()`
|
|
208
|
+
4. 隐式枚举转换
|
|
209
|
+
|
|
210
|
+
## 检测要点
|
|
211
|
+
|
|
212
|
+
1. 识别`enum`和`enum class`定义
|
|
213
|
+
2. 追踪Parcel读取变量到枚举转换
|
|
214
|
+
3. 检查是否进行了值有效性验证
|
|
215
|
+
4. 识别`static_cast`和C风格转换
|
|
216
|
+
5. 排除NOPROTECT标记的代码
|
|
217
|
+
|
|
218
|
+
## 风险流分析(RiskFlow)
|
|
219
|
+
|
|
220
|
+
- **RISK_SOURCE**: Parcel读取的不可信整数
|
|
221
|
+
- **RISK_TYPE**: 无效枚举值转换
|
|
222
|
+
- **RISK_PATH**: 不可信数据 -> 枚举转换 -> 未定义行为
|
|
223
|
+
- **IMPACT_POINT**: 程序逻辑错误、安全漏洞
|
|
224
|
+
|
|
225
|
+
## 影响分析(ImpactAnalysis)
|
|
226
|
+
|
|
227
|
+
- **Trigger**: Parcel数据直接转换为枚举类型
|
|
228
|
+
- **Propagation**: 无效枚举值导致switch/default分支异常
|
|
229
|
+
- **Consequence**: 未定义行为、程序崩溃、安全漏洞
|
|
230
|
+
- **Mitigation**: 转换前验证值在枚举范围内
|
|
231
|
+
|
|
232
|
+
## 误报排除
|
|
233
|
+
|
|
234
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
235
|
+
|------|----------|----------|
|
|
236
|
+
| NOPROTECT 标记 | 有 // NOPROTECT 注释 | 不报 |
|
|
237
|
+
| 已有验证逻辑 | 存在switch/范围检查 | 不报 |
|
|
238
|
+
| 内部枚举 | 仅在内部使用,非外部输入 | 不报 |
|
|
239
|
+
| 第三方库 | 位于 third_party 目录 | 白名单排除 |
|
|
240
|
+
## 测试用例
|
|
241
|
+
|
|
242
|
+
### 触发用例(应该报)
|
|
243
|
+
|
|
244
|
+
```cpp
|
|
245
|
+
// test_BoundaryCondition_005_trigger.cpp
|
|
246
|
+
enum class Color { RED = 0, GREEN = 1, BLUE = 2 };
|
|
247
|
+
|
|
248
|
+
Color trigger_bad_1(Parcel& parcel)
|
|
249
|
+
{
|
|
250
|
+
int value = parcel.ReadInt32();
|
|
251
|
+
return static_cast<Color>(value); // 应该报:未验证
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
enum Status { OK = 0, ERROR = 1 };
|
|
255
|
+
|
|
256
|
+
Status trigger_bad_2(Parcel& parcel)
|
|
257
|
+
{
|
|
258
|
+
int value = parcel.ReadInt32();
|
|
259
|
+
return (Status)value; // 应该报:C风格转换未验证
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
void trigger_bad_3(Parcel& parcel)
|
|
263
|
+
{
|
|
264
|
+
enum class Mode { READ, WRITE };
|
|
265
|
+
Mode mode = static_cast<Mode>(parcel.ReadInt32()); // 应该报:未验证
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 安全用例(不应该报)
|
|
270
|
+
|
|
271
|
+
```cpp
|
|
272
|
+
// test_BoundaryCondition_005_safe.cpp
|
|
273
|
+
enum class Color { RED = 0, GREEN = 1, BLUE = 2 };
|
|
274
|
+
|
|
275
|
+
Color safe_good_1(Parcel& parcel)
|
|
276
|
+
{
|
|
277
|
+
int value = parcel.ReadInt32();
|
|
278
|
+
if (value >= 0 && value <= 2) { // 安全:有验证
|
|
279
|
+
return static_cast<Color>(value);
|
|
280
|
+
}
|
|
281
|
+
return Color::RED;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Color safe_good_2(Parcel& parcel)
|
|
285
|
+
{
|
|
286
|
+
int value = parcel.ReadInt32();
|
|
287
|
+
switch (value) { // 安全:switch验证
|
|
288
|
+
case 0: return Color::RED;
|
|
289
|
+
case 1: return Color::GREEN;
|
|
290
|
+
case 2: return Color::BLUE;
|
|
291
|
+
default: return Color::RED;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// NOPROTECT: 内部使用
|
|
296
|
+
enum Status { OK, ERROR };
|
|
297
|
+
Status noprotect_case(Parcel& parcel)
|
|
298
|
+
{
|
|
299
|
+
return static_cast<Status>(parcel.ReadInt32());
|
|
300
|
+
}
|
|
301
|
+
```
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_BoundaryCondition_006"
|
|
3
|
+
name: "除法和模运算需做除零保护"
|
|
4
|
+
category: "边界条件"
|
|
5
|
+
severity: "CRITICAL"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 除法和模运算需做除零保护
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
除法运算和模运算需要做除零保护,除数为零会导致程序崩溃或未定义行为。从外部输入获取的除数必须在进行运算前进行非零检查。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 错误示例1:外部输入作为除数
|
|
22
|
+
int Calculate(int a, int b)
|
|
23
|
+
{
|
|
24
|
+
return a / b; // 危险:b可能为0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 错误示例2:函数参数作为除数
|
|
28
|
+
double Divide(double numerator, double denominator)
|
|
29
|
+
{
|
|
30
|
+
return numerator / denominator; // 危险:denominator可能为0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 错误示例3:Parcel数据作为除数
|
|
34
|
+
int ProcessData(Parcel& parcel)
|
|
35
|
+
{
|
|
36
|
+
int value = parcel.ReadInt32();
|
|
37
|
+
int divisor = parcel.ReadInt32();
|
|
38
|
+
return value / divisor; // 危险:divisor可能为0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 错误示例4:模运算未检查
|
|
42
|
+
int GetRemainder(int value, int divisor)
|
|
43
|
+
{
|
|
44
|
+
return value % divisor; // 危险:divisor可能为0
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 错误示例5:复杂数学运算
|
|
48
|
+
double Compute(int a, int b, int c)
|
|
49
|
+
{
|
|
50
|
+
return a / (b - c); // 危险:(b-c)可能为0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 错误示例6:数组索引计算
|
|
54
|
+
int GetIndex(int index, int divisor)
|
|
55
|
+
{
|
|
56
|
+
return index / divisor; // 危险:divisor可能为0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 错误示例7:循环中使用除法
|
|
60
|
+
void ProcessArray(int* arr, int size, int divisor)
|
|
61
|
+
{
|
|
62
|
+
for (int i = 0; i < size; i++) {
|
|
63
|
+
arr[i] = arr[i] / divisor; // 危险:divisor可能为0
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 错误示例8:复合赋值运算符
|
|
68
|
+
void UpdateValue(int& value, int divisor)
|
|
69
|
+
{
|
|
70
|
+
value /= divisor; // 危险:divisor可能为0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 错误示例9:取模运算符
|
|
74
|
+
int ComputeHash(int key, int tableSize)
|
|
75
|
+
{
|
|
76
|
+
return key % tableSize; // 危险:tableSize可能为0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 错误示例10:表达式结果作为除数
|
|
80
|
+
int Calculate(int x, int y)
|
|
81
|
+
{
|
|
82
|
+
return 100 / (x - y); // 危险:(x-y)可能为0
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ✅ 修复方案
|
|
87
|
+
|
|
88
|
+
```cpp
|
|
89
|
+
// 正确示例1:参数检查
|
|
90
|
+
int Calculate(int a, int b)
|
|
91
|
+
{
|
|
92
|
+
if (b == 0) {
|
|
93
|
+
LOGE("Division by zero");
|
|
94
|
+
return 0; // 或抛出错误码
|
|
95
|
+
}
|
|
96
|
+
return a / b;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 正确示例2:浮点数检查
|
|
100
|
+
double Divide(double numerator, double denominator)
|
|
101
|
+
{
|
|
102
|
+
const double EPSILON = 1e-10;
|
|
103
|
+
if (std::abs(denominator) < EPSILON) {
|
|
104
|
+
LOGE("Division by zero or near-zero");
|
|
105
|
+
return 0.0;
|
|
106
|
+
}
|
|
107
|
+
return numerator / denominator;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 正确示例3:Parcel数据检查
|
|
111
|
+
int ProcessData(Parcel& parcel)
|
|
112
|
+
{
|
|
113
|
+
int value = parcel.ReadInt32();
|
|
114
|
+
int divisor = parcel.ReadInt32();
|
|
115
|
+
if (divisor == 0) {
|
|
116
|
+
LOGE("Invalid divisor from parcel");
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
return value / divisor;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 正确示例4:模运算检查
|
|
123
|
+
int GetRemainder(int value, int divisor)
|
|
124
|
+
{
|
|
125
|
+
if (divisor == 0) {
|
|
126
|
+
LOGE("Modulo by zero");
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
return value % divisor;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 正确示例5:复杂数学运算检查
|
|
133
|
+
double Compute(int a, int b, int c)
|
|
134
|
+
{
|
|
135
|
+
int denominator = b - c;
|
|
136
|
+
if (denominator == 0) {
|
|
137
|
+
LOGE("Division by zero in Compute");
|
|
138
|
+
return 0.0;
|
|
139
|
+
}
|
|
140
|
+
return static_cast<double>(a) / denominator;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 正确示例6:安全的索引计算
|
|
144
|
+
int GetIndex(int index, int divisor)
|
|
145
|
+
{
|
|
146
|
+
if (divisor == 0) {
|
|
147
|
+
LOGE("Invalid divisor for index calculation");
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
return index / divisor;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 正确示例7:循环中检查
|
|
154
|
+
void ProcessArray(int* arr, int size, int divisor)
|
|
155
|
+
{
|
|
156
|
+
if (divisor == 0) {
|
|
157
|
+
LOGE("Invalid divisor");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (int i = 0; i < size; i++) {
|
|
161
|
+
arr[i] = arr[i] / divisor;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 正确示例8:复合赋值检查
|
|
166
|
+
void UpdateValue(int& value, int divisor)
|
|
167
|
+
{
|
|
168
|
+
if (divisor == 0) {
|
|
169
|
+
LOGE("Division by zero in UpdateValue");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
value /= divisor;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 正确示例9:哈希计算检查
|
|
176
|
+
int ComputeHash(int key, int tableSize)
|
|
177
|
+
{
|
|
178
|
+
if (tableSize <= 0) {
|
|
179
|
+
LOGE("Invalid table size: %d", tableSize);
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
return key % tableSize;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 正确示例10:表达式检查
|
|
186
|
+
int Calculate(int x, int y)
|
|
187
|
+
{
|
|
188
|
+
int denominator = x - y;
|
|
189
|
+
if (denominator == 0) {
|
|
190
|
+
LOGE("Division by zero: x == y");
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
return 100 / denominator;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 正确示例11:使用辅助函数
|
|
197
|
+
template<typename T>
|
|
198
|
+
T SafeDivide(T numerator, T denominator, T defaultValue = T{})
|
|
199
|
+
{
|
|
200
|
+
if (denominator == 0) {
|
|
201
|
+
LOGE("Division by zero detected");
|
|
202
|
+
return defaultValue;
|
|
203
|
+
}
|
|
204
|
+
return numerator / denominator;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 使用
|
|
208
|
+
int result = SafeDivide(a, b, 0);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## 检测范围
|
|
212
|
+
|
|
213
|
+
检查以下模式:
|
|
214
|
+
|
|
215
|
+
1. `/` 除法运算符
|
|
216
|
+
2. `%` 模运算符
|
|
217
|
+
3. `/=` 除法赋值运算符
|
|
218
|
+
4. `%=` 模运算赋值运算符
|
|
219
|
+
5. 从外部输入获取的除数
|
|
220
|
+
|
|
221
|
+
## 检测要点
|
|
222
|
+
|
|
223
|
+
1. 识别所有除法和模运算
|
|
224
|
+
2. 追踪除数来源(参数、Parcel、外部输入)
|
|
225
|
+
3. 检查是否进行了零值检查
|
|
226
|
+
4. 识别表达式作为除数的情况
|
|
227
|
+
5. 排除NOPROTECT标记的代码
|
|
228
|
+
|
|
229
|
+
## 风险流分析(RiskFlow)
|
|
230
|
+
|
|
231
|
+
- **RISK_SOURCE**: 外部输入作为除数
|
|
232
|
+
- **RISK_TYPE**: 除零错误
|
|
233
|
+
- **RISK_PATH**: 未检查除数 -> 除法/模运算 -> 程序崩溃
|
|
234
|
+
- **IMPACT_POINT**: 程序崩溃、拒绝服务
|
|
235
|
+
|
|
236
|
+
## 影响分析(ImpactAnalysis)
|
|
237
|
+
|
|
238
|
+
- **Trigger**: 除数为零的除法或模运算
|
|
239
|
+
- **Propagation**: 触发SIGFPE信号或未定义行为
|
|
240
|
+
- **Consequence**: 程序崩溃、系统不稳定
|
|
241
|
+
- **Mitigation**: 运算前检查除数是否为零
|
|
242
|
+
|
|
243
|
+
## 误报排除
|
|
244
|
+
|
|
245
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
246
|
+
|------|----------|----------|
|
|
247
|
+
| NOPROTECT 标记 | 有 // NOPROTECT 注释 | 不报 |
|
|
248
|
+
| 已有零值检查 | 存在 != 0 或 == 0 判断 | 不报 |
|
|
249
|
+
| 常量除数 | 除数为非零常量 | 不报 |
|
|
250
|
+
| 第三方库 | 位于 third_party 目录 | 白名单排除 |
|
|
251
|
+
| 测试代码 | 位于 test 目录 | 自动跳过 |
|
|
252
|
+
## 测试用例
|
|
253
|
+
|
|
254
|
+
### 触发用例(应该报)
|
|
255
|
+
|
|
256
|
+
```cpp
|
|
257
|
+
// test_BoundaryCondition_006_trigger.cpp
|
|
258
|
+
int trigger_bad_1(int a, int b)
|
|
259
|
+
{
|
|
260
|
+
return a / b; // 应该报:未检查除数
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
int trigger_bad_2(Parcel& parcel)
|
|
264
|
+
{
|
|
265
|
+
int value = parcel.ReadInt32();
|
|
266
|
+
int divisor = parcel.ReadInt32();
|
|
267
|
+
return value / divisor; // 应该报:未检查除数
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
int trigger_bad_3(int value, int divisor)
|
|
271
|
+
{
|
|
272
|
+
return value % divisor; // 应该报:未检查模数
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
void trigger_bad_4(int& value, int divisor)
|
|
276
|
+
{
|
|
277
|
+
value /= divisor; // 应该报:未检查除数
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
int trigger_bad_5(int x, int y)
|
|
281
|
+
{
|
|
282
|
+
return 100 / (x - y); // 应该报:表达式作为除数
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 安全用例(不应该报)
|
|
287
|
+
|
|
288
|
+
```cpp
|
|
289
|
+
// test_BoundaryCondition_006_safe.cpp
|
|
290
|
+
int safe_good_1(int a, int b)
|
|
291
|
+
{
|
|
292
|
+
if (b == 0) { // 安全:有零值检查
|
|
293
|
+
LOGE("Division by zero");
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
return a / b;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
int safe_good_2(int a, int b)
|
|
300
|
+
{
|
|
301
|
+
return b != 0 ? a / b : 0; // 安全:三元运算符检查
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
int safe_good_3(int value)
|
|
305
|
+
{
|
|
306
|
+
return value / 10; // 安全:常量除数
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
int safe_good_4(int value)
|
|
310
|
+
{
|
|
311
|
+
constexpr int divisor = 2;
|
|
312
|
+
return value / divisor; // 安全:constexpr常量
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// NOPROTECT: 除数已在外部检查
|
|
316
|
+
int noprotect_case(int a, int b)
|
|
317
|
+
{
|
|
318
|
+
return a / b;
|
|
319
|
+
}
|
|
320
|
+
```
|