You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

coding_guild_cpp_zh_cn.md 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. <!-- TOC -->
  2. - [说明](#说明)
  3. - [适用范围](#适用范围)
  4. - [代码风格](#1-代码风格)
  5. - [命名](#11-命名)
  6. - [格式](#12-格式)
  7. - [注释](#13-注释)
  8. - [通用编码](#2-通用编码)
  9. - [代码设计](#21-代码设计)
  10. - [头文件和预处理](#22-头文件和预处理)
  11. - [数据类型](#23-数据类型)
  12. - [常量](#24-常量)
  13. - [变量](#25-变量)
  14. - [表达式](#26-表达式)
  15. - [转换](#27-转换)
  16. - [控制语句](#28-控制语句)
  17. - [声明与初始化](#29-声明与初始化)
  18. - [指针与数组](#210-指针与数组)
  19. - [字符串](#211-字符串)
  20. - [断言](#212-断言)
  21. - [类和对象](#213-类和对象)
  22. - [函数设计](#214-函数设计)
  23. - [函数使用](#215-函数使用)
  24. - [内存](#216-内存)
  25. - [文件](#217-文件)
  26. - [安全函数](#218-安全函数)
  27. <!-- /TOC -->
  28. ## 说明
  29. 本规范以[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)为基础,参考华为通用编码规范、安全编程规范,并结合业界共识整理而成,参与MindSpore社区的开发者首先需要遵循本规范内容,其余遵循Google C++ Style Guide规范;
  30. 如果对规则异议,建议提交issue并说明理由,经MindSpore社区运营团队评审后可接纳并修改生效;
  31. ## 适用范围
  32. MindSpore开源社区
  33. ------------------
  34. ### 1. 代码风格
  35. #### 1.1 命名
  36. ##### 规则 1.1.1 文件命名
  37. C++文件使用小写+下划线的方式命名,以`.cc`作为后缀,头文件以`.h`作为后缀,单元测试文件以`_test.cc`结尾.
  38. > a_b_c.h
  39. > a_b_c.cc
  40. > a_b_c_test.cc
  41. ##### 规则 1.1.2 局部变量、参数命名采用小写加下划线方式
  42. ```cpp
  43. void FooBar(int func_param) {
  44. int local_var;
  45. }
  46. ```
  47. ##### 规则 1.1.3 成员变量命名采用小写加下划线,并以下划线作为后缀
  48. ```cpp
  49. class FooBar {
  50. public:
  51. int mamber_var_;
  52. };
  53. ```
  54. ##### 规则 1.1.4 宏命名采用大写加下划线
  55. ```cpp
  56. #define MS_LOG(...)
  57. ```
  58. ##### 规则 1.1.5 常量、枚举命名采用k驼峰
  59. ```cpp
  60. const int kDaysInAWeek = 7;
  61. enum UrlTableErrors {
  62. kOk = 0,
  63. kErrorOutOfMemory,
  64. kErrorMalformedInput,
  65. };
  66. ```
  67. ##### 规则 1.1.6 函数命名规则
  68. 1. 类成员变量访问器:访问器命名应与变量命名规则保持一致,如:
  69. ```c++
  70. int count() {return this->count_;}
  71. ```
  72. 2. 类成员变量修改器:访问器命名应以`set_`开头,后接变量名称,如:
  73. ```c++
  74. void set_count(int count) {this->count_ = count;}
  75. ```
  76. 3. 其余类成员函数/普通函数:基于大驼峰规则命名,如:
  77. ```c++
  78. void FindPattern(...);
  79. ```
  80. #### 1.2 格式
  81. ##### 建议 1.2.1 每行字符数不要超过 120 个
  82. 如果超过120个字符,请选择合理的方式进行换行。
  83. ##### 规则 1.2.2 使用空格进行缩进,每次缩进2格
  84. ##### 规则 1.2.3 在声明指针、引用变量或参数时, `&`、`*`跟随变量名,另外一边留空格
  85. ```cpp
  86. char *c;
  87. const std::string &str;
  88. ```
  89. ##### 规则 1.2.4 if语句必须要使用大括号
  90. ```cpp
  91. // 即使if分支代码只有一行,也必须使用大括号
  92. if (cond) {
  93. single line code;
  94. }
  95. ```
  96. ##### 规则 1.2.5 for/while等循环语句必须使用大括号,即使循环体是空的,或者循环语句只有一条
  97. ##### 规则 1.2.6 表达式换行要保持换行的一致性,运算符放行末
  98. ```cpp
  99. int a = a_very_long_expression +
  100. a_very_very_long_expression +
  101. a_very_very_very_long_expression;
  102. ```
  103. ##### 规则 1.2.7 每个变量定义和赋值语句单独一行
  104. ```cpp
  105. a = 1;
  106. b = 2;
  107. c = 3;
  108. ```
  109. #### 1.3 注释
  110. ##### 规则 1.3.1 文件头注释包含版权声明
  111. 所有h文件、cc文件,均需包含如下版权声明:
  112. ```cpp
  113. /**
  114. * Copyright 2019 Huawei Technologies Co., Ltd
  115. *
  116. * Licensed under the Apache License, Version 2.0 (the "License");
  117. * you may not use this file except in compliance with the License.
  118. * You may obtain a copy of the License at
  119. *
  120. * http://www.apache.org/licenses/LICENSE-2.0
  121. *
  122. * Unless required by applicable law or agreed to in writing, software
  123. * distributed under the License is distributed on an "AS IS" BASIS,
  124. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  125. * See the License for the specific language governing permissions and
  126. * limitations under the License.
  127. */
  128. ```
  129. > 关于版权说明,应注意:
  130. > 2020年新建的文件,应该是`Copyright 2020 Huawei Technologies Co., Ltd`
  131. > 2019年创建年份,2020年修改年份,应该是`Copyright 2019-2020 Huawei Technologies Co., Ltd`
  132. ##### 规则 1.3.2 代码注释置于对应代码的上方或右边,注释符与注释内容之间要有1个空格,右置注释与前面代码至少1空格,使用`//`,而不是`/**/`
  133. ```cpp
  134. // this is multi-
  135. // line comment
  136. int foo; // this single-line comment
  137. ```
  138. ##### 规则 1.3.3 代码中禁止使用 TODO/TBD/FIXME 等注释,建议提issue跟踪
  139. ##### 建议 1.3.4 不要写空有格式的函数头注释
  140. 并不是所有的函数都需要函数头注释,函数尽量通过函数名自注释,按需写函数头注释;函数原型无法表达的,却又希望读者知道的信息,才需要加函数头注释辅助说明。
  141. 不要写无用、信息冗余的函数头,函数头注释内容可选,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等。
  142. ### 2. 通用编码
  143. #### 2.1 代码设计
  144. ##### 规则 2.1.1 对所有外部数据进行合法性检查,包括但不限于:函数入参、外部输入命名行、文件、环境变量、用户数据等
  145. ##### 规则 2.1.2 函数执行结果传递,优先使用返回值,尽量避免使用出参
  146. ```cpp
  147. FooBar *Func(const std::string &in);
  148. ```
  149. ##### 规则 2.1.3 删除无效、冗余或永不执行的代码
  150. 虽然大多数现代编译器在许多情况下可以对无效或从不执行的代码告警,响应告警应识别并清除告警;
  151. 应该主动识别无效的语句或表达式,并将其从代码中删除。
  152. ##### 规则 2.1.4 补充C++异常机制的规范
  153. ###### 规则 2.1.4.1 需要指定捕获异常种类,禁止捕获所有异常
  154. ```cpp
  155. // 错误示范
  156. try {
  157. // do something;
  158. } catch (...) {
  159. // do something;
  160. }
  161. // 正确示范
  162. try {
  163. // do something;
  164. } catch (const std::bad_alloc &e) {
  165. // do something;
  166. }
  167. ```
  168. #### 2.2 头文件和预处理
  169. ##### 规则 2.2.1 使用新的标准C++头文件
  170. ```cpp
  171. // 正确示范
  172. #include <cstdlib>
  173. // 错误示范
  174. #include <stdlib.h>
  175. ```
  176. ##### 规则 2.2.2 禁止头文件循环依赖
  177. 头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h之类导致任何一个头文件修改,都导致所有包含了a.h/b.h/c.h的代码全部重新编译一遍。
  178. 头文件循环依赖直接体现了架构设计上的不合理,可通过优化架构去避免。
  179. ##### 规则 2.2.3 禁止包含用不到的头文件
  180. ##### 规则 2.2.4 禁止通过 extern 声明的方式引用外部函数接口、变量
  181. ##### 规则 2.2.5 禁止在extern "C"中包含头文件
  182. ##### 规则 2.2.6 禁止在头文件中或者#include之前使用using导入命名空间
  183. #### 2.3 数据类型
  184. ##### 建议 2.3.1 避免滥用 typedef或者#define 对基本类型起别名
  185. ##### 规则 2.3.2 使用using 而非typedef定义类型的别名,避免类型变化带来的散弹式修改
  186. ```cpp
  187. // 正确示范
  188. using FooBarPtr = std::shared_ptr<FooBar>;
  189. // 错误示范
  190. typedef std::shared_ptr<FooBar> FooBarPtr;
  191. ```
  192. #### 2.4 常量
  193. ##### 规则 2.4.1 禁止使用宏表示常量
  194. ##### 规则 2.4.2 禁止使用魔鬼数字\字符串
  195. ##### 建议 2.4.3 建议每个常量保证单一职责
  196. #### 2.5 变量
  197. ##### 规则 2.5.1 优先使用命名空间来管理全局常量,如果和某个class有直接关系的,可以使用静态成员常量
  198. ```cpp
  199. namespace foo {
  200. int kGlobalVar;
  201. class Bar {
  202. private:
  203. static int static_member_var_;
  204. };
  205. }
  206. ```
  207. ##### 规则 2.5.2 尽量避免使用全局变量,谨慎使用单例模式,避免滥用
  208. ##### 规则 2.5.3 禁止在变量自增或自减运算的表达式中再次引用该变量
  209. ##### 规则 2.5.4 指向资源句柄或描述符的指针变量在资源释放后立即赋予新值或置为NULL
  210. ##### 规则 2.5.5 禁止使用未经初始化的变量
  211. #### 2.6 表达式
  212. ##### 建议 2.6.1 表达式的比较遵循左侧倾向于变化、右侧倾向于不变的原则
  213. ```cpp
  214. // 正确示范
  215. if (ret != SUCCESS) {
  216. ...
  217. }
  218. // 错误示范
  219. if (SUCCESS != ret) {
  220. ...
  221. }
  222. ```
  223. ##### 规则 2.6.2 通过使用括号明确操作符的优先级,避免出现低级错误
  224. ```cpp
  225. // 正确示范
  226. if (cond1 || (cond2 && cond3)) {
  227. ...
  228. }
  229. // 错误示范
  230. if (cond1 || cond2 && cond3) {
  231. ...
  232. }
  233. ```
  234. #### 2.7 转换
  235. ##### 规则 2.7.1 使用有C++提供的类型转换,而不是C风格的类型转换,避免使用const_cast和reinterpret_cast
  236. #### 2.8 控制语句
  237. ##### 规则 2.8.1 switch语句要有default分支
  238. #### 2.9 声明与初始化
  239. ##### 规则 2.9.1 禁止用`memcpy_s`、`memset_s`初始化非POD对象
  240. #### 2.10 指针和数组
  241. ##### 规则 2.10.1 禁止持有std::string的c_str()返回的指
  242. ```cpp
  243. // 错误示范
  244. const char * a = std::to_string(12345).c_str();
  245. ```
  246. ##### 规则 2.10.2 优先使用unique_ptr 而不是shared_ptr
  247. ##### 规则 2.10.3 使用std::make_shared 而不是new 创建shared_ptr
  248. ```cpp
  249. // 正确示范
  250. std::shared_ptr<FooBar> foo = std::make_shared<FooBar>();
  251. // 错误示范
  252. std::shared_ptr<FooBar> foo(new FooBar());
  253. ```
  254. ##### 规则 2.10.4 使用智能指针管理对象,避免使用new/delete
  255. ##### 规则 2.10.5 禁止使用auto_ptr
  256. ##### 规则 2.10.6 对于指针和引用类型的形参,如果是不需要修改的,要求使用const
  257. ##### 规则 2.10.7 数组作为函数参数时,必须同时将其长度作为函数的参数
  258. ```cpp
  259. int ParseMsg(BYTE *msg, size_t msgLen) {
  260. ...
  261. }
  262. ```
  263. #### 2.11 字符串
  264. ##### 规则 2.11.1 对字符串进行存储操作,确保字符串有’\0’结束符
  265. #### 2.12 断言
  266. ##### 规则 2.12.1 断言不能用于校验程序在运行期间可能导致的错误,可能发生的运行错误要用错误处理代码来处理
  267. #### 2.13 类和对象
  268. ##### 规则 2.13.1 单个对象释放使用delete,数组对象释放使用delete []
  269. ```cpp
  270. const int kSize = 5;
  271. int *number_array = new int[kSize];
  272. int *number = new int();
  273. ...
  274. delete[] number_array;
  275. number_array = nullptr;
  276. delete number;
  277. number = nullptr;
  278. ```
  279. ##### 规则 2.13.2 禁止使用std::move操作const对象
  280. ##### 规则 2.13.3 严格使用virtual/override/final修饰虚函数
  281. ```cpp
  282. class Base {
  283. public:
  284. virtual void Func();
  285. };
  286. class Derived : public Base {
  287. public:
  288. void Func() override;
  289. };
  290. class FinalDerived : public Derived {
  291. public:
  292. void Func() final;
  293. };
  294. ```
  295. #### 2.14 函数设计
  296. ##### 规则 2.14.1 使用 RAII 特性来帮助追踪动态分配
  297. ```cpp
  298. // 正确示范
  299. {
  300. std::lock_guard<std::mutex> lock(mutex_);
  301. ...
  302. }
  303. ```
  304. ##### 规则 2.14.2 非局部范围使用lambdas时,避免按引用捕获
  305. ```cpp
  306. {
  307. int local_var = 1;
  308. auto func = [&]() { ...; std::cout << local_var << std::endl; };
  309. thread_pool.commit(func);
  310. }
  311. ```
  312. ##### 规则 2.14.3 禁止虚函数使用缺省参数值
  313. ##### 建议 2.14.4 使用强类型参数\成员变量,避免使用void*
  314. #### 2.15 函数使用
  315. ##### 规则 2.15.1 函数传参传递,要求入参在前,出参在后
  316. ```cpp
  317. bool Func(const std::string &in, FooBar *out1, FooBar *out2);
  318. ```
  319. ##### 规则 2.15.2 函数传参传递,要求入参用`const T &`,出参用 `T *`
  320. ```cpp
  321. bool Func(const std::string &in, FooBar *out1, FooBar *out2);
  322. ```
  323. ##### 规则 2.15.3 函数传参传递,不涉及所有权的场景,使用T * 或const T & 作为参数,而不是智能指针
  324. ```cpp
  325. // 正确示范
  326. bool Func(const FooBar &in);
  327. // 错误示范
  328. bool Func(std::shared_ptr<FooBar> in);
  329. ```
  330. ##### 规则 2.15.4 函数传参传递,如需传递所有权,建议使用shared_ptr + move传参
  331. ```cpp
  332. class Foo {
  333. public:
  334. explicit Foo(shared_ptr<T> x):x_(std::move(x)){}
  335. private:
  336. shared_ptr<T> x_;
  337. };
  338. ```
  339. ##### 规则 2.15.5 单参数构造函数必须用explicit修饰,多参数构造函数禁止使用explicit修饰
  340. ```cpp
  341. explicit Foo(int x); //good :white_check_mark:
  342. explicit Foo(int x, int y=0); //good :white_check_mark:
  343. Foo(int x, int y=0); //bad :x:
  344. explicit Foo(int x, int y); //bad :x:
  345. ```
  346. ##### 规则 2.15.6 拷贝构造和拷贝赋值操作符应该是成对出现或者禁止
  347. ```cpp
  348. class Foo {
  349. private:
  350. Foo(const Foo&) = default;
  351. Foo& operator=(const Foo&) = default;
  352. Foo(Foo&&) = delete;
  353. Foo& operator=(Foo&&) = delete;
  354. };
  355. ```
  356. ##### 规则 2.15.7 禁止保存、delete指针参数
  357. ##### 规则 2.15.8 禁止使用非安全函数,需要给出清单
  358. ##### 规则 2.15.9 禁止使用非安全退出函数,需要给出清单
  359. ```cpp
  360. {
  361. kill(...); // 调用kill强行终止其他进程(如kill -9),会导致其他进程的资源得不到清理。
  362. TerminateProcess(); // 调用erminateProcess函数强行终止其他进程,会导致其他进程的资源得不到清理。
  363. pthread_exit(); // 严禁在线程内主动终止自身线程,线程函数在执行完毕后会自动、安全地退出;
  364. ExitThread(); // 严禁在线程内主动终止自身线程,线程函数在执行完毕后会自动、安全地退出;
  365. exit(); // main函数以外,禁止任何地方调用,程序应该安全退出;
  366. ExitProcess(); // main函数以外,禁止任何地方调用,程序应该安全退出;
  367. abort(); // 禁用,abort会导致程序立即退出,资源得不到清;
  368. }
  369. ```
  370. ##### 规则 2.15.10 禁用rand函数产生用于安全用途的伪随机数
  371. C标准库rand()函数生成的是伪随机数,请使用/dev/random生成随机数。
  372. ##### 规则 2.15.11 严禁使用string类存储敏感信息
  373. string类是C++内部定义的字符串管理类,如果口令等敏感信息通过string进行操作,在程序运行过程中,敏感信息可
  374. 能会散落到内存的各个地方,并且无法清0。
  375. 以下代码,Foo函数中获取密码,保存到string变量password中,随后传递给VerifyPassword函数,在这个过程中,
  376. password实际上在内存中出现了2份。
  377. ```cpp
  378. int VerifyPassword(string password) {
  379. //...
  380. }
  381. int Foo() {
  382. string password = GetPassword();
  383. VerifyPassword(password);
  384. ...
  385. }
  386. ```
  387. 应该使用char或unsigned char保存敏感信息,如下代码:
  388. ```cpp
  389. int VerifyPassword(const char *password) {
  390. //...
  391. }
  392. int Foo() {
  393. char password[MAX_PASSWORD] = {0};
  394. GetPassword(password, sizeof(password));
  395. VerifyPassword(password);
  396. ...
  397. }
  398. ```
  399. ##### 规则 2.15.12 内存中的敏感信息使用完毕后立即清0
  400. 口令、密钥等敏感信息使用完毕后立即清0,避免被攻击者获取。
  401. #### 2.16 内存
  402. ##### 规则 2.16.1 内存分配后必须判断是否成功
  403. 内存分配失败后,那么后续的操作存在未定义的行为风险。比如malloc申请失败返回了空指针,对空指针的解引用是一种未定义行为。
  404. ##### 规则 2.16.2 禁止引用未初始化的内存
  405. malloc、new分配出来的内存没有被初始化为0,要确保内存被引用前是被初始化的。
  406. ##### 规则 2.16.3 避免使用realloc()函数
  407. 随着参数的不同,realloc函数行为也不同,这不是一个设计良好的函数。虽然在编码中提供了一些便利性,但是却极易引发各种bug。
  408. ##### 规则 2.16.4 不要使用alloca()函数申请栈上内存
  409. POSIX和C99均未定义alloca()的行为,在有些平台下不支持该函数,使用alloca会降低程序的兼容性和可移植性,该函数在栈帧里申请内存,申请的大小很可能超过栈的边界,影响后续的代码执行。
  410. #### 2.17 文件
  411. ##### 规则 2.17.1 必须对文件路径进行规范化后再使用
  412. 当文件路径来自外部数据时,需要先将文件路径规范化,如果没有作规范化处理,攻击者就有机会通过恶意构造文件路径进行文件的越权访问:
  413. 例如,攻击者可以构造“../../../etc/passwd”的方式进行任意文件访问。
  414. 在linux下,使用realpath函数,在windows下,使用PathCanonicalize函数进行文件路径的规范化。
  415. 【错误代码示例】
  416. 以下代码从外部获取到文件名称,拼接成文件路径后,直接对文件内容进行读取,导致攻击者可以读取到任意文件的内容:
  417. ```cpp
  418. char *fileName = GetMsgFromRemote();
  419. ...
  420. sprintf_s(untrustPath, sizeof(untrustPath), "/tmp/%s", fileName);
  421. char *text = ReadFileContent(untrustPath); // Bad,读取前未检查untrustPath是否允许访问
  422. ```
  423. 【正确代码示例】
  424. 正确的做法是,对路径进行规范化后,再判断路径是否是本程序所认为的合法的路径:
  425. ```cpp
  426. char *fileName = GetMsgFromRemote();
  427. ...
  428. sprintf_s(untrustPath, sizeof(untrustPath), "/tmp/%s", fileName);
  429. char path[PATH_MAX] = {0};
  430. if (realpath(untrustPath, path) == NULL) {
  431. //error
  432. ...
  433. }
  434. if (!IsValidPath(path)) { // Good,检查文件位置是否正确
  435. //error
  436. ...
  437. }
  438. char *text = ReadFileContent(untrustPath);
  439. ```
  440. 【例外】
  441. 运行于控制台的命令行程序,通过控制台手工输入文件路径,可以作为本建议例外。
  442. ##### 规则 2.17.2 不要在共享目录中创建临时文件
  443. 程序的临时文件应当是程序自身独享的,任何将自身临时文件置于共享目录的做法,将导致其他共享用户获得该程序的额外信息,产生信息泄露。因此,不要在任何共享目录创建仅由程序自身使用的临时文件。
  444. 如Linux下的/tmp目录是一个所有用户都可以访问的共享目录,不应在该目录下创建仅由程序自身使用的临时文件。
  445. #### 2.18 安全函数
  446. <table>
  447. <thead>
  448. <tr>
  449. <th style="width: 100px;">安全函数类型</th>
  450. <th style="width: 150px;">说明</th>
  451. <th>备注</th>
  452. </tr>
  453. </thead>
  454. <tr>
  455. <td rowspan="1">xxx_s</td>
  456. <td>Huawei Secure C库的安全函数API</td>
  457. <td>集成Huawei Secure C库即可使用</td>
  458. </tr>
  459. <tr>
  460. <td rowspan="1">xxx_sp</td>
  461. <td>Huawei Secure C库的安全函数性能优化API(宏实现)</td>
  462. <td>
  463. 性能优化宏接口对count、destMax、strSrc为常量时有优化效果,如果是变量则优化效果不明显.宏接口使用策略:默认使用_s接口,在性能敏感的调用点受限使用_sp接口,受限场景如下:
  464. a) memset_sp/memcpy_sp使用场景:destMax和count为常量
  465. b) strcpy_sp/strcat_sp使用场景:destMax为常量且strSrc为字面量
  466. c) strncpy_sp/strncat_sp使用场景:destMax和count为常量且strSrc为字面量</td>
  467. </tr>
  468. </table>
  469. ##### 规则 2.18.1 请使用社区提供的安全函数库的安全函数,禁止使用内存操作类危险函数
  470. <table>
  471. <thead>
  472. <tr>
  473. <th style="width: 100px;">函数类别</th>
  474. <th style="width: 150px;">危险函数</th>
  475. <th>安全替代函数</th>
  476. </tr>
  477. </thead>
  478. <tr>
  479. <td rowspan="4">内存拷贝</td>
  480. <td>memcpy或bcopy</td>
  481. <td>memcpy_s</td>
  482. </tr>
  483. <tr>
  484. <td>wmemcpy</td>
  485. <td>wmemcpy_s</td>
  486. </tr>
  487. <tr>
  488. <td>memmove</td>
  489. <td>memmove_s</td>
  490. </tr>
  491. <tr>
  492. <td>wmemmove</td>
  493. <td>wmemmove_s</td>
  494. </tr>
  495. <tr>
  496. <td rowspan="4">字符串拷贝</td>
  497. <td>strcpy</td>
  498. <td>strcpy_s</td>
  499. </tr>
  500. <tr>
  501. <td>wcscpy</td>
  502. <td>wcscpy_s</td>
  503. </tr>
  504. <tr>
  505. <td>strncpy</td>
  506. <td>strncpy_s</td>
  507. </tr>
  508. <tr>
  509. <td>wcsncpy</td>
  510. <td>wcsncpy_s</td>
  511. </tr>
  512. <tr>
  513. <td rowspan="4">字符串串接</td>
  514. <td>strcat</td>
  515. <td>strcat_s</td>
  516. </tr>
  517. <tr>
  518. <td>wcscat</td>
  519. <td>wcscat_s</td>
  520. </tr>
  521. <tr>
  522. <td>strncat</td>
  523. <td>strncat_s</td>
  524. </tr>
  525. <tr>
  526. <td>wcsncat</td>
  527. <td>wcsncat_s</td>
  528. </tr>
  529. <tr>
  530. <td rowspan="6">格式化输出</td>
  531. <td>sprintf</td>
  532. <td>sprintf_s</td>
  533. </tr>
  534. <tr>
  535. <td>swprintf</td>
  536. <td>swprintf_s</td>
  537. </tr>
  538. <tr>
  539. <td>vsprintf</td>
  540. <td>vsprintf_s</td>
  541. </tr>
  542. <tr>
  543. <td>vswprintf</td>
  544. <td>vswprintf_s</td>
  545. </tr>
  546. <tr>
  547. <td>snprintf</td>
  548. <td>snprintf_s 或 snprintf_truncated_s</td>
  549. </tr>
  550. <tr>
  551. <td>vsnprintf</td>
  552. <td>vsnprintf_s 或 vsnprintf_truncated_s</td>
  553. </tr>
  554. <tr>
  555. <td rowspan="12">格式化输入</td>
  556. <td>scanf</td>
  557. <td>scanf_s</td>
  558. </tr>
  559. <tr>
  560. <td>wscanf</td>
  561. <td>wscanf_s</td>
  562. </tr>
  563. <tr>
  564. <td>vscanf</td>
  565. <td>vscanf_s</td>
  566. </tr>
  567. <tr>
  568. <td>vwscanf</td>
  569. <td>vwscanf_s</td>
  570. </tr>
  571. <tr>
  572. <td>fscanf</td>
  573. <td>fscanf_s</td>
  574. </tr>
  575. <tr>
  576. <td>fwscanf</td>
  577. <td>fwscanf_s</td>
  578. </tr>
  579. <tr>
  580. <td>vfscanf</td>
  581. <td>vfscanf_s</td>
  582. </tr>
  583. <tr>
  584. <td>vfwscanf</td>
  585. <td>vfwscanf_s</td>
  586. </tr>
  587. <tr>
  588. <td>sscanf</td>
  589. <td>sscanf_s</td>
  590. </tr>
  591. <tr>
  592. <td>swscanf</td>
  593. <td>swscanf_s</td>
  594. </tr>
  595. <tr>
  596. <td>vsscanf</td>
  597. <td>vsscanf_s</td>
  598. </tr>
  599. <tr>
  600. <td>vswscanf</td>
  601. <td>vswscanf_s</td>
  602. </tr>
  603. <tr>
  604. <td rowspan="1">标准输入流输入</td>
  605. <td>gets</td>
  606. <td>gets_s</td>
  607. </tr>
  608. <tr>
  609. <td rowspan="1">内存初始化</td>
  610. <td>memset</td>
  611. <td>memset_s</td>
  612. </tr>
  613. </table>
  614. ##### 规则 2.18.2 正确设置安全函数中的destMax参数
  615. ##### 规则 2.18.3 禁止封装安全函数
  616. ##### 规则 2.18.4 禁止用宏重命名安全函数
  617. ```cpp
  618. #define XXX_memcpy_s memcpy_s
  619. #define SEC_MEM_CPY memcpy_s
  620. #define XX_memset_s(dst, dstMax, val, n) memset_s((dst), (dstMax), (val), (n))
  621. ```
  622. ##### 规则 2.18.5 禁止自定义安全函数
  623. 使用宏重命名安全函数不利于静态代码扫描工具(非编译型)定制针对安全函数误用的规则,同时,由于命名风格多
  624. 样,也不利于提示代码开发者函数的真实用途,容易造成对代码的误解及重命名安全函数的误用。重命名安全函数不
  625. 会改变安全函数本身的检查能力。
  626. ```cpp
  627. void MemcpySafe(void *dest, unsigned int destMax, const void *src, unsigned int count) {
  628. ...
  629. }
  630. ```
  631. ##### 规则 2.18.6 必须检查安全函数返回值,并进行正确的处理
  632. 原则上,如果使用了安全函数,需要进行返回值检查。如果返回值!=EOK, 那么本函数一般情况下应该立即返回,不
  633. 能继续执行。
  634. 安全函数有多个错误返回值,如果安全函数返回失败,在本函数返回前,根据产品具体场景,可以做如下操作(执行
  635. 其中一个或多个措施):
  636. (1)记录日志
  637. (2)返回错误
  638. (3)调用abort立即退出程序
  639. ```cpp
  640. {
  641. ...
  642. err = memcpy_s(destBuff, destMax, src, srcLen);
  643. if (err != EOK) {
  644. MS_LOG("memcpy_s failed, err = %d\n", err);
  645. return FALSE;
  646. }
  647. ...
  648. }
  649. ```
  650. ##### 规则 2.18.7 禁止外部可控数据作为system、popen、WinExec、ShellExecute、execl, xeclp, execle, execv, execvp、CreateProcess等进程启动函数的参数
  651. ##### 规则 2.18.8 禁止外部可控数据作为dlopen/LoadLibrary等模块加载函数的参数
  652. ##### 规则 2.18.9 禁止在信号处理例程中调用非异步安全函数
  653. 信号处理例程应尽可能简化。在信号处理例程中如果调用非异步安全函数,可能会导致函数的执行不符合预期的结
  654. 果。 下列代码中的信号处理程序通过调用fprintf()写日志,但该函数不是异步安全函数。
  655. ```cpp
  656. void Handler(int sigNum) {
  657. ...
  658. fprintf(stderr, "%s\n", info);
  659. }
  660. ```
  661. ------------------