《最新C&C++语言安全编程规范_V1.0.doc》由会员分享,可在线阅读,更多相关《最新C&C++语言安全编程规范_V1.0.doc(62页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、精品资料C&C+语言安全编程规范_V1.0.华为技术有限公司内部技术规范DKBA 6914-2013.05C&C+语言安全编程规范2013年05月07日发布 2013年05月07日实施华为技术有限公司Huawei Technologies Co., Ltd.版权所有 侵权必究修订声明本规范拟制与解释部门: 网络安全技术能力中心本规范的相关系列规范或文件: Java语言安全编程规范Web应用安全开发规范相关国际规范或文件一致性: 无替代或作废的其它规范或文件: 无相关规范或文件的相互关系:本规范作为C语言编程规范和C+语言编程规范安全性要求的补充和扩展。规范号主要起草部门专家主要评审部门专家修订
2、情况DKBA6914-2013.05网络安全能力中心:罗东 67107、 于鹏 90006799、苗宏 90006736、朱喜红 00210657电信软件与核心网:陈辉军 00190784无线产品线:肖飞龙 00051938网络产品线:魏建雄 00222905IT产品线:熊华梁00106214中央软件院:朱楚毅00217543、林水平 00109837、周强 00048368、辛威 00176185、鞠章蕾 00040951、谢青 00101378中央硬件院:刘永合00222758终端公司:杨棋斌 00060469企业网络:黄凯进 00040281、企业SecoSpace:王瑾 9000382
3、8中央软件院:黄茂青 00057072、卢峰 00210300网络产品线:李强 00203020、罗天 00062283、廖永强 00111217、任志清 00048956、李海蛟 00040826、陈璟 00222879、勾国凯 00048893、范佳甲 00109753中央硬件院:刘崇山 00159994、施文超 00109740企业网络:李有永 90002701IT产品线:李显才 00044635、何昌军 00061280能力中心:郭曙光 00121837网络安全实验室:林结斌 00206214电信软件与核心网:朱刚 00192988无线产品线:李瀛 00130531、王爱成 00223
4、009、杨彬 00065941、于继万 00052142、解然 00234688V1.0目 录 C&C+语言安全编程规范0 规范制定说明0.1 前言随着公司业务发展,越来越多的产品被公众、互联网所熟知,并成为安全研究组织的研究对象、黑客的漏洞挖掘目标,容易引起安全问题。安全问题影响的不只是单个产品,甚至有可能影响到公司整体声誉。产品安全涉及需求、设计、实现、部署多个环节,实现的安全是产品安全的重要一环。为了帮助产品开发团队编写安全的代码,减少甚至规避由于编码错误引入安全风险,特制定本规范。C&C+语言安全编程规范参考业界安全编码的研究成果,并结合产品编码实践的经验总结,针对C/C+语言编程中的
5、字符串操作、整数操作、内存管理、文件操作、STL库使用等方面,描述可能导致安全漏洞或潜在风险的常见错误。以期减少缓冲区溢出、整数溢出、格式化字符串攻击、命令注入攻击、目录遍历等典型安全问题。0.2 使用对象本规范的读者及使用对象主要为使用C和C+语言的开发人员、测试人员等。0.3 适用范围本规范适合于公司基于C或C+语言开发的产品。0.4 术语定义原则: 编程时必须遵守的指导思想。规则:编程时必须遵守的约定。建议:编程时必须加以考虑的约定。说明:对此原则/规则/建议进行必要的解释。错误示例:对此原则/规则/建议从反面给出例子。推荐做法:对此原则/规则/建议从正面给出例子。延伸阅读材料:建议进一
6、步阅读的参考材料。1 通用原则原则1.1:对外部输入进行校验说明:对于外部输入(包括用户输入、外部接口输入、配置文件、网络数据和环境变量等)可能用于以下场景的情况下,需要检验入参的合法性:l 输入会改变系统状态l 输入作为循环条件l 输入作为数组下标l 输入作为内存分配的尺寸参数l 输入作为格式化字符串l 输入作为业务数据(如作为命令执行参数、拼装sql语句、以特定格式持久化)l 输入影响代码逻辑这些情况下如果不对用户数据作合法性验证,很可能导致DoS、内存越界、格式化字符串漏洞、命令注入、SQL注入、缓冲区溢出、数据破坏等问题。对外部输入验证常见有如下几种方式:(1)校验输入数据长度:如果输
7、入数据是字符串,通过校验输入数据的长度可以加大攻击者实施攻击的难度,从而防止缓冲区溢出、恶意代码注入等漏洞。(2)校验输入数据的范围:如果输入数据是数值,必须校验数值的范围是否正确,是否合法、在有效值域内,例如在涉及到内存分配、数组操作、循环条件、计算等安全操作时,若没有进行输入数值有效值域的校验,则可能会造成内存分配失败、数组越界、循环异常、计算错误等问题,这可能会被攻击者利用并进行进一步的攻击。(3)输入验证前,对数据进行归一化处理以防止字符转义绕过校验:通过对输入数据进行归一化处理(规范化,按照常用字符进行编码),彻底去除元字符,可以防止字符转义绕过相应的校验而引起的安全漏洞。(4)输入
8、校验应当采用“白名单”形式:“黑名单”和“白名单”是进行数据净化的两种途径。“黑名单”尝试排斥无效的输入,而“白名单”则通过定义一个可接受的字符列表,并移除任何不接受的字符来仅仅接受有效的输入。有效输入值列表通常是一个可预知的、定义良好的集合,并且其大小易于管理。“白名单”的好处在于,程序员可以确定一个字符串中仅仅包含他认为安全的字符。“白名单”比“黑名单”更受推荐的原因是,程序员不必花力气去捕捉所有不可接受的字符,只需确保识别了可接受的字符就可以了。这样一来,程序员就不用绞尽脑汁去考虑攻击者可能尝试哪些字符来绕过检查。原则1.2:禁止在日志中保存口令、密钥说明:在日志中不能保存口令和密钥,其
9、中的口令包括明文口令和密文口令。对于敏感信息建议采取以下方法,l 不打印在日志中;l 若因为特殊原因必须要打印日志,则用“*”代替。原则1.3:及时清除存储在可复用资源中的敏感信息说明:存储在可复用资源中的敏感信息如果没有正确的清除则很有可能被低权限用户或者攻击者所获取和利用。因此敏感信息在可复用资源中保存应该遵循存储时间最短原则。可复用资源包括以下几个方面:l 堆(heap)l 栈(stack)l 数据段(data segment)l 数据库的映射缓存存储口令、密钥的变量使用完后必须显式覆盖或清空。原则1.4:正确使用经过验证的安全的标准加密算法说明:禁用私有算法或者弱加密算法(如DES,S
10、HA1等),应该使用经过验证的、安全的、公开的加密算法。加密算法分为对称加密算法和非对称加密算法。推荐使用的常用对称加密算法有:l AES推荐使用的常用非对称算法有:l RSAl 数字签名算法(DSA)此外还有验证消息完整性的安全哈希算法(SHA256)等。基于哈希算法的口令安全存储必须加入盐值(salt)。密钥长度符合最低安全要求:l AES: 128位l RSA: 2048位l DSA: 1024位l SHA: 256位原则1.5:遵循最小权限原则说明:程序在运行时可能需要不同的权限,但对于某一种权限不需要始终保留。例如,一个网络程序可能需要超级用户权限来捕获原始网络数据包,但是在执行数据
11、报分析等其它任务时,则可能不需要相同的权限。因此程序在运行时只分配能完成其任务的最小权限。过高的权限可能会被攻击者利用并进行进一步的攻击。(1)撤销权限时应遵循正确的撤销顺序:在涉及到set-user-ID和set-group-ID程序中,当有效的用户ID(user ID)和组ID(group ID)与真实的用户不同时,不但要撤销用户层面(user level)的权限而且要撤销组层面(group level)的权限。在进行这样的操作时,要保证撤销顺序的正确性。权限撤销顺序的不正确操作,可能会被攻击者获得过高的权限而进行进一步的攻击。(2) 完成权限撤销操作后,应确保权限撤销成功:不同平台下所谓
12、的“适当的权限”的意义是不相同的。例如在Solaris中,setuid()的适当的权限指的是PRIV_PROC_SETID权限在进程的有效权限集中。在BSD中意味着有效地用户ID(EUID)为0或者uid=geteuid()。而在Linux中,则是指进程具有CAP_SETUID能力并且当EUID不等于0、真正的用户ID(RUID)或者已保存的set-user ID(SSUID)中任何一个时,setuid(geteuid()是失败的。正是由于权限行为的复杂性,所以所需的权限在撤销时可能会失败。这会被攻击者利用并进行进一步的攻击。例如Kernel版本在2.2.0-2.2.15的Linux就有一个权
13、限撤销漏洞,当权限功能位置为0时,setuid(getuid()没有如预期的那样撤销权限成功。因此在进行权限撤销操作后,应该校验以保证权限撤销成功。原则1.6:删除或修改没有效果的代码说明:删除或修改一些即使执行后、也不会有任何效果的代码。一些存在的代码(声明或表达式),即使它被执行后,也不会对代码的结果或数据的状态产生任何的影响,或者产生不是所预期的效果,这样的代码在可能是由于编码错误引起的,往往隐藏着逻辑上的错误。原则1.7:删除或修改没有使用到的变量或值说明:删除或修改没有使用到的变量或值。一些变量或值存在于代码里,但并没有被使用到,这可能隐含着逻辑上的错误,需要被识别出来,删除这类语句
14、或做相应的修改。2 字符串操作安全规则2.1:确保有足够的空间存储字符串的字符数据和0结束符说明:在分配内存或者在执行字符串复制操作时,除了要保证足够的空间可以容纳字符数据,还要预留0结束符的空间,否则会造成缓冲区溢出。错误示例1:拷贝字符串时,源字符串长度可能大于目标数组空间。void main(int argc, char *argv)char dst128;if ( argc 1 )strcpy(dst, argv1); / 源字符串长度可能大于目标数组空间,造成缓冲区溢出/*/ 推荐做法:根据源字符串长度来为目标字符串分配空间。void main(int argc, char *arg
15、v)char *dst = NULL;if ( argc 1 )dst = (char *)malloc(strlen(argv1) + 1); /* 【修改】确保字符串空间足够容纳argv1 */if( dst != NULL )strncpy(dst, argv1, strlen(argv1);dststrlen(argv1) = 0; /【修改】dst以0结尾/*.dst使用后free.*/ 错误示例2:典型的差一错误,未考虑0结束符写入数组的位置,造成缓冲区溢出和内存改写。void NoCompliant()char dstARRAY_SIZE + 1;char srcARRAY_SI
16、ZE + 1;unsigned int i = 0;memset(src, , sizeof(dst);for(i=0; srci != 0 & (i sizeof(dst); +i )dsti = srci;dsti = 0; /*/ 推荐做法:void Compliant()char dstARRAY_SIZE + 1;char srcARRAY_SIZE + 1;unsigned int i = 0;memset(src, , sizeof(dst);for(i=0; srci!=0 & (i sizeof(dst) - 1 ); +i) /*【修改】考虑0结束符 */dsti = sr
17、ci;dsti = 0; /*/ 规则2.2:字符串操作过程中确保字符串有0结束符说明:字符串结束与否是以0作为标志的。没有正确地使用0结束字符串可能导致字符串操作时发生缓冲区溢出。因此对于字符串或字符数组的定义、设置、复制等操作,要给0预留空间,并保证字符串有0结束符。注意:strncpy、strncat等带n版本的字符串操作函数在源字符串长度超出n标识的长度时,会将包括0结束符在内的超长字符串截断,导致0结束符丢失。这时需要手动为目标字符串设置0结束符。错误示例1:strlen()不会将0结束符算入长度,配合memcpy使用时会丢失0结束符。void Noncompliant()char
18、dst11;char src = 0123456789;char *tmp = NULL;memset(dst, , sizeof(dst); memcpy(dst, src, strlen(src);printf(src: %s rn, src);tmp = dst; /到此,dst还没有以0结尾doputchar(*tmp);while (*tmp+); / 访问越界return; 推荐做法: 为目标字符串设置0结束符void Compliant()char dst11;char src = 0123456789;char *tmp = NULL;memset(dst, , sizeof(
19、dst);memcpy(dst, src, strlen(src);dstsizeof(dst) - 1 = 0; /【修改】dst以0结尾printf(src: %s rn, src);tmp = dst;doputchar(*tmp); while (*tmp+);return; 错误示例2:strncpy()拷贝限长字符串,截断了0结束符。 void Noncompliant()char dst5;char src = 0123456789;strncpy(dst, src, sizeof(dst); printf(dst); /访问越界,dst没有0结束符return;推荐做法: vo
20、id Compliant()char dst5; char src = 0123456789;strncpy(dst, src, sizeof(dst);dstsizeof(dst)-1 = 0; / 【修改】最后字节置为0 printf(dst); return;规则2.3:把数据复制到固定长度的内存前必须检查边界说明:将未知长度的数据复制到固定长度的内存空间可能会造成缓冲区溢出,因此在进行复制之前应首先获取并检查数据长度。典型的如来自gets()、getenv()、scanf()的字符串。错误示例:输入消息长度不可预测,不加检查的复制会造成缓冲区溢出。void Noncompliant()
21、char dst16;char * temp = getInputMsg();if(temp != NULL)strcpy(dst,temp); / temp长度可能超过dst的大小return; 推荐做法: void Compliant()char dst16;char *temp = getInputMsg();if(temp != NULL)strncpy(dst, temp, sizeof(dst); /* 【修改】只复制不超过数组dst大小的数据 */dstsizeof(dst) -1 = 0; /【修改】copy以0结尾return;规则2.4:避免字符串/内存操作函数的源指针和目
22、标指针指向内存重叠区说明:内存重叠区是指一段确定大小及地址的内存区,该内存区被多个地址指针指向或引用,这些指针介于首地址和尾地址之间。在使用像memcpy、strcpy、strncpy、sscanf()、sprintf()、snprintf()和wcstombs()这样的函数时,复制重叠对象会存在未定义的行为,这种行为可能破坏数据的完整性。错误示例1:snprintf的参数使用存在问题void Noncompliant()#define MAX_LEN 1024char cBufMAX_LEN + 1 = 0;int nPid = 0;strncpy(cBuf, ”Hello World!”,
23、 strlen(”Hello World!”);snprintf(cBuf, MAX_LEN, %d: %s, nPid, cBuf); /* cBuf既是源又是目标,函数使用不安全 */return; 推荐做法:使用不同源和目标缓冲区来实现复制功能。void Compliant()#define MAX_LEN 1024char cBufMAX_LEN + 1 = 0;char cDescMAX_LEN + 1 = 0; /【修改】另起一个缓冲区,防止缓冲区重叠出错int nPid = 0;strncpy(cDesc, ”Hello World!”, strlen(”Hello World!
24、”); /* 【修改】防止缓冲区重叠出错 */snprintf(cBuf, MAX_LEN, %d: %s, nPid, cDesc); /* 【修改】防止缓冲区重叠出错 */return;错误示例2:#define MSG_OFFSET 3#define MSG_SIZE 6void NoCompliant ()char str = test string;char *ptr1 = str;char *ptr2;ptr2 = ptr1+MSG_OFFSET;memcpy(ptr2, ptr1, MSG_SIZE);return;推荐做法:使用memmove函数,源字符串和目标字符串所指内存区
25、域可以重叠,但复制后目标字符串内容会被更改,该函数将返回指向目标字符串的指针。#define MSG_OFFSET 3#define MSG_SIZE 6void Compliant ()char str = test string;char *ptr1 = str;char *ptr2;ptr2 = ptr1 + MSG_OFFSET;memmove(ptr2, ptr1, MSG_SIZE); /*【修改】使用memmove代替memcpy,防止缓冲区重叠出错 */return;memcpy与memmove的目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中。但当源内存和目标内存存在
26、重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。memmove的处理措施:l 当源内存的首地址等于目标内存的首地址时,不进行任何拷贝l 当源内存的首地址大于目标内存的首地址时,实行正向拷贝l 当源内存的首地址小于目标内存的首地址时,实行反向拷贝3 格式化输出安全规则3.1:格式化输出函数的格式化参数和实参类型必须匹配说明:使用格式化字符串应该小心,确保格式字符和参数在数据类型上的匹配。格式字符和参数之间的不匹配会导致未定义的行为。大多数情况下,不正确的格式化字符串会可能会导致格式化漏洞,使程序异常终止。错误示例1:格式字符和参数的类型不匹配void N
27、oncompliant_ArgMismatch()char *error_msg = Resource not available to user.;int error_type = 3; /* .do something. */printf(Error (type %s): %dn, error_type, error_msg); /*【错误】格式化参数类型不匹配 */推荐做法: void Noncompliant_ArgMismatch()char *error_msg = Resource not available to user.;int error_type = 3; /* .do
28、 something. */printf(Error (type %s): %dn, error_msg, error_type); /*【修改】匹配格式化参数类型 */错误示例2:将结构体作为参数void Noncompliant_StructAsArg()struct sParam int num; char msg100; int result; ; struct sParam tmp = 10, hello Baby!, 0; char *errormsg = Resource not available to user.; int errortype = 3;/* .do somet
29、hing. */if (tmp.result = 0) printf(Error Param: %s n, tmp); /*【错误】不能将整个结构体作为格式化参数 */ 推荐做法: void Noncompliant_StructAsArg()struct sParam int num; char msg100; int result; ; struct sParam tmp = 10, hello Baby!, 0; char *errormsg = Resource not available to user.; int errortype = 3;/* .do something. */
30、if (tmp.result = 0) printf(Error Param:num=%d, msg=%s, result=%dn, tmp.num, tmp.msg, tmp.result); /【修改】将结构体的内部变量作为格式化参数 规则3.2:格式化输出函数的格式化参数和实参个数必须匹配说明:使用格式化字符串应该小心,确保格式字符和参数在数量上的匹配。格式字符和参数之间的不匹配会导致未定义的行为。大多数情况下,不正确的格式化字符串会导致程序异常终止。错误示例:格式字符和参数的数量不匹配,格式化字符串在编码时会大量使用,如拼装SQL语句和拼装调试信息。尤其是调试信息,量大时容易copy-
31、paste省事,这就容易出现不匹配的错误。void Noncompliant()char *error_msg = Resource not available to user.;/* .do something. */printf(Error (type %s)n); /【错误】格式化参数个数不匹配推荐做法:void Compliant()char *error_msg = Resource not available to user.;/* .do something. */printf(Error (type %s)n, error_msg); /【修改】使格式化参数个数匹配规则3.3:
32、禁止以用户输入来构造格式化字符串说明:调用格式化I/O函数时,不要直接或者间接将用户输入作为格式化字符串的一部分或者全部。如果攻击者对一个格式化字符串可以部分或完全控制,将导致进程崩溃、查看栈的内容、改写内存、甚至执行任意代码等风险。错误示例:下列代码直接将用户输入作为格式字符串输出。void Noncompliant(char *user, char *password) char input1000; if (fgets(input, sizeof(input) - 1, stdin) = NULL) /* handle error */ inputsizeof(input)-1 = 0;
33、 printf(input); /【错误】不允许将用户输入直接作为格式字符串示例代码的input直接来自用户输入,并作为格式化字符串直接传递给printf()。当用户输入的是“%s%s%s%s%s%s%s%s%s%s%s%s”,就可能触发无效指针或未映射的地址读取。格式字符%s显示栈上相应参数所指定的地址的内存。这里input被当成格式化字符串,而没有提供参数,因此printf()读取栈中任意内存位置,直到格式字符耗尽或者遇到一个无效指针或未映射地址为止。推荐做法:通过显式参数”%s”将 printf()的格式化字符串确定下来。void Compliant(char *user, char *
34、password) char input1000; if (fgets(input, sizeof(input)-1, stdin) = NULL) /* handle error */ inputsizeof(input)-1 = 0; printf(“%s”, input); /【修改】通过%s将格式字符串确定下来建议3.1:使用格式化函数时推荐使用精度说明符说明:使用格式化函数时(例如sprintf(),scanf_s()等),可能会含有字符串参数,应尽量为格式化指示符加上精度说明符以限制拷贝字符串的长度,防止缓冲区溢出漏洞。错误示例:使用格式化函数sprintf,没有添加精度说明符,可
35、能会导致缓冲区溢出。#define BUF_SIZE 128void NoCompliant()char bufferBUF_SIZE + 1;sprintf( buffer, Usage: %s argumentn, argv0 );/* .do something. */推荐做法:优先采用snprintf替代sprintf来防止缓冲区溢出。若没有带n版本的snprintf函数,可参考如下示例,使用sprintf,在接收字符串时,加上精度说明符,确定接收的长度,以免造成缓冲区溢出。#define BUF_SIZE 128void Compliant()char bufferBUF_SIZE
36、+ 1;sprintf(buffer, Usage: %.100s argumentn, argv0); /*【修改】字符串加上精度说明符 */* .do something. */通过精度限制从argv0 中只能拷贝 100 个字节。4 整数安全C99标准定义了整型提升(integer promotions)、整型转换级别(integer conversion rank)以及普通算术转换(usual arithmetic conversions)的整型操作。不过这些操作实际上也带来了安全风险。规则4.1:确保无符号整数运算时不会出现反转说明:反转是指无法用无符号整数表示的运算结果将会根据该类
37、型可以表示的最大值加1执行求模操作。将运算结果用于以下之一的用途,应防止反转:l 作为数组索引l 指针运算l 作为对象的长度或者大小l 作为数组的边界l 作为内存分配函数的实参错误示例:下列代码可能导致相加操作产生无符号数反转现象。INT32 NoCompliant(UINT32 ui1, UINT32 ui2, UINT32 * ret)if( NULL = ret )return ERROR;*ret = ui1 + ui2;/*上面的代码可能会导致ui1加ui2产生无符号数反转现象,譬如ui1 = UINT_MAX且ui2 = 2;这可能会导致后面的内存分配数量不足或者产生易被利用的潜在
38、风险;*/return (OK);推荐做法:INT32 Compliant(UINT32 ui1, UINT32 ui2, UINT32 * ret)if( NULL = ret )return ERROR;if(UINT_MAX - ui1) ui2) /【修改】确保无符号整数运算时不会出现反转return ERROR;else*ret = ui1+ ui2;return OK;延伸阅读材料:漏洞就是因为反转问题,导致分配内存空间不足,引发堆溢出。规则4.2:确保有符号整数运算时不会出现溢出说明:整数溢出是是一种未定义的行为,意味着编译器在处理有符号整数溢出时具有很多选择。将运算结果用于以下
39、之一的用途,应防止溢出:l 作为数组索引l 指针运算l 作为对象的长度或者大小l 作为数组的边界l 作为内存分配函数的实参错误示例:下列代码中两个有符号整数相乘可能会产生溢出。INT32 NoCompliant(INT32 si1, INT32 si2, INT32 *ret)if ( NULL = ret )return ERROR;*ret = si1 * si2;/* 上面的代码可能会产生两个有符号整数相乘可能会产生溢出,譬如si1 = INT_MAX且si2 非0;*/return OK;推荐做法:INT32 Compliant(INT32 si1, INT32 si2, INT32
40、*ret)if ( NULL = ret )return ERROR;INT64 tmp = (INT64)si1 *(INT64)si2; /*【修改】确保有符号整数运算时不会出现溢出 */if(INT_MAX tmp)return ERROR;*ret = si1 * si2;return OK;延伸阅读材料:整数溢出可能导致缓冲区溢出以及任意代码执行。Apple Mac OS X 10.3及以前版本在处理GIF文件时,存在整数溢出漏洞,可被利用执行任意代码。攻击者可以将特定的GIF文件放在Web页面或者邮件附件中,诱使目标打开此文件从而触发利用。具体可参考US-CERT披露的漏洞。规则4
41、.3:确保整型转换时不会出现截断错误说明: 将一个较大整型转换为较小整型,并且该数的原值超出较小类型的表示范围,就会发生截断错误,原值的低位被保留而高位被丢弃。截断错误会引起数据丢失,甚至可能引发安全问题。特别是将运算结果用于以下用途:l 作为数组索引l 指针运算l 作为对象的长度或者大小l 作为数组的边界(如作为循环计数器)错误示例:数据类型强制转化导致数据被截断。INT32 NoCompliant(UINT32 ui, INT8 *ret)if( NULL = ret )return ERROR;*ret = (INT8)ui;/*上面的代码会导致数据被截断,譬如ui = UINT_MAX
42、场景下*/return (OK); 推荐做法:INT32 Compliant(UINT32 ui, INT8 *ret)if(NULL = ret)return ERROR;if(SCHAR_MAX = ui) /【修改】确保整型转换时不会出现截断*ret = (INT8)ui;elsereturn ERROR; return OK; 规则4.4:确保整型转换时不会出现符号错误说明: 有时从带符号整型转换到无符号整型会发生符号错误,符号错误并不丢失数据,但数据失去了原来的含义。带符号整型转换到无符号整型,最高位(high-order bit)会丧失其作为符号位的功能。如果该带符号整数的值非负,那么转换后值不变;如果该带符号整数的值为负,那么转换后的结果通常是一个非常大的正数。错误示例:符号错误绕过长度检查#define BUF_SIZE 10int main(int argc, char* argv)intlength;char bufBUF_SIZE;if (argc != 3)return -1;