C/C++编程规范

2023-05-16

C/C++编程规范

  • 1、strncpy、strncat
  • 2、避免字符串/内存操作函数的源指针和目标指针指向内存重叠区
  • 3、使用格式化函数时推荐使用精度说明符
  • 4、确保无符号整数运算时不会出现反转
  • 5、确保有符号整数运算时不会出现溢出
  • 6、确保整型转换时不会出现截断错误、符号错误
  • 7、把整型表达式比较或赋值为一种更大类型之前必须用这种更大类型对它进行求值
  • 8、避免对有符号整数进行位操作符运算
  • 9、内存管理
  • 10、禁止调用OS命令解析器执行命令或运行程序,防止命令注入
  • 11、禁止使用std::ostrstream,推荐使用std::ostringstream
  • 12、C++中,必须使用C++标准库替代C的字符串操作函数
  • 13、必须使用int类型来接收字符输入/输出函数的返回值,不要使用char型
  • 14、文件路径验证前,必须对其进行标准化
  • 15、访问文件时尽量使用文件描述符代替文件名作为输入,以避免竞争条件问题
  • 16、正确处理容器的erase()方法与迭代子的关系

请添加图片描述
更多资讯、知识,微信公众号搜索:“上官宏竹”。


1、strncpy、strncat

注意:strncpy、strncat等带n版本的字符串操作函数在源字符串长度超出n标识的长度时,会将包括’\0’结束符在内的超长字符串截断,导致’\0’结束符丢失。这时需要手动为目标字符串设置’\0’结束符。

	char dst[11];     // 【注意】最好每次定义时初始化为0: dst[11] = {0};
	char src[] = "0123456789";
	char *tmp = NULL;
	memset(dst, '@', sizeof(dst));
	memcpy(dst, src, strlen(src));
	dst[sizeof(dst) - 1] = ’\0’; 	//【修改】dst以’\0’结尾

2、避免字符串/内存操作函数的源指针和目标指针指向内存重叠区

在使用像memcpy、strcpy、strncpy、sscanf()、sprintf()、snprintf()和wcstombs()这样的函数时,复制重叠对象会存在未定义的行为,这种行为可能破坏数据的完整性。

memcpy与memmove的目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中。
但当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。
memmove的处理措施:
当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
当源内存的首地址大于目标内存的首地址时,实行正向拷贝
当源内存的首地址小于目标内存的首地址时,实行反向拷贝

3、使用格式化函数时推荐使用精度说明符

#define BUF_SIZE 128
void Compliant()
{
	char buffer[BUF_SIZE + 1];
	sprintf(buffer, "Usage: %.100s argument\n", argv[0]); /*【修改】字符串加上精度说明符 */
	/* ...do something... */
}
//通过精度限制从argv[0] 中只能拷贝 100 个字节。

4、确保无符号整数运算时不会出现反转

无符号数u1 u2,在计算u1+u2时,需要判断u1+u2是否大于UINT_MAX

	if((UINT_MAX - ui1) < ui2) //【修改】确保无符号整数运算时不会出现反转
	{
		return ERROR;
	}
	else
	{
		*ret = ui1+ ui2;
	}

5、确保有符号整数运算时不会出现溢出

	INT32 si1, INT32 si2;
	INT64 tmp = (INT64)si1 *(INT64)si2; /*【修改】确保有符号整数运算时不会出现溢出 */
	//++ 将32位有符号数转换成64位,并且计算完成后需要比较结果是否有符号数的范围内
	if((INT_MAX < tmp) || (INT_MIN > tmp))
	{
		return ERROR;
	}

6、确保整型转换时不会出现截断错误、符号错误

【截断错误】将一个较大整型转换为较小整型,并且该数的原值超出较小类型的表示范围,就会发生截断错误,原值的低位被保留而高位被丢弃。
【符号错误】从带符号整型转换到无符号整型会发生符号错误,符号错误并不丢失数据,但数据失去了原来的含义。带符号整型转换到无符号整型,最高位(high-order bit)会丧失其作为符号位的功能。如果该带符号整数的值非负,那么转换后值不变;如果该带符号整数的值为负,那么转换后的结果通常是一个非常大的正数。

	//++[符号错误绕过长度检查]
	int	length;       //++ 声明为无符号数
	char buf[BUF_SIZE];
	length = atoi(argv[1]); //【错误】atoi返回值可能为负数
	if (length < BUF_SIZE)  // len为负数,长度检查无效
	{
		memcpy(buf, argv[2], length); /* 带符号的len被转换为size_t类型的无符号整数,负值被解释为一个极大的正整数。memcpy()调用时引发buf缓冲区溢出*/
		printf("Data copied\n");
	}

7、把整型表达式比较或赋值为一种更大类型之前必须用这种更大类型对它进行求值

UINT32 blockNum;
UINT64 alloc = (UINT64)blockNum * 16; /*【修改】确保整型表达式转换时不出现数值错误 */
//++ 先将两个32位的数之积转换为64位再赋值给64位的变量。

8、避免对有符号整数进行位操作符运算

说明:位操作符(~、>>、<<、&、^、|)应该只用于无符号整型操作数,因为有符号整数上的有些位操作的结果是由编译器所决定的,可能会出现出乎意料的行为或编译器定义的行为。

9、内存管理

申请内存后初始化(memset )
禁止内存指针移动后(非malloc分配后的起始值),通过该指针释放内存,会出现未知错误。因为malloc一块内存后,它的前一个字节存放了分配的内存大小,free时会根据该字节所代表的大小去free内存。

10、禁止调用OS命令解析器执行命令或运行程序,防止命令注入

禁止使用system()和popen()。替代方案是POSIX的exec系列函数或Win32 API CreateProcess()等与命令解释器无关的进程创建函数来替代。
错误示例:

system(sprintf("any_exe %s", input)); //【错误】参数不是硬编码,禁止使用system

这行代码是需要执行一个名为any_exe的程序,程序参数来自用户的输入input。这种情况下,恶意用户输入参数:
happy; useradd attacker
最终shell将字符串”any_exe happy; useradd attacker”解释为两条独立的命令连续执行:
any_exe happy
useradd attacker
这样攻击者通过注入了一条命令”useradd attacker”创建了一个新用户。这明显不是程序所希望的。
改用:

if (execve("/usr/bin/any_exe", args, envs) == -1) /*【修改】使用execve代替system */

11、禁止使用std::ostrstream,推荐使用std::ostringstream

说明: std::ostrstream的使用上需要特别注意几点:
(1)str() 会调用成员函数freeze(),它会冻结字符序列,当缓冲区不够大以至于需要分配新缓冲区时,这么做可以避免事情变得复杂。
(2)str()不会附加字符串终止符号(’\0’)。
(3)data()返回所有字符串,没有附带’\0’结尾字符(目前有些编译器自动调用c_str方法了)。
上面如果不注意,就可能会导致内存访问越界、缓冲区溢出等问题,所以建议不要使用ostrstream。[C++03]标准将std::strstream标明为deprecated,替代方案是std::stringstream。ostringstream没有上述问题。
错误示例:下列代码使用了std::ostrstream,可能会导致内存访问越界等问题。

void NoCompliant()
{
	std::ostrstream mystr; //【错误】不要使用std::ostrstream
	mystr << "hello world";
	// ostream.str方法返回的指针,没有空结束符,容易造成问题
	char *p = mystr.str();
	std::cout << mystr.str() << std::endl;
}

12、C++中,必须使用C++标准库替代C的字符串操作函数

C标准的系列字符串处理函数strcpy/strcat/sprintf/scanf/gets,不检查目标缓冲区的大小,容易引入缓冲区溢出的安全漏洞。
C++标准库提供了字符串类抽象的一个公共实现std::string,支持字符串的常规操作

13、必须使用int类型来接收字符输入/输出函数的返回值,不要使用char型

说明:字符输入/输出函数fgetc()、getc()和getchar()都从一个流读取一个字符,并把它以int值的形式返回。如果这个流到达了文件尾或者发生读取错误,函数返回EOF。fputc()、putc()、putchar()和ungetc()也返回一个字符或EOF。

如果这些I/O函数的返回值需要与EOF进行比较,不要将返回值转换为char类型
因为char是有符号8位的值,int是32位的值。如果getchar()返回的字符的ASCII值为0xFF,转换为char类型后将被解释为EOF。0xFF这个值被有符号扩展后是0xFFFFFFFF,刚好等于EOF的值。
注意:对于sizeof(int) == sizeof(char)的平台,用int接收返回值也可能无法与EOF区分,这时要用feof()和ferror()检测文件尾和文件错误。

14、文件路径验证前,必须对其进行标准化

说明:当文件路径来自非信任域时,需要先将文件路径规范化再做校验。路径在验证时会有很多干扰因素,如相对路径与绝对路径,如文件的符号链接、硬链接、快捷路径、别名等。
所以在验证路径时需要对路径进行标准化,使得路径表达唯一化、无歧义。
如果没有作标准化处理,攻击者就有机会:
推荐做法:
Linux下对文件进行标准化,可以防止黑客通过构造指向系统关键文件的链接文件。realpath() 函数返回绝对路径,删除了所有符号链接:

void  Compliant(char *lpInputPath)
{
	char realpath[MAX_PATH];
	if ( realpath(inputPath, realpath) == NULL)
		/* handle error */;
	/*... do something ...*/
}

Windows下可以使用PathCanonicalize函数对文件路径进行标准化:

void  Compliant(char *lpInputPath)
{
	char realpath[MAX_PATH];
	char *lpRealPath = realpath;
	if ( PathCanonicalize(lpRealPath,lpInputPath) == NULL)
		/* handle error */;
	/*... do something ...*/
}

15、访问文件时尽量使用文件描述符代替文件名作为输入,以避免竞争条件问题

说明:该建议应用场景如下,当对文件的元信息进行操作时(比如修改它的所有者、对文件进行统计,或者修改它的权限位),首先要打开该文件,然后对打开的文件进行操作。只要有可能,应尽量避免使用获取文件名的操作,而是使用获取文件描述符的操作。这样做将避免文件在程序运行时被替换(一种可能的竞争条件)。
例如,当access()和open()两者都利用一个字符串参数而不是一个文件句柄来进行相关操作时,攻击者就可以通过在access()和open()之间的间隙替换掉原来的文件。
错误示例:下列代码使用access()函数,可能引发竞争条件问题。

void  Noncompliant(char * file)
{
	if(!access(file, W_OK))     //【不推荐】不要使用函数access(),易引发条件竞争
	{
		f = fopen(file, "w+");
		/*...*/
		/* close f after operate(f)*/
	}
	else 
	{
		fprintf(stderr, "Unable to open file %s.\n", file);
	}
}

16、正确处理容器的erase()方法与迭代子的关系

说明:调用容器的erase(iter)方法后,迭代子指向的对象被析构,迭代子已经失效,如果再对迭代子执行递增递减或者引用操作会导致程序崩溃。

//++ 错误用法:
	m_mapID2NE.erase(iter); 
	iter++;  //【错误】erase后,iter指向的对象可能已失效
//++ 正确用法:
	m_mapID2NE.erase(iter++); //【修改】将迭代子后置递增作为erase参数	

也可以使用earse方法的返回值来保存迭代子,因为返回的是被删除元素迭代子指向的下一个元素位置:iter = erase(iter)
注意这种用法可以用于list和vector的erase(),但不适用于map。因为std::map::erase()的返回值在不同STL实现版本是有差异的,有的有返回值,有的没有返回值,所以对map只能使用推荐做法。


更多资讯、知识,微信公众号搜索:“上官宏竹”。
请添加图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++编程规范 的相关文章

  • Python虚拟环境(pipenv、venv、conda一网打尽)

    Python虚拟环境详解 一 什么是虚拟环境1 什么是Python环境2 什么是虚拟环境3 常用工具 二 pipenv1 安装pipenv2 命令概览3 pipenv基本使用1 xff09 创建虚拟环境2 xff09 激活虚拟环境3 xff
  • MERGE INTO用法更新和插入多条子记录

    lt update id 61 34 addMarkChild 34 parameterType 61 34 java util HashMap 34 gt MERGE INTO t kpi simu detail person dp US
  • 51 nod 1255 字典序最小的子序列

    http www 51nod com onlineJudge questionCode html problemId 61 1255 思路 xff1a 分三种情况 xff1a 1 xff1a 栈空 xff0c 直接将字母压入 2 xff1a
  • C++ thread.join 与 terminate

    C 43 43 11 std thread join 主要函数注意事项原因解决方案 std thread 是C 43 43 11的新特性 xff0c 对于windows来说 xff0c 取代了原来的静态方法的创建方式 DWORD WINAP
  • 算法-计算无向图中两个节点之间所有的路径

    图论 寻找节点间所有路径 最近在论文中 xff0c 要获得节点间的路由 xff0c 由此又开始继续去年的算法学习 下面的就关于寻找两个节点之间所有的路径 因为是在获得路径后 xff0c 再加上权重 xff0c 所以不能将那些求最短路径的算法
  • 算法-图论_关键节点的判断

    无向图的关节点 概述 xff1a 在网络中关节点的判断将成为影响网络连通性的主要因素 节点之间通过关键点传递信息 xff0c 如在我们以太网中的网关 当网关节点失效 xff0c 那么两个网络之间的节点就不能够进行通信 在无线传感器网络中 x
  • Mongodb锁机制

    Mongodb锁机制 Mongodb使用读写锁来允许很多用户同时去读一个资源 xff0c 比如数据库或者集合 读采用的是共享锁 xff0c 写采用的是排它锁 对于大部分的读写操作 xff0c WiredTiger使用的都是乐观锁 xff0c
  • 利用pycharm创建第一个django项目

    利用pycharm创建第一个django项目 最近把python的基本语法还有一些绘图chart学习完了 xff0c 开始django学习之路 xff0c 它是个免费的强大的开源web框架 安装好python 下载pycharm之后 xff
  • VSCode中调试nodejs

    VSCode中调试nodejs 项目中用node去编译脚本 xff0c 尝试调试 1 下载nodejs 下载网址 xff1a https nodejs org zh cn 我是安装在windows环境中 xff0c 所以选择 2 启动VS
  • Bilateral Upsample

    bilateral的原理和方法已经了解过 xff0c 借鉴Bilateral的方法进行上采样 xff0c 可以使用输入的图像作为先验 xff0c 引导低分辨图像恢复成原分辨的图像 xff0c 这里简要介绍如下 xff1a 1 首先看一下联合
  • 注册表

    1 概述 是微软的一个数据库 xff0c 用于存储系统和应用程序的设置信息 参数包括控制windows的启动 xff0c 硬件驱动及windows应用程序的运行 非常重要 xff0c 谨慎操作 数据结构 注册表由键 xff0c 子键 xff
  • VS以管理员权限打开

    描述 在VS进行debug的时候 xff0c 会提示使用管理员的权限进行调试 经常出现重新打开程序的问题 xff0c 所以干脆将VS直接设置为默认管理员的角色运行 1 找到程序目录 C Program Files x86 Microsoft
  • ABAQUS仿真结果云图不对称问题

    问题描述 xff1a 在某次使用ABAQUS进行冲压仿真的过程中 xff0c 本应产生基本对称的应力应变云图 xff0c 但是结果显示并不对称 解决方法 xff1a 设置幅值曲线 ABAQUS幅值曲线介绍 在ABAQUS中 xff0c 通过
  • GreenHills基本操作:调试

    1 编译 1 1 整体编译 1 2 编译是否优化选项 或 2 调试 2 1 进入调式 2 2 调试下载 2 3 跳转至具体行 2 4 函数调用过程查看 2 5 通过文件树查找指定文件 2 6 快速查看所需函数 xff0c 设置断点
  • ROS2 humble + windows10,如何安装调试rplidar a1激光扫描传感器

    玩ROS一直是在ubuntu xff0c 最近因为各种原因 xff0c 在windows10上调试ROS2 这一典型的问题是 xff0c 很多硬件都是没有库文件的支持 xff0c 国内的很多硬件也大多只支持ROS1 完全不调试源码直接使用各
  • CloudCompare源码分析_显示渲染的程序架构

    这一讲介绍一下CloudCompare的大致绘制流程或者说绘图框架的结构 根据前面一篇对CloudCompare读取PLY文件的介绍 xff0c 很容易找出当cloudcompare读取到mesh文件 xff08 newGroup xff0
  • CloudCompare源码分析_八叉树(Octree)算法基础CC中的八叉树结构

    官方参考地址 xff1a CloudCompare octree CloudCompareWiki CC的octree算法主要体现在DgmOctree h和DgmOctree cpp中 xff0c 他采用了一种分级的结构 xff0c 最大支
  • VisualStudio2019无法正常使用printf或std::cout(卡死)的解决办法

    相信很多人碰到过这个问题 xff0c 就是在使用vs2019时 xff0c 即使是一个非常简单的程序 xff0c 如 xff0c include lt stdio h gt int main int argc char args print
  • cv2.imshow error: The function is not implemented. Rebuild the library with Windows...

    在python中调用cv2 imshow xff0c 出现这个一个报错 xff1a cv2 error OpenCV 4 5 5 D a opencv python opencv python opencv modules highgui
  • PySpark ERROR: Python in worker has different version 3.9 than that in driver 3.8

    在wiindows环境下 xff0c 使用PySpark的时候报错 xff1a Python in worker has different version 3 9 than that in driver 3 8 PySpark canno

随机推荐