建议和规则
建议:
-
理解数组的工作方式
-
获取数组的长度时不要对指针应用sizeof操作符
-
显示地指定数组的边界,即使它已经由初始化值列表隐式地指定
规则:
-
保证数组索引位于合法的范围内
-
在所有源文件中使用一致的数组记法
-
保证变长数组的长度参数位于合法范围之内
-
保证复制的目标具有足够的存储空间
-
保证表达式中的数组类型是兼容的
-
不允许循环迭代到数组尾部之后
-
不要对两个并不指向同一个数组的指针进行相减或比较
-
不要把一个指向非数组对象的指针加上或减去一个整数
-
如果结果值并不引用合法的数组元素,不要把指针加上或减去一个整数
本文地址:http://www.cnblogs.com/archimedes/p/c-security-array.html,转载请注明源地址。
获取数组的长度时不要对指针应用sizeof操作符
代码:
void clear(int array[]) { for(size_t i = 0; i < sizeof(array) / sizeof(array[0]); i++) { array[i] = 0; } } void dowork(void) { int dis[12]; clear(dis); /*...*/ }
clear()使用sizeof(array) / sizeof(array[0])这种用法确定这个数组的元素数量,但由于array是一个形参,因此它是指针类型,sizeof(array) = sizeof(int *) = 4 (32位OS)
当sizeof操作符应用于声明为数组或函数类型的形参时,它产生经过调整的(指针)类型的长度
解决方案:
void clear(int array[], size_t len) { for(size_t i = 0; i < len; i++) { array[i] = 0; } } void dowork(void) { int dis[12]; clear(dis, sizeof(dis) / sizeof(dis[0])); /*...*/ }
保证数组索引位于合法的范围内:
代码:
enum {TABLESIZE = 100}; int *table = NULL; int insert_in_table(int pos, int value) { if(!table) { table = (int *)malloc(sizeof(int) *TABLESIZE); } if(pos >= TABLESIZE) { return -1; } table[pos] = value; return 0; }
pos为int类型,可能为负数,导致在数组所引用的内存边界之外进行写入
解决方案:
enum {TABLESIZE = 100}; int *table = NULL; int insert_in_table(size_t pos, int value) { if(!table) { table = (int *)malloc(sizeof(int) *TABLESIZE); } if(pos >= TABLESIZE) { return -1; } table[pos] = value; return 0; }
在所有源文件中使用一致的数组记法
当在同一文件中时,void func(char *a); 和 void func(char a[]); 完全等价
但在函数原型之外,如果一个数组在一个文件中声明为指针,在另一个不同的文件中声明为数组,它们是不等价的
代码:
//main.c #include<stdlib.h> enum {ARRAYSIZE = 100}; char *a; void insert_a(void); int main(void) { a = (char*)malloc(ARRAYSIZE); if(a == NULL) { //处理分配错误 } insert_a(); return 0; } //insert_a.c char a[]; void insert_a(void) { a[0] = 'a'; }
解决方案:
//insert_a.h enum {ARRAYSIZE = 100}; extern char *a; void insert_a(void); //insert_a.c #include "insert_a.h" char *a; void insert_a(void) { a[0] = 'a'; } //main.c #include<stdlib.h> #include"insert_a.h" int main(void){ a = (char*)malloc(ARRAYSIZE); if(a == NULL) { //处理分配错误 } insert_a(); return 0; }
保证变长数组的长度参数位于合法范围之内
代码:
void func(size_t s) { int vla[s]; /*...*/ } /*...*/ func(size); /*...*/
解决方案:
enum {MAX_ARRAY = 1024}; void func(size_t s) { if(s < MAX_ARRAY && s != 0) { int vla[s]; /*...*/ } else { //错误处理 } } /*...*/ func(size); /*...*/
保证复制的目标具有足够的存储空间
代码:
enum {WORKSPACE_SIZE = 256}; void func(const int src[], size_t len) { int dest[WORKSPACE_SIZE]; if(len > WORKSPACE_SIZE) { //错误处理 } memcpy(dest, src, sizeof(int) * len); /*...*/ }
保证表达式中的数组类型是兼容的
代码:
enum {a = 10, b = 15, c = 20}; int arr1[c][b]; int (*arr2)[a]; arr2 = arr1; //不匹配 a != b
解决方案:
enum {a = 10, b = 10, c = 20}; int arr1[c][b]; int (*arr2)[a]; arr2 = arr1; //匹配 a == b
不要把一个指向非数组对象的指针加上或减去一个整数
代码:
struct numbers { short num1; short num2; /*...*/ short num9; }; int sum_numbers(const struct numbers *numb) { int total = 0; const int *numb_ptr; for(numb_ptr = &numb->num1; numb_ptr <= &numb->num9; numb_ptr++) { total += *(numb_ptr); } return total; } int main(void) { struct numbers my_numbers = {1,2,3,4,5,6,7,8,9}; sum_numbers(&my_numbers); return 0; }
上面的代码试图用指针运算访问结构的元素,这是危险的,因为结构中的字段并不保证在内存中是连续的
解决方案(使用数组):
struct numbers { short num1; short num2; /*...*/ short num9; }; int sum_numbers(const short *numb, size_t dim) { int total = 0; const int *numb_ptr; for(numb_ptr = numb; numb_ptr < numb + dim; numb_ptr++) { total += *(numb_ptr); } return total; } int main(void) { short my_numbers[9] = {1,2,3,4,5,6,7,8,9}; sum_numbers(my_numbers, sizeof(my_numbers) / sizeof(my_numbers[0])); return 0; }
参考资料
《C安全编码标准》