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 = 2;
int a = 81 / CUBE(++i);

在这个例子中,a的初始化表达式展开为: int a = 81/((++i) * (++i) * (++i));

解决方案:

inline int cube(int x)
{
    return x * x *x;
}
/*...*/
int i = 2;
int a = 81 / cube(++i);

代码2:

#include<stdio.h>
size_t count = 0;
#define EXEC_BUMP(func) (func(), ++count)
void g(void) {
    printf("Called g, count = %zu.\n", count);
}
void aFunc(void) {
    size_t count = 0;
    while(count++ <10) {
        EXEC_BUMP(g);
    }
}
int main(void){
    aFunc();
    return 0;
}

运行结果:

解决方案:

#include<stdio.h>
size_t count = 0;
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 = 0;
    while(count++ <10) {
        exec_bump(g);
    }
}
int main(void){
    aFunc();
    return 0;
}

运行结果:

和函数不同,宏的执行可以是交错的,两个宏单独执行时无害,但是它们在同一个表达式中组合在一起时可能导致未定义的行为:

代码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 2 * x;
}
inline int g(int x) {
    ++operations;
    ++calls_to_f;
    return 1 + x;
}
/*...*/
y = f(x) + g(x);

在宏参数名两边加上括号

代码1:

#define CUBE(I) (I * I * I)
int a = 81 / CUBE(2 + 1)

被展开为: int a = 81 / (2 + 1 * 2 + 1 * 2 + 1);

解决方案:

#define CUBE(I) ((I) * (I) * (I))
int a = 81 / CUBE(2 + 1)

例外:当替换文本中的参数名由逗号分隔时,不管实际参数如何复杂,不需要对宏参数加上括号,因为逗号操作符的优先级低于其他任何操作符

#define FOO(a, b, c)   bar(a, b, c)
/*...*/
FOO(arg1, arg2, arg3);

宏替换列表应该加上括号

宏替换列表应该加上括号,以保护表达式中所有优先级较低的操作符

代码1:

#define CUBE(X) (X) * (X) * (X)
int i = 3;
int a = 81 / CUBE(i);
//被展开为: int a = 81 / i * i * i

解决方案:

#define CUBE(X) ((X) * (X) * (X))
int i = 3;
int a = 81 / 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语言中do...while(0)用法小结

参考资料

《C安全编码标准》

C安全编码--预处理,布布扣,bubuko.com

时间: 2024-08-07 18:35:25

C安全编码--预处理的相关文章

(转)WAVE PCM 声音文件格式

WAVE文件格式是Microsoft为存储多媒体的RIFF规范的一部分.一个RIFF文件以一个文件头开始,然后是一系列的数据块.一个WAVE文件常常仅由一个WAVE块构成,WAVE块包含一个说明格式的fmt块和存储取样信息的数据块. 标准WAVE文件格式 偏移 长度(字节) 变量名 描述 备注 0 4 ChunkID ASCII字符“RIFF”,大端形式 RIFF头 4 4 ChunkSize 36+SubChunk2Size,即:4 + (8 + SubChunk1Size) + (8 + S

2.1.2 小试牛刀--模拟实现Windows的TCP程序

实例功能 使用Visual C++开发一个类似于Windows自带的TCP程序 源码路径 光盘\yuanma\2\TCP 本实例的目的是,使用Visual C++ 6.0开发一个类似于Windows自带的TCP程序. 1. 划分模块 项目中TCP模块的功能描述如下. (1) 服务器端能够以默认选项启动提供服务功能,默认选项包括服务器端的IP或主机名和端口号. (2) 服务器端能够根据用户指定的选项,提供服务功能,这些选项包括服务器端的IP或主机名和端口号. (3) 如果服务器以错误选项启动,则提

requests库和urllib包对比

python中有多种库可以用来处理http请求,比如python的原生库:urllib包.requests类库.urllib和urllib2是相互独立的模块,python3.0以上把urllib和urllib2合并成一个库了,requests库使用了urllib3.requests库的口号是"HTTP For Humans",为人类使用HTTP而生,用起来不知道要比python原生库好用多少呢,比起urllib包的繁琐,requests库特别简洁和容易理解.话不多说,代码为证~~~ 下

机器学习实战:数据预处理之独热编码(One-Hot Encoding)

问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from Europe", "from US", "from Asia"] ["uses Firefox", "uses Chrome", "uses Safari", "uses Internet

数据预处理:独热编码(One-Hot Encoding)

http://blog.csdn.net/pipisorry/article/details/61193868 问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from Europe", "from US", "from Asia"] ["uses Firefox", "uses Ch

数据预处理之one-hot编码

What 用二进制表示机器状态. 使用N位状态寄存器来对N个状态进行编码 参考链接 例如: 自然状态码为:000,001,010,011,100,101 独热编码为:000001,000010,000100,001000,010000,100000 Why 在机器学习中,经常有一些值为离散的属性,比如,性别(男1,女0),区域(采用区域编号).这些作为特征,其取值只代表类别,大小并没有实际意义.而机器学习算法往往是局限于应用在连续的,有序的数据上.这就需要对这类特征进行预处理,来使我们的算法效果

【转】数据预处理之独热编码(One-Hot Encoding)

原文链接:http://blog.csdn.net/dulingtingzi/article/details/51374487 问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的三个特征: ["male", "female"] ["from Europe", "from US", "from Asia"] ["uses Firefox", "

机器学习:数据预处理之独热编码(One-Hot)

在机器学习算法中,我们经常会遇到分类特征,例如:人的性别有男女,祖国有中国,美国,法国等.这些特征值并不是连续的,而是离散的,无序的.通常我们需要对其进行特征数字化. 那什么是特征数字化呢?例子如下: 性别特征:["男","女"] 祖国特征:["中国","美国,"法国"] 运动特征:["足球","篮球","羽毛球","乒乓球"] 假如某个

机器学习入门-数据预处理-数字映射和one-hot编码 1.LabelEncoder(进行数据自编码) 2.map(进行字典的数字编码映射) 3.OnehotEncoder(进行one-hot编码) 4.pd.get_dummies(直接对特征进行one-hot编码)

1.LabelEncoder() # 用于构建数字编码 2 .map(dict_map)  根据dict_map字典进行数字编码的映射 3.OnehotEncoder()  # 进行one-hot编码,输入的参数必须是二维的,因此需要做reshape,同时使用toarray() 转换为列表形式 3  pd.get_dummies(feature,drop_first=False) 如果填单个特征的话,只对一个特征做one-hot编码映射, drop_first表示去除one-hot编码后的第一列