《华为C&C++语言安全规范》笔记

《华为C&C++语言安全规范》笔记

通过阅读《华为C&C++语言安全规范》1,我了解到了我在编程中很多缺失的部分。现在记录下几个要点:

规则1.1.4:严禁对指针变量进行sizeof操作

编码人员往往由于粗心,将指针当做数组进行sizeof操作,导致实际的执行结果与预期不符。 下面的代码,buffer和path分别是指针和数组,编码人员想对这2个内存进行清0操作,但由于编码人员的疏忽,第5行代码,将内存大小误写成了sizeof,与预期不符。
如果要判断当前的指针类型大小,请使用sizeof(char *)的方式。

相关指南:
CERT.ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array

指针与数组名与指针有太多的相似,甚至很多时候,数组名可以作为指针使用。但是数组和指针存在差异。指针,是一个变量,存储的数据是地址。数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组,其外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量2。数组名只是大多数时候隐式转换成指向首元素的指针类型右值。这些时候不会转换:1)对其用 &;2)对其用 sizeof;3)C++中取引用3

我们先来一段代码作为演示:

在X86的编译环境下,输出的结果为:

显然第14行输出的是数组长度,第15行输出的是指针长度(在X86下为4字节,在x64环境下为8字节)。

第17-第20行,对数组取引用,其地址和本身的地址是一样,而指针则不一样。

第23行,当调用函数的时候,数组会转换为指针,因此长度为4,并且可以做自加运算。

规则1.2.1:断言必须使用宏定义,禁止直接调用系统提供的assert()

断言只能在调试版使用,断言被触发后,程序会立即退出,因此严禁在正式发布版本使用断言,请通过编译选项进行控制。 错误用法如:

ASSERT()是MFC的宏4,ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。
assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中5

在msvc16里面是这样定义的:

在MinGW里面

综上所述,使用语言自带的assert即可,也没有其他的选择。当在Release环境下,assert也自动编译为。对于华为的这条安全规范表示不解,希望有人能解答一下。

另外:这里还发现很有意思的东西:

/* According to C99 standard (section 7.2) the assert
macro shall be redefined each time assert.h gets
included depending on the status of NDEBUG macro. */

意思就是每次包含时,都会根据NDEBUG的当前状态重新定义assert宏6

规则1.2.3:严禁在断言内改变运行环境

在程序正式发布阶段,断言不会被编译进去,为了确保调试版和正式版的功能一致性,严禁在断言中使用任何赋值、修改变量、资源操作、内存申请等操作。 例如,以下的断言方式是错误的:

建议1.2.1:不要将多条语句放在同一个断言中

为了更加准确地发现错误的位置,每一条断言只校验一个条件。 下面的断言同时校验多个条件,在断言触发的时
候,无法判断到底是哪一个条件导致的错误:

应该将每个条件分开:

规则1.3.2:严禁对公共接口API函数的参数进行ASSERT操作

对于设计成API的函数,必须对参数进行合法性判断,严禁在API实现过程中产生RASH。对API函数的参数进行ASSERT操作是没有意义的。 例如,对于提供应用服务器IP的平台公共API接口这样实现是错误的:

公共接口API应当对输入参数进行代码检查:

建议1.3.1:谨慎使用不可重入函数

不可重入函数在多线程环境下其执行结果不能达到预期效果,需谨慎使用。常见的不可重入函数包括:
rand, srand
getenv, getenv_s
strtok
strerror
asctime, ctime, localtime, gmtime
setlocale
atomic_init
tmpnam
mbrtoc16, c16rtomb, mbrtoc32, c32rtomb
gethostbyaddr
gethostbyname
inet_ntoa

《华为C&C++语言安全规范》笔记7

建议1.3.2:字符串或指针作为函数参数时,请检查参数是否为NULL

如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL,如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 例如下面的代码,因为BYTE *p有可能为NULL,因此在使用前需要进行判断。

下面的代码,由于p的合法性由调用者保证,对于Foo函数,不可能出现p为NULL的情况,因此加上ASSERT进行校验。

int Foo(int *p, int count){    ASSERT(p != NULL); //ASSERT is added to verify p.    ASSERT(count > 0);    int c = p[0];    	...    }int Foo2(

来源:雪靡

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年5月8日
下一篇 2021年5月8日

相关推荐