文档:CERT C Programming Language Secure Coding Standard
1.用常量(const)或枚举(enum)来声明不可变值
一般而言,用const来声明不可变值而不是用宏定义。使用const的话,编译器可以检查对象类型(float, double),对象范围等。
对于整型常量,应使用enum(枚举)代替const,可以消除获取整型变量地址可能,同时避免为整型变量分配存储空间。
-
int const max = 15;
-
int a[max]; /* 会报错,invalid declaration outside of a function */
-
int const *p;
使用enum来代替const
-
enum { max = 15 };
-
int a[max]; /* OK */
-
int const *p;
2.建议不要重复定义同名变量,在一个域包含另一个域的情况下。
-
char msg[100];
-
void hello_message()
-
{
-
char msg[80] = "Hello";
-
strcpy(msg, "Error");
-
}
修改msg为error_msg.
-
char error_msg[100];
-
void hello_message()
-
{
-
char hello_msg[80] = "Hello";
-
strcpy(error_msg, "Error");
-
}
3.建议使用能可视化清楚表达含义的标识符:
比如:数字1和小写字母l,数字0和大写字母0,很容易引起混淆。
4.声明常量时,const作为声明符号放置在最右边。
-
typedef char *NTCS;
-
const NTCS p;
上述P是指向const char的指针,不是声明一个指向char的const指针。
正确声明:
-
typedef char *NTCS;
-
NTCS const p;
5.建议多个变量分别声明:
我要声明两个char *变量str1,str2。
不要认为str1和str2都是char*,实际上str1是char*,str2是char。
正确方式:
-
char *str1 = 0;
-
char *str2 = 0
6.使用typedef增强代码可读性
声明一个函数指针,其可读性差
-
void (*signal(int, void (*)(int)))(int)
使用typedef,增强可读性:
-
typedef void fv(int),
-
typedef void (*pfv)(int);
-
fv *signal(int, fv *);
-
pfv signal(int, pfv);
7.建议使用有意义的符号常量来描述文字值,代码中尽量避免使用magic number。
-
/* ... */
-
if (age >= 18) {
-
/* Take action */
-
}
-
else {
-
/* Take a different action */
-
}
-
/* ... */
使用ADULT_AGE来替代18,,更能表达代码意图。
-
enum { ADULT_AGE=18 };
-
/* ... */
-
if (age >= ADULT_AGE) {
-
/* Take action */
-
}
-
else {
-
/* Take a different action */
-
}
-
/* ... */
8.确保每一个函数都有一个函数原型。
若函数原型不可用,编译器不会对传给函数的参数进行类型检测。C的参数类型检测只发生在编译阶段,不会发生在链接或动态加载阶段。
-
#include <stdio.h>
-
extern char *strchr();
-
int main(void) {
-
char *c = strchr(12, 5);
-
printf("Hello %c!\n", *c);
-
return 0;
-
}
上述代码运行会报错。C99上说,extern char *strchr()会阻止编译时的类型检测,导致编译通过运行时报错。
正确做法:
-
#include <stdio.h>
-
#include <string.h>
-
int main(void) {
-
char *c = strchr("world", 'w');
-
printf("Hello %c!\n", *c);
-
return 0;
-
}
9.若一个函数指针接受的参数个数少于初始化它的函数参数个数,会造成段错误、数据泄露等。
-
int add(int x, int y, int z) {
-
return x + y + z;
-
}
-
int main(int argc, char *argv[]) {
-
int (*fn_ptr) (int, int) ;
-
int res;
-
fn_ptr = &add;
-
res = fn_ptr(2, 3); /* incorrect */
-
/* ... */
-
return 0;
-
}
fn_ptr(2,3)的参数个数少于add函数(要求三个参数)
-
int add(int x, int y, int z) {
-
return x + y + z;
-
}
-
int main(int argc, char *argv[]) {
-
int (*fn_ptr) (int, int, int) ;
-
int res;
-
fn_ptr = &add;
-
res = fn_ptr(2, 3, 4);
-
/* ... */
-
return 0;
-
}
10.返回errno的函数返回值类型使用error_t。
-
enum { NO_FILE_POS_VALUES = 3 };
-
int opener(FILE* file, int *width, int *height, int *data_offset) {
-
int file_w;
-
int file_h;
-
int file_o;
-
fpos_t offset;
-
if (file == NULL) { return -1; }
-
if (fgetpos(file, &offset) != 0) { return -1; }
-
if (fscanf(file, "%i %i %i", &file_w, &file_h, &file_o) != NO_FILE_POS_VALUES) { return -1;
-
}
-
if (fsetpos(file, &offset) != 0) { return -1; }
-
*width = file_w;
-
*height = file_h;
-
*data_offset = file_o;
-
return 0;
-
}
使用error_t:
-
#include <errno.h>
-
enum { NO_FILE_POS_VALUES = 3 };
-
errno_t opener(FILE* file, int *width, int *height, int *data_offset) {
-
int file_w;
-
int file_h;
-
int file_o;
-
int rc;
-
fpos_t offset;
-
if (file == NULL) { return EINVAL; }
-
if ((rc = fgetpos(file, &offset)) != 0 ) { return rc; }
-
if (fscanf(file, "%i %i %i", &file_w, &file_h, &file_o) != NO_FILE_POS_VALUES) { return EIO;
-
}
-
if ((rc = fsetpos(file, &offset)) != 0 ) { return rc; }
-
*width = file_w;
-
*height = file_h;
-
*data_offset = file_o;
-
return 0;
-
}
11.小心使用可变参数函数
-
int average(int first, ...) {
-
size_t count = 0;
-
int sum = 0;
-
int i = first;
-
va_list marker;
-
va_start(marker, first);
-
while (i != -1) {
-
sum += i;
-
count++;
-
i = va_arg(marker, int);
-
}
-
va_end(marker);
-
return(count ? (sum / count) : 0);
-
}
当遇到-1时,函数停止处理参数。va_start()用来初始化参数表,va_end()用来完成参数表。va_start()和va_end()是成对出现的。
错误的使用:
-
int avg = average(1, 4, 6, 4, 1)
由于没有-1这个终止参数,导致函数读取stack的值作为参数直到遇到-1或被中断。
12.可变参数函数的参数是没有定义类型,开发人员要确保传递的参数与对应的参数要一致,除了以下情况:
a.传递参数为符号整型,相应的参数为无符号整型,他们之间是可以相互表示的。
b.指向void型的指针,相应的参数类型为字符指针。
类型表示错误:
错误代码,类型不一致:
-
char const *error_msg = "Error occurred";
-
/* ... */
-
printf("%s:%d", 15, error_msg)
正确书写(开发人员要确保参数类型一致):
-
char const *error_msg = "Error occurred";
-
/* ... */
-
printf("%d:%s", 15, error_msg)
类型对齐错误:
-
long long a = 1;
-
char msg[128] = "Default message";
-
/* ... */
-
printf("%d %s", a, msg)
long long类型用%d来解析,导致数据不完整。
正确:
-
long long a = 1;
-
char msg[128] = "Default message";
-
/* ... */
-
printf("%lld %s", a, msg)
阅读(491) | 评论(0) | 转发(0) |