简体   繁体   English

为什么 valgrind 在重用分配的内存时会抱怨大小为 1 的无效读写?

[英]Why would valgrind complain about invalid read and writes of size 1 on reusing allocated memory?

I have written a function to compare version c strings like "1.2.3" , "1.123.9" etc.我编写了一个函数来比较版本 c 字符串,如"1.2.3""1.123.9"等。

int compare_versions(char* first, char* second)
{
  size_t first_last_dot1 = 0;
  size_t first_last_dot2 = strcspn(first, ".");
  size_t second_last_dot1 = 0;
  size_t second_last_dot2 = strcspn(second, ".");

  while(first_last_dot2 || second_last_dot2)
  {
    if(first_last_dot2 && !second_last_dot2) return 1;
    if(!first_last_dot2 && second_last_dot2) return -1;

    char* first_c = (char*)calloc(first_last_dot2 + 1, sizeof(char));
    strncat(first_c, first + first_last_dot1, first_last_dot2);
    int first_n = atoi(first_c);
    first_last_dot1 += first_last_dot2 + 1;
    first_last_dot2 = strcspn(first + first_last_dot1, ".");
    free(first_c);

    char* second_c = (char*)calloc(second_last_dot2 + 1, sizeof(char));
    strncat(second_c, second + second_last_dot1, second_last_dot2);
    int second_n = atoi(second_c);
    second_last_dot1 += second_last_dot2 + 1;
    second_last_dot2 = strcspn(second + second_last_dot1, ".");
    free(second_c);

    if (first_n != second_n)
    {
      if(first_n < second_n) return -1;
      return 1;
    }
  }

  return 0;
}

and valgrind wouldn't complain at all for my 'tests':并且 valgrind 根本不会抱怨我的“测试”:

assert(0 == compare_versions("1.12345678.2", "1.12345678.2"));

valgrind output: valgrind 输出:

==24002== Memcheck, a memory error detector
==24002== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==24002== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==24002== Command: ./a.out
==24002== 
==24002== 
==24002== HEAP SUMMARY:
==24002==     in use at exit: 0 bytes in 0 blocks
==24002==   total heap usage: 10 allocs, 10 frees, 48 bytes allocated
==24002== 
==24002== All heap blocks were freed -- no leaks are possible
==24002== 
==24002== For counts of detected and suppressed errors, rerun with: -v
==24002== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

Now I wanted to reuse the allocated memory, namely: when I allocate a memory block of size 4 I can reuse it for all c strings of size up to 4 ( including the terminating null character).现在我想重用分配的内存,即:当我分配一个大小为 4 的内存块时,我可以将它重用于所有大小为 4 的 c 字符串(包括终止空字符)。

So I wrote this version of the very same function:所以我写了这个版本的完全相同的函数:

int compare_versions(char* first, char* second)
{
  size_t first_last_dot1 = 0;
  size_t first_last_dot2 = strcspn(first, ".");
  size_t second_last_dot1 = 0;
  size_t second_last_dot2 = strcspn(second, ".");

  char* first_c = (char*)calloc(first_last_dot2 + 1, sizeof(char));
  char* second_c = (char*)calloc(second_last_dot2 + 1, sizeof(char));
  int first_max = first_last_dot2;
  int second_max = second_last_dot2;

  while(first_last_dot2 || second_last_dot2)
  {
    // first longer than second ( different only by last segment )
    if(first_last_dot2 && !second_last_dot2) {
      free(first_c);
      free(second_c);
      return 1;
    }
    // second longer than first ( different only by last segment )
    if(!first_last_dot2 && second_last_dot2) {
      free(first_c);
      free(second_c);
      return -1;
    }

    if(first_last_dot2 > first_max) {
      first_max = first_last_dot2;
      first_c = (char*)realloc(first_c, first_last_dot2 + 1);
      memset(first_c, 0, first_last_dot2);
    }
    strncat(first_c, first + first_last_dot1, first_last_dot2);
    int first_n = atoi(first_c);
    first_last_dot1 += first_last_dot2 + 1;
    first_last_dot2 = strcspn(first + first_last_dot1, ".");

    if(second_last_dot2 > second_max) {
      second_max = second_last_dot2;
      second_c = (char*)realloc(second_c, second_last_dot2 + 1);
      memset(second_c, 0, second_last_dot2);
    }
    strncat(second_c, second + second_last_dot1, second_last_dot2);
    int second_n = atoi(second_c);
    second_last_dot1 += second_last_dot2 + 1;
    second_last_dot2 = strcspn(second + second_last_dot1, ".");

    if (first_n != second_n)
    {
      free(first_c);
      free(second_c);
      if(first_n < second_n) return -1;
      return 1;
    }
  }

  free(first_c);
  free(second_c);
  return 0;                                                                                                                                                                                                                                                                       
}

but then I will get the following output from valgrind:但随后我将从 valgrind 获得以下输出:

==24039== Memcheck, a memory error detector
==24039== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==24039== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==24039== Command: ./a.out
==24039== 
==24039== Invalid write of size 1
==24039==    at 0x4A07E83: strncat (mc_replace_strmem.c:304)
==24039==    by 0x400828: compare_versions (main.c:46)
==24039==    by 0x400986: main (main.c:94)
==24039==  Address 0x4c330e9 is 0 bytes after a block of size 9 alloc'd
==24039==    at 0x4A06C20: realloc (vg_replace_malloc.c:662)
==24039==    by 0x4007F1: compare_versions (main.c:43)
==24039==    by 0x400986: main (main.c:94)
==24039== 
==24039== Invalid read of size 1
==24039==    at 0x39AA436FF3: ____strtol_l_internal (strtol_l.c:438)
==24039==    by 0x39AA433C5F: atoi (atoi.c:28)
==24039==    by 0x400834: compare_versions (main.c:47)
==24039==    by 0x400986: main (main.c:94)
==24039==  Address 0x4c330e9 is 0 bytes after a block of size 9 alloc'd
==24039==    at 0x4A06C20: realloc (vg_replace_malloc.c:662)
==24039==    by 0x4007F1: compare_versions (main.c:43)
==24039==    by 0x400986: main (main.c:94)
==24039== 
...
==24039== 
==24039== HEAP SUMMARY:
==24039==     in use at exit: 0 bytes in 0 blocks
==24039==   total heap usage: 4 allocs, 4 frees, 22 bytes allocated
==24039== 
==24039== All heap blocks were freed -- no leaks are possible
==24039== 
==24039== For counts of detected and suppressed errors, rerun with: -v
==24039== ERROR SUMMARY: 34 errors from 8 contexts (suppressed: 6 from 6)

What is the problem here?这里有什么问题?

Valgrind says it all - I'll just look at the initial error: Valgrind 说明了一切 - 我只看初始错误:

==24039== Invalid write of size 1
==24039==    at 0x4A07E83: strncat (mc_replace_strmem.c:304)
==24039==    by 0x400828: compare_versions (main.c:46)
==24039==    by 0x400986: main (main.c:94)
==24039==  Address 0x4c330e9 is 0 bytes after a block of size 9 alloc'd
==24039==    at 0x4A06C20: realloc (vg_replace_malloc.c:662)
==24039==    by 0x4007F1: compare_versions (main.c:43)
==24039==    by 0x400986: main (main.c:94)

The line compare_versions (main.c:46) is:compare_versions (main.c:46)是:

    strncat(first_c, first + first_last_dot1, first_last_dot2);

The line compare_versions (main.c:43) is:compare_versions (main.c:43)是:

      first_c = (char*)realloc(first_c, first_last_dot2 + 1);

The error occurs in the third loop cycle.错误发生在第三个循环周期。 first_last_dot2 has been set in the second loop cycle in the line first_last_dot2first_last_dot2中的第二个循环循环中设置

    first_last_dot2 = strcspn(first + first_last_dot1, ".");

to the value 1 (the length of the version string's last part, which is 2 );到值1 (版本字符串的最后一部分的长度,即2 ); previously first_last_dot2 had been set in the second loop cycle to the value 8 (the length of the version string's middle part, which is 12345678 ), and so the realloc allocated size 8 + 1 = 9 .之前first_last_dot2已在第二个循环周期中设置为值8 (版本字符串中间部分的长度,即12345678 ),因此realloc分配的大小为8 + 1 = 9 Since now the if condition (first_last_dot2 > first_max) is not true, the realloc as well as the由于现在if条件(first_last_dot2 > first_max)不为真, realloc以及

      memset(first_c, 0, first_last_dot2);

are skipped;被跳过; because of not executing the latter, first_c still contains 12345678 and the strncat appends 2 to it, writing the terminating null byte to first_c [9], which is 0 bytes after a block of size 9 alloc'd .由于没有执行后者, first_c仍然包含12345678并且strncat2附加到它,将终止空字节写入first_c [9],这是大小为 9 alloc'd 的块之后的 0 个字节
If you move the two memset s (also the one for second_c ) after the if blocks, there's no error.如果在if块之后移动两个memset (也是second_c的一个),则没有错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM