首页 > 代码库 > C安全编码--数组

C安全编码--数组

建议和规则

建议:

  • 理解数组的工作方式

  • 获取数组的长度时不要对指针应用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.cchar a[];void insert_a(void) {    a[0] = a;}

解决方案:

//insert_a.henum {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安全编码标准》