C语言安全编码(4)
这是安全编码的第4篇。
1.非NULL结尾的字符序列禁止传递给库函数
例1.1:
1// 例1.1
2void func()
3{
4 char str[] = "xyz";
5 printf("%s\n", str);
6}
1void func()
2{
3 char str[] = "xyz"; // 不指定数组大小
4 printf("%s\n", str);
5}
例1.2:
1// 例1.2
2wchar_t *msg = NULL;
3size_t msg_size = 1024;
4void lesson_memory()
5{
6 wchar_t *temp;
7 size_t temp_size;
8 /* .... */
9 if(msg != NULL) {
10 temp_size = msg_size / 2 + 1;
11 temp = realloc(msg, temp_size * sizeof(wchar_t));
12 if(temp == NULL) {
13 /* error */
14 }
15 msg = temp;
16 msg_size = temp_size;
17 }
18}
1wchar_t *msg = NULL;
2size_t msg_size = 1024;
3void lesson_memory()
4{
5 wchar_t *temp;
6 size_t temp_size;
7 /* .... */
8 if(msg != NULL) {
9 temp_size = msg_size / 2 + 1;
10 temp = realloc(msg, temp_size * sizeof(wchar_t));
11 if(temp == NULL) {
12 /* error */
13 }
14 msg = temp;
15 msg[temp_size - 1] = L'\0'; //保证字符串总是以NULL结尾
16 msg_size = temp_size;
17 }
18}
例1.3:
1// 例1.3
2#define STR_SIZE 64
3size_t func(const char *src)
4{
5 char str[STR_SIZE];
6 size_t ret = 0;
7 if(src) {
8 str[sizeof(str) - 1] = '\0';
9 strncpy(str, src, sizeof(str));
10 ret = strlen(str);
11 }
12 return ret;
13}
1#define STR_SIZE 64
2size_t func(const char *src)
3{
4 char str[STR_SIZE];
5 size_t ret = 0;
6 if(src) { // 截断字符串
7 strncpy(str, src, sizeof(str) - 1);
8 str[sizeof(str) - 1] = '\0';
9 ret = strlen(str);
10 }
11 return ret;
12}
或者:
1#define STR_SIZE 64
2size_t func(const char *src)
3{
4 char str[STR_SIZE];
5 size_t ret = 0;
6 if(src) { // 截断字符串 strncpy_s()
7 // strncpy_s()最多拷贝n个字符,如果没有NULL结尾,则在第n位自动被设置为NULL
8 errno_t err = strncpy_s(str, sizeof(str), src, strlen(src));
9 if(err == 0) {
10 ret = strlen_a(str, sizeof(str));
11 }
12 }
13 return ret;
14}
或者:
1#define STR_SIZE 64
2size_t func(const char *src)
3{
4 char str[STR_SIZE];
5 size_t ret = 0;
6 if(src) { // 不截断复制
7 if(strlen(src) < sizeof(str)) {
8 strcpy(str, src);
9 ret = strlen(str);
10 }
11 }
12 return ret;
13}
2.正确判断宽字符串的长度
例2.1:
1// 例2.1-1
2void func()
3{
4 wchar_t wstr1[] = L"0123456789";
5 wchar_t wstr2[] = L"0000000000";
6 strncpy(wstr1, wstr2, 10);
7}
1// 例2.1-2
2void func()
3{
4 char nstr1[] = "0123456789";
5 char nstr2[] = "0000000000";
6 wcsncpy(nstr1, nstr2, 10);
7}
1void func()
2{
3 wchar_t wstr1[] = L"0123456789";
4 wchar_t wstr2[] = L"0000000000";
5 wcsncpy(wstr1, wstr2, 10); // wcsncpy处理宽字符串
6
7 char nstr1[] = "0123456789";
8 char nstr2[] = "0000000000";
9 strncpy(nstr1, nstr2, 10); // strncpy处理窄字符串
10}
例2.2:
1// 例2.2-1
2void func()
3{
4 wchar_t wstr1[] = L"0123456789";
5 wchar_t *wstr2[] = (wchar_t *)malloc(strlen(wstr1) + 1);
6 if(wstr2 == NULL) {
7 /* error */
8 }
9 free(wstr2);
10 wstr2 = NULL;
11}
1// 例2.2-2
2void func()
3{
4 wchar_t wstr1[] = L"0123456789";
5 wchar_t *wstr2[] = (wchar_t *)malloc(wcslen(wstr1) + 1);
6 if(wstr2 == NULL) {
7 /* error */
8 }
9 free(wstr2);
10 wstr2 = NULL;
11}
1void func()
2{
3 wchar_t wstr1[] = L"0123456789";
4 wchar_t *wstr2[] = (wchar_t *)malloc((wcslen(wstr1) + 1) * sizeof(wchar_t));
5 if(wstr2 == NULL) {
6 /* error */
7 }
8 free(wstr2);
9 wstr2 = NULL;
10}
释放非动态分配的内存可能导致严重的错误,有些编译器甚至会使程序异常终止,因此避免对不是由动态内存分配函数(malloc()、calloc()、realloc()等)所返回的指针调用free()。给free()传递空指针不会用任何动作发生。
1// 例3.1
2#define MAX_SIZE 1024
3int main(int argc, char *argv[])
4{
5 char *str = NULL;
6 unsigned int len;
7 if(argc == 2) {
8 len = strlen(argv[1]) + 1;
9 if(len > MAX_SIZE) {
10 /* error */
11 }
12 str = (char *)malloc(len);
13 if(str == NULL) {
14 /* error */
15 }
16 strcpy(str, argv[1]);
17 } else {
18 str = "string";
19 printf("%s", str);
20 }
21 free(str);
22 return 0;
23}
在这段代码中,若argc的值不等于2,则str是一个字符串常量,不是动态分配的内存,传入free()可能导致严重的错误。
对于上面的情况,合适的处理方法如下:
1#define MAX_SIZE 1024
2int main(int argc, char *argv[])
3{
4 char *str = NULL;
5 unsigned int len;
6 if(argc == 2) {
7 len = strlen(argv[1]) + 1;
8 if(len > MAX_SIZE) {
9 /* error */
10 }
11 str = (char *)malloc(len);
12 if(str == NULL) {
13 /* error */
14 }
15 strcpy(str, argv[1]);
16 } else {
17 printf("string");
18 return EXIT_FALURE;
19 }
20 free(str);
21 return 0;
22}