strdup() memory leak even after free()

2019-04-28 11:40发布

I've never needed to use strdup(stringp) with strsep(&stringp_copy, token) together until recently and I think it was causing a memory leak.

(strdup() has always free'd just fine before.)

I fixed the leak, and I think I understand how, but I just can't figure out why I needed to.

Original code (summarized):

const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *line, *field_name;
int colon_position;
message_copy = strdup(message);

while(line = strsep(&message_copy, "\n")) {
  printf(line);
  char *colon = strchr(line, ':');
  if (colon != NULL) {
    colon_position = colon - line;
    strncpy(field_name, line, colon_position);
    printf("%s\n", field_name);
  }
}

free(message_copy);

New code that doesn't leak:

const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *freeable_message_copy, *line, *field_name;
int colon_position;
freeable_message_copy = message_copy = strdup(message);

while(line = strsep(&message_copy, "\n")) {
  printf(line);
  char *colon = strchr(line, ':');
  if (colon != NULL) {
    colon_position = colon - line;
    strncpy(field_name, line, colon_position);
    printf("%s\n", field_name);
  }
}

free(freeable_message_copy);

How is the message_copy pointer being overwritten in the first code? or is it?

4条回答
聊天终结者
2楼-- · 2019-04-28 11:53

Because strsep() modifies the message_copy argument, you were trying to free a pointer that was not returned by malloc() et al. This would generate complaints from some malloc() libraries, and from valgrind. It is also undefined behaviour, usually leading to crashes in short order (but crashes in code at an inconvenient location unrelated to the code that did the damage).

In fact, your loop iterates until message_copy is set to NULL, so you were freeing NULL, which is defined and safe behaviour, but it is also a no-op. It did not free the pointer allocated via strdup().

Summary:

  • Only free pointers returned by the memory allocators.
  • Do not free pointers into the middle or end of a block returned by the memory allocators.
查看更多
姐就是有狂的资本
3楼-- · 2019-04-28 11:53

From the man page,

...This token is terminated by overwriting the delimiter with a null byte ('\0') and *stringp is updated to point past the token....

查看更多
相关推荐>>
4楼-- · 2019-04-28 11:55

The function strsep() takes a pointer to the original string (message_copy) and modifies it to return a new pointer to the 'next' token

const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *original_copy;
//here you have allocated new memory, a duplicate of message
message_copy = original_copy = strdup(message);

Print out the pointer here,

printf("copy %p, original %p\n", message_copy, original_copy);

Note that as you use strsep(), you are modifying the message_copy,

char* token;
//here you modify message_copy
while(token = strsep(&message_copy, "\n")) {
    printf("%s\n", token);
}

This illustrates the changed message_copy, while original_copy is unchanged,

printf("copy %p, original %p\n", message_copy, original_copy);

Since message_copy does not point to the original strdup() result this would be wrong,

free(message_copy);

But keep around the original strdup() result, and this free works

//original_copy points to the results of strdup
free(original_copy);
查看更多
小情绪 Triste *
5楼-- · 2019-04-28 12:08

Have a read of the strsep man page here.

In short the strsep function will modify the original character pointer that's passed into the function, overwriting each occurrance of the delimiter with a \0, and the original character pointer is then updated to point past the \0.

Your second version doesn't leak, as you created a temporary pointer to point to the start of the original char pointer returned from strdup(), so the memory was freed correctly, as you called free() with the original char pointer instead of the updated one that strsep() had modified.

查看更多
登录 后发表回答