C安全编码--预处理
建议和规则
建议:
用内联函数或静态函数代替与函数相似的宏
在宏参数名两边加上括号
宏替换列表应该加上括号
应该使用typedef定义编码类型
不要复用标准头文件名
理解连接标记或执行字符串化时的宏替换
把头文件放在包含防护条件中
避免使用连续的问号
保证头文件名唯一
不要用不安全的函数替换安全函数
在一个do-while循环中包装多条语句的宏
规则:
不要通过连接创建统一字符名称
不要在不安全宏的参数中包含赋值、增值、减值、volatile访问或函数调用
本文地址:http://www.cnblogs.com/archimedes/p/c-security-pretreatment-.html,转载请注明源地址。
用内联函数或静态函数代替与函数相似的宏
宏是危险的,用法与真正的函数相似,但是具有不同的语义。C99在C中增加了内联函数,当内联函数和宏可以互换使用时,应该优先选择内联函数,内联替换并不是文本替换,也没有创建函数,决定一个函数是否为内联函数是一个底层的优化细节,编译器应该不依赖程序换做出这个决定,是否使用内联函数取决于目标编译器对它们的支持,它们对系统性能特征所产生的影响以及可移植性问题,静态函数常常具有与内联函数相同的优点。
下面的例子中,当传递给CUBE宏的参数是一个具有副作用的表达式时,这个宏就具有未定义的行为。
代码1:
#define CUBE(x) ((x) * (x) * (x))
/*...*/
int i = ;
int a = 81 / CUBE(++i);
在这个例子中,a的初始化表达式展开为: int a = 81/((++i) * (++i) * (++i));
解决方案:
inline int cube(int x)
{
return x * x *x;
}
/*...*/
int i = ;
int a = / cube(++i);
代码2:
#include<stdio.h>
size_t count = ;
#define EXEC_BUMP(func) (func(), ++count)
void g(void) {
printf("Called g, count = %zu.\n", count);
}
void aFunc(void) {
size_t count = ;
while(count++ <) {
EXEC_BUMP(g);
}
}
int main(void){
aFunc();
return ;
}
运行结果:

解决方案:
#include<stdio.h>
size_t count = ;
void g(void) {
printf("Called g, count = %zu.\n", count);
}
typedef void(*exec_func)(void);
inline void exec_bump(exec_func f) {
f();
++count;
}
void aFunc(void) {
size_t count = ;
while(count++ <) {
exec_bump(g);
}
}
int main(void){
aFunc();
return ;
}
运行结果:

和函数不同,宏的执行可以是交错的,两个宏单独执行时无害,但是它们在同一个表达式中组合在一起时可能导致未定义的行为:
代码3:
#define F(x) (++operations, ++calls_to_F, 2 * x)
#define G(x) (++operations, ++calls_to_G, x + 1)
/*...*/
y = F(x) + G(x);
operations变量在同一个表达式中读取并修改了2次,因此按照某种顺序,可能会接收到错误的值
解决方案:
inline int f(int x) {
++operations;
++calls_to_f;
return * x;
}
inline int g(int x) {
++operations;
++calls_to_f;
return + x;
}
/*...*/
y = f(x) + g(x);
在宏参数名两边加上括号
代码1:
#define CUBE(I) (I * I * I)
int a = / CUBE( + )
被展开为: int a = 81 / (2 + 1 * 2 + 1 * 2 + 1);
解决方案:
#define CUBE(I) ((I) * (I) * (I))
int a = / CUBE( + )
例外:当替换文本中的参数名由逗号分隔时,不管实际参数如何复杂,不需要对宏参数加上括号,因为逗号操作符的优先级低于其他任何操作符
#define FOO(a, b, c) bar(a, b, c)
/*...*/
FOO(arg1, arg2, arg3);
宏替换列表应该加上括号
宏替换列表应该加上括号,以保护表达式中所有优先级较低的操作符
代码1:
#define CUBE(X) (X) * (X) * (X)
int i = ;
int a = / CUBE(i);
//被展开为: int a = 81 / i * i * i
解决方案:
#define CUBE(X) ((X) * (X) * (X))
int i = ;
int a = / CUBE(i);
这个方案最好实现为内联函数
应该使用typedef定义编码类型
如果需要对类型进行编码,应该使用类型定义(typedef)而不是宏定义(#define)。类型定义遵循作用域规则,而宏定义却不遵循
代码1:
#define cstring char *
cstring s1, s2;
其中s1声明为char *,s2声明为char
解决方案:
typedef char * cstring;
cstring s1, s2;
不要复用标准头文件名
如果一个文件与标准头文件同名。并且位于包含源文件的搜索路径中,其行为是未定义的
建议:不要复用标准头文件名、系统特定的头文件名或其他的头文件名
把头文件放在包含防护条件中
防止头文件没多次包含或是忘记包含,通过一种简单的技巧:每个头文件应该用#define指令定义一个符号,表示已经被包含,然后整个头文件出现在一个包含防护条件中:
#ifndef HEADER_H
#define HEADER_H
/*....header的内容*/
#endif
避免使用连续的问号
两个连续的问号表示一个三字符序列,据C99标准,在一个源文件中,下列这些3个字符的连续出现被对应的单个字符所替换
| ??= | # | ??) | ] | ??! | | |
| ??( | [ | ??' | ^ | ??> | } |
| ??/ | \ | ??< | { | ??- | ~ |
代码1:
//what is the value of a now ??/
a++;
由于??/等价于\,a++相当于被注释掉
解决方案:
//what is the value of a now? ?/
a++;
保证头文件名唯一
文件名中只有前8个字符保证是唯一的
文件名中的点号后面只有1个非数字字符
文件名中字符的大小写并不保证是区分的
代码1:
#include<stdio.h>
#include “Library.h”
#include "library.h"
#include "utilities_math.h"
#include "utilities_physics.h"
#include "my_library.h"
Library.h和library.h可能表示同一个文件,并不清楚utilities_math和utilities_physics能否进行区分
解决方案:
#include<stdio.h>
#include “Lib_main.h”
#include "lib_2.h"
#include "util_math.h"
#include "util_physics.h"
#include "my_library.h"
不要用不安全的函数替换安全函数
宏经常用于修补现有的代码,用一个标识符对另一个标识符进行全局替换,但是这种做法存在一些风险,当一个函数被一个不够安全的函数替换时,这种做法就显得特别的危险
代码:
#define vsnprintf(buf, size, fmt, list) \
vsprintf(buf, fmt, list)
vsprintf函数并不会检查边界,因此size参数将被丢弃,在使用不信任的数据的时候可能会导致潜在的缓冲区溢出问题
解决方案:
#include<stdio.h>
#ifndef __USE_ISOC99
/* 重新实现 vsnprintf()*/
#include "my_stdio.h"
#endif
在一个do-while循环中包装多条语句的宏
参考资料
《C安全编码标准》
C安全编码--预处理的更多相关文章
- 部分博文目录索引(C语言+算法)
今天将本博客的部分文章建立一个索引,方便大家进行阅读,当然每一类别中的文章都会持续的添加和更新(PS:博文主要使用C语言) 博客地址:http://www.cnblogs.com/archimedes ...
- (转)WAVE PCM 声音文件格式
WAVE文件格式是Microsoft为存储多媒体的RIFF规范的一部分.一个RIFF文件以一个文件头开始,然后是一系列的数据块.一个WAVE文件常常仅由一个WAVE块构成,WAVE块包含一个说明格式的 ...
- requests库和urllib包对比
python中有多种库可以用来处理http请求,比如python的原生库:urllib包.requests类库.urllib和urllib2是相互独立的模块,python3.0以上把urllib和ur ...
- 【转】数据预处理之独热编码(One-Hot Encoding)
原文链接:http://blog.csdn.net/dulingtingzi/article/details/51374487 问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. ...
- 机器学习实战:数据预处理之独热编码(One-Hot Encoding)
问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from ...
- 数据预处理:独热编码(One-Hot Encoding)
python机器学习-sklearn挖掘乳腺癌细胞( 博主亲自录制) 网易云观看地址 https://study.163.com/course/introduction.htm?courseId=10 ...
- 数据预处理:独热编码(One-Hot Encoding)和 LabelEncoder标签编码
一.问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 离散特征的编码分为两种情况: 1.离散特征的取值之间没有大小的意义,比如color:[red,blue],那么就使用one- ...
- 数据预处理之独热编码(One-Hot Encoding)(转载)
问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from ...
- 机器学习 数据预处理之独热编码(One-Hot Encoding)
问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from ...
随机推荐
- 通过IIS不能连接远程数据库的问题
近期遇到一个奇怪的问题:在调试MES程序时发现,如果连接的是远程的SQL SERVER数据库(通过了IIS),则提示连接失败,就是经常见到的数据库不允许远程连接的错误提示: 而且又测试了以下几种情况: ...
- oo智慧
单一职责:学 寝室不能学习,学习要去教室 开闭原则:美 爱美穿衣打扮是扩展 整容是修改,修改有风险,所以对扩展开放,对修改封闭 里氏替换:死 人会死,你是人,你会死 依赖倒置:钱 一切向钱看,钱是抽象 ...
- 基于Solr实现HBase的二级索引
文章来源:http://www.open-open.com/lib/view/open1421501717312.html 实现目的: 由于hbase基于行健有序存储,在查询时使用行健十分高效,然后想 ...
- js 变量提升+方法提升
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- Spring重点—— IOC 容器中 Bean 的生命周期
一.理解 Bean 的生命周期,对学习 Spring 的整个运行流程有极大的帮助. 二.在 IOC 容器中,Bean 的生命周期由 Spring IOC 容器进行管理. 三.在没有添加后置处理器的情况 ...
- 在Winform开发框架中,利用DevExpress控件实现数据的快速录入和选择
在实际的项目开发过程中,有好的控件或者功能模块,我都是想办法尽可能集成到我的WInform开发框架中,这样后面开发项目起来,就可以节省很多研究时间,并能重复使用,非常高效方便.在我很早之前的一篇博客& ...
- 使用“原生”HTML DOM获取input的输入值并显示
理论基础 HTML <input> value属性.value规定<input>元素的值.value对于不同input类型,用法也不同. 1.对于"button&qu ...
- jquery实现表格中点击相应行变色功能
对于一个表格,为了使我们选中的项更容易区分,需要为选中项添加高亮,同时也需要,将其他项的高亮形式去除.类似于: <!DOCTYPE html> <html lang="en ...
- Windows nexus 启动失败
现象: nexus Windows系统服务安装成功,但启动失败 D:\nexus-2.10.0-02-bundle\nexus-2.10.0-02\bin>nexus.bat Usage: ne ...
- FreeBSD的新一代包管理工具Pkg 1.3.0 发布
Pkg 是 FreeBSD 下一代的打包管理工具,用来替代原来的 pkg_info/pkg_create/pkg_add 工具,可以本地打包,也提供远程打包,主要是为了方便远程二进制包升级. Pkg ...