从写代码上看,内存管理包括内存分配、内存读写、内存释放。内存管理设计不当,会导致堆缓冲溢出、悬空指针(指向一块已经删除了的内存的指针)、double free等问题。
1.MALLOC和RECALLOC内存分配不会初始化内存数据
调用malloc函数成功后,其分配的内存的数据没有初始化,未知数据。calloc函数调用后,新分配的内存空间也是没有初始化,其行为和malloc一样。因此调用上述函数后,最好初始化内存数据。
上述问题不会影响calloc函数,它调用后就会把内存数据初始化为0x00.
问题代码:
-
char *buf = malloc(MAX_BUF_SIZE);
-
if (buf == NULL) {
-
/* Handle Allocation Error */
-
}
-
strcpy(buf, str);
-
/* process buf */
-
free(buf)
若str的长度小于MAX_BUF_SIZE,buf里的数据就是str+未初始化的位置数据。
正确代码:进行初始化操作
-
char *buf = malloc(MAX_BUF_SIZE);
-
if (buf == NULL) {
-
/* Handle Allocation Error */
-
}
-
memset(buf,'\0', MAX_BUF_SIZE); /* Initialize memory to default value */
-
strcpy(buf, str);
-
/* process buf */
-
free(buf)
2.内存资源的分配操作和释放操作最好在同一代码块或同一抽象层内
分配和释放在不同的模块或抽象层内,会加大程序员追踪内存块生命周期的负担。若某个内存块已经分配或已经释放的话,会导致double-free、访问已释放内存资源、写未分配的内存资源等问题。
错误代码:
-
int verify_size(char *list, size_t list_size) {
-
if (size < MIN_SIZE_ALLOWED) {
-
/* Handle Error Condition */
-
free(list);
-
return -1;
-
}
-
return 0;
-
}
-
void process_list(size_t number) {
-
char *list = malloc(number);
-
if (list == NULL) {
-
/* Handle Allocation Error */
-
}
-
if (verify_size(list, number) == -1) {
-
/* Handle Error */
-
}
-
/* Continue Processing list */
-
free(list);
-
}
上面代码可能会产生double-free问题。
正确代码:
-
int verify_size(char *list, size_t list_size) {
-
if (size < MIN_SIZE_ALLOWED) {
-
/* Handle Error Condition */
-
return -1;
-
}
-
return 0;
-
}
-
void process_list(size_t number) {
-
char *list = malloc(number);
-
if (list == NULL) {
-
/* Handle Allocation Error */
-
}
-
if (verify_size(list, number) == -1) {
-
/* Handle Error */
-
}
-
/* Continue Processing list */
-
free(list);
-
}
3.free(P)后,记得设置P=NULL。因为free(p)后p的值还是等于该内存块地址,这样会导致double-freee等一系列问题。
错误代码:指针message可能存在double-free的问题
-
if (message_type == value_1) {
-
/* Process message type 1 */
-
free(message);
-
}
-
/* ...*/
-
if (message_type == value_2) {
-
/* Process message type 2 */
-
free(message);
-
}
正确代码:free(NULL)是允许的。
-
if (message_type == value_1) {
-
/* Process message type 1 */
-
free(message);
-
message = NULL;
-
}
-
/* ...*/
-
if (message_type == value_2) {
-
/* Process message type 2 */
-
free(message);
-
message = NULL;
-
}
四.申请分配0字节的内存空间其结果依赖于实现。
C99规定若申请的大小为0,其行为依赖于实现;可能是返回值为NULL,也有可能返回值为非零值,其值不能用来访问对象。
1.MALLOC函数
错误代码:
-
list = malloc(size);
-
if (list == NULL) {
-
/* Handle Allocation Error */
-
}
-
/* Continue Processing list */
若size等于0,list的值不等于NULL,会导致堆溢出错误。
正确代码:
-
if (size <= 0) {
-
/* Handle Error */
-
}
-
list = malloc(size);
-
if (list == NULL) {
-
/* Handle Allocation Error */
-
}
-
/* Continue Processing list */
2.Realloc函数
若申请新内存区域成功后,会释放原始内存区域。若释放原始内存失败,realloc返回值为NULL。若realloc的size参数为0,realloc会返回一个NULL指针。
错误代码:
-
char *p2;
-
char *p = malloc(100);
-
/* ... */
-
if ((p2 = realloc(p, nsize)) == NULL) {
-
free(p);
-
p = NULL;
-
return NULL;
-
}
-
p = p2
若nsize等于0,realloc申请成功会释放p的资源,然后返回一个NULL指针,然后再free(p)会导致double-free的错误。
正确代码:
-
char *p2;
-
char *p = malloc(100);
-
/* ... */
-
if ( (nsize == 0) || (p2 = realloc(p, nsize)) == NULL) {
-
free(p);
-
p = NULL;
-
return NULL;
-
}
-
p = p2
五.别使用自定义函数来作为内分分配函数的参数
若自定义函数返回的值过大或为0,将导致程序行为无法预测或堆溢出等。
错误代码:
-
#include <stdlib.h>
-
#include <stdio.h>
-
#define MAXLINE 1000
-
size_t calc() {
-
char line[MAXLINE], c;
-
size_t size = 0;
-
while ( (c = getchar()) != EOF && c != '\n') {
-
line[size] = c;
-
size++;
-
if (size >= MAXLINE)
-
break;
-
}
-
return size;
-
}
-
int main(void) {
-
char * line = malloc(calc());
-
if (NULL != line){
-
memcpy(line, xxxx, ....);
-
}
-
}
若calc()函数返回0,line不会NULL,会导致堆溢出错误。
正确代码:
-
#include <stdlib.h>
-
#include <stdio.h>
-
#define MAXLINE 1000
-
size_t calc() {
-
char line[MAXLINE], c;
-
size_t size = 0;
-
while ( (c = getchar()) != EOF && c != '\n') {
-
line[size] = c;
-
size++;
-
if (size >= MAXLINE)
-
break;
-
}
-
return size;
-
}
-
int main(void) {
-
size_t size = calc();
-
if (size > 0) {
-
char * line = malloc(size)
-
if (NULL != line){
-
memcpy(line, xxxx, ....);
-
}
-
}
-
}
六.确保calloc函数的size参数不会发生整型溢出
-
void calloc(size_t nmemb, size_t size)
确保nmemb*size其大小范围在size_t内,否则会导致溢出。
阅读(799) | 评论(0) | 转发(0) |