Valgrind无效写入大小为8

时间:2016-01-26 20:35:40

标签: c memory valgrind

我有一些代码可能会破坏看起来像下面示例的字符串并将其存储到数据结构中。

ORGANIZER;CN=John Doe;ON=Another Person;SN=Maybe another

以下是我的功能:

CalError parseOptionalParam(char * paramString, CalParam * param) {
  char * parseString = malloc(strlen(paramString) + sizeof(char*));
  strcpy(parseString, paramString);

  char * tokenSemi;
  tokenSemi = strtok(parseString, ";");
  if(tokenSemi == NULL) return SYNTAX;
  int i = 0;
  while(tokenSemi != NULL) {
    tokenSemi = strtok(NULL, ";");
    if(tokenSemi == NULL) return SYNTAX;

    char * tokenEqual = strtok(tokenSemi, "=");
    param->name = malloc(strlen(tokenEqual) + sizeof(char*));
    strcpy(param->name, tokenEqual);

    param = realloc(param, sizeof(param) + sizeof(char*));
    tokenEqual = strtok(tokenSemi, "=");
    param->value[i] = malloc(sizeof(char*) + strlen(tokenEqual));
    strcpy(param->value[i], tokenEqual);

    i++;
  }
  free(parseString);
  return OK;
}

以下是valgrind告诉我的事情:

==7925== Invalid write of size 8
==7925==    at 0x400E56: parseOptionalParam (calutil.c:79)
==7925==    by 0x400CED: parseCalProp (calutil.c:50)
==7925==    by 0x400B0B: main (testfile.c:8)
==7925==  Address 0x5202508 is 8 bytes after a block of size 16 alloc'd
==7925==    at 0x4C2DD9F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7925==    by 0x400E13: parseOptionalParam (calutil.c:77)
==7925==    by 0x400CED: parseCalProp (calutil.c:50)
==7925==    by 0x400B0B: main (testfile.c:8)

第79行是以param->value[i] =开头的行,然后valgrind指的是上面2行的realloc。我很困惑这里有什么问题?在param结构中,在结构的末尾有一个灵活的数组成员value。我只是试图在结构中分配另一个数组位置,然后将该位置用于字符串。

在这两行中的某些时候,我猜我在做一些关于记忆的错误,但我不确定它到底是什么。

2 个答案:

答案 0 :(得分:4)

  

在param结构中,在结构的末尾有一个灵活的数组成员value。我只是试图在结构中分配另一个数组位置,然后将该位置用于字符串。

让我们进行数学运算:为CalParam分配足够的空间,使其灵活的数组成员具有您需要的i个元素

  • sizeof(CalParam) - 这是struct
  • 的基本尺寸
  • (i+1)*sizeof(char*) - 这是char*大小为i+1
  • 的数组

因此,您的realloc电话应如下所示:

param = realloc(param, sizeof(*param) + (i+1)*sizeof(char*));

请注意param前面的星号。这很重要,因为param是指针。

然而,这不是故事的结尾,因为param是按值传递的,而您正在通过realloc更改其值。这将导致调用者中的悬空引用。为了解决这个问题,你需要通过指针接收param指针(即双星号指针),并在realloc的调用中分配它,如下所示:

CalError parseOptionalParam(char * paramString, CalParam **param) {
    ...
    *param = realloc(*param, sizeof(**param) + (i+1)*sizeof(char*));
    ...
}

请注意那里有更多的星号。为了使您的代码更易于阅读,请考虑将第一个sizeof替换为sizeof(CalParam),如下所示:

*param = realloc(*param, sizeof(CalParam) + (i+1)*sizeof(char*));

最后,您还应该使用realloc修复潜在的内存泄漏:您应该直接分配给*param,而应分配给NULL,然后分配返回*paramfree旧值并报告错误。

答案 1 :(得分:2)

我假设CalParam的以下定义。

typedef struct {
    char* name;
    char* value[];
} CalParam;

sizeof(param)sizeof(typeof(param))相同,与sizeof(CalParam*)相同,后者是指针的大小。你可能意味着sizeof(*param)

sizeof(*param)sizeof(typeof(*param))相同,与sizeof(CalParam)相同。类型是常量,因此sizeof在编译时是已知的,因此必须在计数中忽略灵活的数组。

因此,

param = realloc(param, sizeof(param) + sizeof(char*));

应该是

param = realloc(param, sizeof(CalParam) + sizeof(char*)*(i+1));

你的其他内存分配也很糟糕。在三个地方,你有类似

的东西
char * dst = malloc(strlen(src) + sizeof(char*));
strcpy(dst, src);

那应该是

char * dst = malloc((strlen(src) + 1) * sizeof(char));
strcpy(dst, src);

但当然,sizeof(char)1,所以我们可以使用

char * dst = malloc(strlen(src) + 1);
strcpy(dst, src);

但是strdup做同样的事情。

char * dst = strdup(src);