C语言安全编码(2)
安全编码的意义是建立编程人员的攻击者思维,养成安全编码的习惯,编写出安全可靠的代码。
这是安全编码的第2篇。
1.填充数据禁止比较
例1.1
1// 例1.1
2struct str {
3 int a;
4 char s;
5 char buf[8];
6};
7
8void compare(const struct str *left, const struct str *right)
9{
10 if(memcmp(left, right, sizeof(struct str)) == 0) {
11 /*....*/
12 }
13}
1// 比较结构体中的各个字段
2void compare(const struct str *left, const struct str *right)
3{
4 if((left && right) &&
5 (left->a == right->a) &&
6 (left->s == right->s) &&
7 (memcmp(left->buf, right->buf, 8) == 0)) {
8 /*....*/
9 }
10}
2.指针和数组的边界不要超出
(1)例2.1:越界指针
1#define SIZE 24
2static int tab[SIZE];
3
4int *func(int index)
5{
6 if(index < SIZE) {
7 return tab + index;
8 }
9 return NULL;
10}
1#define SIZE 24
2static int tab[SIZE];
3
4int *func(int index)
5{
6 if(index > 0 && index < SIZE) { // 检测并拒绝无效值
7 return tab + index;
8 }
9 return NULL;
10}
1#define SIZE 24
2static int tab[SIZE];
3
4int *func(unsigned int index)
5{
6 if(index < SIZE) { // 无符号类型,自动规避负值
7 return tab + index;
8 }
9 return NULL;
10}
(2)例2.2:越界索引
1// 例2.1
2static int *tab = NULL;
3static size_t size = 0;
4
5int insert(size_t pos, int val)
6{
7 if(size < pos) {
8 int *temp;
9 size = pos +1;
10 temp = (int *)realloc(tab, size * sizeof(*tab));
11 if(temp == NULL) {
12 return -1;
13 }
14 tab = temp;
15 }
16 tab[pos] = val;
17 return 0;
18}
1static int *tab = NULL;
2static size_t size = 0;
3
4int insert(size_t pos, int val)
5{
6 if(size < pos) {
7 if((pos > SIZE-1) || ((pos + 1) > SIZE_MAX / sizeof(*tab))) {
8 return -1;
9 }
10 int *temp;
11 temp = (int *)realloc(tab, (pos+1) * sizeof(*tab));
12 if(temp == NULL) {
13 return -1;
14 }
15 size = pos +1;
16 tab = temp;
17 }
18 tab[pos] = val;
19 return 0;
20}
(3)例2.3:指针超过柔性数组成员范围
1// 例2.3
2struct Str {
3 size_t len;
4 char buf[];
5};
6
7char *find(const struct Str *s, int c) {
8 char *first = s->buf;
9 char *last = s->buf + s->len;
10
11 while(first++ != last) {
12 if(*first == (unsigned char)c) {
13 return first;
14 }
15 }
16 return NULL;
17}
18
19void func() {
20 struct Str *s = (struct Str *)malloc(sizeof(struct Str));
21 if(s == NULL) {
22 /* error */
23 }
24 s->len = 0;
25 find(s,'a')
26}
1struct Str {
2 size_t len;
3 char buf[];
4};
5
6char *find(const struct Str *s, int c) {
7 char *first = s->buf;
8 char *last = s->buf + s->len;
9
10 while(first != last) {
11 if(*++first == (unsigned char)c) { // 确认指针不会越界后再增加
12 return first;
13 }
14 }
15 return NULL;
16}
17
18void func(){
19 struct Str *s = (struct Str *)malloc(sizeof(struct Str));
20 if(s == NULL) {
21 /* error */
22 }
23 s->len = 0;
24 find(s,'a')
25}
3.变长数组的大小要在有效范围内
变长数组本质上与普通数组相同,只是在声明时使用了一个长度变量而不是长度常量,其只能在块范围内声明。若长度参数不是正值,则该行为是未定义行为,若参数过大,可能导致不可预料的行为。
(1)例3.1:
1// 例3.1
2void func(size_t size)
3{
4 int buf[size];
5 do_sth(buf, size);
6 /*....*/
7}
1// 例3.1
2void func(size_t size)
3{
4 if(size == 0||SIZE_MAX / sizeof(int) < size) {
5 return;
6 }
7 if(size < MAX_ARRAY) {
8 int buf[size];
9 do_sth(buf, size);
10 } else {
11 int *arr = (int *)malloc(size * sizeof(int));
12 if(arr == NULL) {
13 /* error */
14 }
15 do_sth(arr, size);
16 free(arr);
17 }
18}
(2)例3.2:
1// 例3.1
2#define N1 1024
3
4void *func(size_t n2)
5{
6 typedef int a[n2][N1];
7
8 A *arr = malloc(sizeof(A));
9 if(arr == NULL) {
10 /* error */
11 }
12
13 for(size_t i=0; i!=n2; i++) {
14 memset(arr[i], 0, N1*sizeof(int));
15 }
16 return arr;
17}
对于上面的情况,合适的处理方法如下:
1// 例3.1
2#define N1 1024
3
4void *func(size_t n2)
5{
6 if(n2 > SIZE_MAX / (N1 * sizeof(int))) { // 进行检查
7 return;
8 }
9
10 typedef int a[n2][N1];
11
12 A *arr = malloc(sizeof(A));
13 if(arr == NULL) {
14 /* error */
15 }
16
17 for(size_t i=0; i!=n2; i++) {
18 memset(arr[i], 0, N1*sizeof(int));
19 }
20 return arr;
21}