【问题标题】:glibc corrupted double linked list error while running a C code on 64 bit machine在 64 位机器上运行 C 代码时 glibc 损坏的双链表错误
【发布时间】:2014-03-19 16:48:39
【问题描述】:

我有一个大型 C 代码,在 32 位机器上运行良好,但在 64 位机器上却失败了

*** glibc detected *** ./XXX.exe: corrupted double-linked list: 错误。

我已经运行 Valgrind 并使用 print 语句来准确查明错误的来源,但对修复一无所知。 实际代码非常大,但我在这里给出了相关部分。希望有人可以帮我解决它。 实际错误来自Buf_Close() 模块,它试图将Buf_elm_p p 释放为if (p != NULL) free(p);

这些是主代码调用的函数,我在这里只给出这个,因为错误就在这里。主代码的调用顺序是:

1. Buf_Init 2. Buf_New_p 3. Buf_Close

Buf_bf_p 
Buf_Init( size_t elm_size, 
      int    nelm_buf, 
      long   nbuf )
/*******************************************************************************
!C
!Description: Buf_Init (buffer initialize) initializes buffer data structures 
 and buffers.

!Input Parameters:
 elm_size   element size (bytes)
 nelm_buf   number of elements per buffer
 nbuf       number of buffers

!Output Parameters:
 (returns)      pointer to the buffer data structure 
                or NULL for an error return
*****************************************************************************/
{
  Buf_bf_p bf;
  long buf_size;
  long ibuf;

  /* Calculate buffer size and check */
  buf_size = ((long) elm_size) * nelm_buf;

  /*Allocate the buffer data structure */
  if ((bf = (Buf_bf_p) malloc(sizeof(Buf_bf_t))) == NULL)
    {Buf_Error(&BUF_NOMEMORY, "Init");  return NULL;}
  bf->key = BUF_KEY;
  bf->elm_size = elm_size;
  bf->nelm_buf = nelm_buf;
  bf->nbuf = nbuf;
  bf->buf_size = buf_size;
  bf->fp = NULL;
  bf->access = NO_FILE;
  bf->nbuf_alloc = 1;
  bf->ibuf_end = 0;
  bf->ibuf_newest = 0;
  bf->ibuf_oldest = 0;
  bf->nelm = 0;

  /* Allocate the buffer status data structure */
  bf->nbstat = max(NBSTAT_START, bf->nbuf + 1);
  if ((bf->bstat = (Buf_bstat_t *) 
     malloc(bf->nbstat * sizeof(Buf_bstat_t))) == NULL)
    {Buf_Error(&BUF_NOMEMORY, "Init");  return NULL;}

  /* Allocate the first buffer */
  bf->bstat[0].loc = MEM_ONLY;
  if( (bf->bstat[0].buf_p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
  { Buf_Error(&BUF_NOMEMORY, "Init");  
    return NULL;
  }
  else
  {
     /* initialize */
     memset( bf->bstat[0].buf_p, '\0', bf->buf_size ); 
  }

  bf->bstat[0].newer = -1;
  bf->bstat[0].older = -1;

  /* Initialize the rest of the buffer status array */
  printf("bf->nbstat %d\n", bf->nbstat);
  for (ibuf = 1; ibuf < bf->nbstat; ibuf++) {
    bf->bstat[ibuf].loc = NOT_ALLOC;
    bf->bstat[ibuf].buf_p = NULL;
    bf->bstat[ibuf].newer = -1;
    bf->bstat[ibuf].older = -1;
    bf->bstat[ibuf].initialized = 1;
  }

  return bf;
}

Buf_elm_p 
Buf_New_p( Buf_bf_p bf, 
           long *ielm )
/*******************************************************************************
!C
!Description: Buf_New_p (new buffer element pointer) returns a memory 
 location and element number of a new element; elements are number
 sequentially as they are allocated.

!Input Parameters:
 bf     pointer to the buffer data structure 

!Output Parameters:
 ielm       new element number
 (returns)      pointer to memory location of new element 
                or NULL for error
!Notes:
   1. 'Buf_Init' must be called before this routine to initialize 
      the buffer data structure.
   2. If there is no more space in memory and disk write access is allowed, 
      the oldest buffer is written to disk and the memory is re-used.
   3. If the file is opened with 'read only' access this routine will return 
      an error.

!END
******************************************************************************/
{
  long ibuf, jelm, jbuf, kbuf;
  long nbuf_cmplt;
  Buf_elm_p p;
  long dsk_loc, eof_loc;


  /* New element number/location */
  *ielm = bf->nelm++;
  ibuf = *ielm / bf->nelm_buf;
  jelm = *ielm % bf->nelm_buf;

  /* Are we at the past the end of the last buffer? */
  if (ibuf > bf->ibuf_end) {

    if (ibuf != (bf->ibuf_end + 1))
      {Buf_Error(&BUF_BADBUF, "New_p");  return NULL;}

    /* Re-allocate buffer status data structure if not large enough */
    if( ibuf >= bf->nbstat ) 
    {
       bf->nbstat += min(bf->nbstat, MAX_NEW_NBSTAT);
       if( (bf->bstat = realloc(bf->bstat, bf->nbstat * sizeof(Buf_bstat_t))) 
           == NULL)
      {  Buf_Error(&BUF_NOMEMORY, "New_p");  
          return NULL;
      }
    }

    if (bf->nbuf_alloc < bf->nbuf  ||  bf->access == NO_FILE) {

      /* Allocate a new buffer */
      if( (p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
      {  Buf_Error(&BUF_NOMEMORY, "New_p");  
         return NULL;
      }
      else
      {
         /* initialize */
         memset( p, '\0', bf->buf_size ); 
      }
      bf->nbuf_alloc++;
      if (bf->nbuf < bf->nbuf_alloc) bf->nbuf = bf->nbuf_alloc;

    } else {

      /* Re-use an existing buffer */

      /* Get the oldest buffer */
      jbuf = bf->ibuf_oldest;
      /* Delete oldest buffer from old/new pointer list */
      p = bf->bstat[jbuf].buf_p;
      bf->ibuf_oldest = bf->bstat[jbuf].newer;
      bf->bstat[bf->ibuf_oldest].older = -1;

      bf->bstat[jbuf].buf_p = NULL;
      bf->bstat[jbuf].older = -1;
      bf->bstat[jbuf].newer = -1;
      bf->bstat[jbuf].initialized = 1;

    }

    /* Put current buffer in old/new pointer list */
    bf->bstat[ibuf].loc = MEM_ONLY;
    bf->bstat[ibuf].buf_p = p;
    bf->bstat[ibuf].older = bf->ibuf_newest;
    bf->bstat[ibuf].newer = -1;
    bf->bstat[ibuf].initialized = 1;

    bf->ibuf_end = ibuf;
    bf->bstat[bf->ibuf_newest].newer = ibuf;
    bf->ibuf_newest = ibuf;

  }

  /* Calculate pointer to memory location of element */
  p = (unsigned char *) bf->bstat[ibuf].buf_p + (jelm * bf->elm_size);

  return p;
}


int 
Buf_Close( Buf_bf_p bf )
/*******************************************************************************
!C
!Description: Buf_Close (buffer cache file close) writes the remainder of the 
 cache to the disk cache file and closes the file and frees memory of the 
 buffer data structure and buffers.

!Input Parameters:
 bf     pointer to the buffer data structure 

 Notes:
   1. 'Buf_Create' or 'Buf_Open' must be called before this routine to open
      the file.

!END
*****************************************************************************/
{
  int i;
  long dsk_loc;
  logical_t cmplt_flag;
  /* int b; */
  Buf_elm_p p;
  long ibuf, nelm_wrt;
  int  nb;
  unsigned char header[HEADER_SIZE];

  /* Write remaining buffers which are still only in memory */
  for (ibuf = 0; ibuf < (bf->ibuf_end + 1); ibuf++)
  /* for (ibuf = 0; ibuf < (bf->ibuf_end); ibuf++)*/{
    p = bf->bstat[ibuf].buf_p;
    /* Free the buffer memory */
**THIS FOLLOWING LINE IS WHERE THE ERROR IS COMING FROM**

**VALGRIND SHOWS `Conditional jump or move depends on uninitialised value(s)` ERROR**

**BUT AM NOT SURE HOW `p` is coming out to be uninitialized`**

if (p != NULL) free(p);  
}

  /* Free the buffer status memory */
  free(bf->bstat);

  /* Free the buffer cache data structure */
  bf->fp = (FILE *)NULL;
  bf->key = 0;
  free(bf);  

  printf("buf here 5\n");
  return BUF_NORMAL;
}

【问题讨论】:

  • 如果它在 32 位机器上运行良好而不在 64 位机器上运行良好,则问题可能出在您的代码中,当然不在 glibc 中。您是否在 32 位上使用 valgrind 运行它?或者你在某个地方的指针运算有问题(假设指针是 32 位宽)?
  • 是的,我知道问题出在它本身的某个地方,而不是在 glibc 中。这段代码最初是为 32 位机器设计的,我的感觉是可能需要进行一些 64 位特定的更改,我不确定。

标签: c linked-list 64-bit glibc memory-corruption


【解决方案1】:

我从事的项目有很多有问题的做法(目前正在努力清理它们)。我遇到了这个错误,并通过了我能想象到的一切来找出问题所在,包括 clang sanitizers、各种 valgrind 工具和各种其他技巧。

问题:在main() 返回的同时,exit() 在一个线程中被调用,因此所有全局/静态构造函数同时在两个单独的线程中启动。我真的有点生气,我没有早点建立联系。

此错误还表现为:

  • double free or corruption
  • segfault/sig11 内 exit()
  • malloc_consolidate 内部崩溃,调用堆栈如下所示:

我猜你不能在项目符号后添加代码示例

#0  0xabcdabcd in malloc_consolidate () from /lib/libc.so.6
#1  0xabcdabcd in _int_free () from /lib/libc.so.6
#2  0xabcdabcd in operator delete (...)
#3  0xabcdabcd in operator delete[] (...)
(...)

此外,在 valgrind 下运行时,我无法让它显示这个问题——无论是时间问题,还是 valgrind 工作原理的一些人工制品隐藏了我可能永远不知道的问题。

【讨论】:

    【解决方案2】:

    通过静态分析来理解你的程序逻辑有点困难。但是 Valgrind 在场景下打印“条件跳转或移动取决于未初始化的值” 程序试图以可能影响程序的外部可见行为的方式使用未初始化的数据。 未初始化数据的来源往往是:

    1. 过程中的局部变量尚未初始化。
    2. 在您(或构造函数)在那里写东西之前堆块的内容(使用 malloc、new 或类似函数分配)。

    要查看有关程序中未初始化数据来源的信息,您可以使用 --track-origins=yes 选项。所以你可以按如下方式运行程序(./a.out):

    valgrind --tool=memcheck --track-origins=yes ./a.out

    这可能会有所帮助,并提供更接近您问题的实际来源的有用信息。您可以从该位置找到有关它的更多详细信息:

    http://valgrind.org/docs/manual/mc-manual.html

    【讨论】:

    • 您也可以在 valgrind 报告错误的模块中使用 calloc() 更改 malloc() 并可能验证一次。尽管大多数地方 memset() 已经在 malloc() 之后被调用,但是可能在某些极端情况下 memset() 没有被调用,内容可能未初始化,这可能会导致这种情况。
    • 我确实使用--track-origin=yes 运行了 Valgrind,但该输出仍然不是很有帮助。另外在这里使用calloc 而不是malloc 会有什么帮助?这是指针以某种方式保持未初始化的问题。无论您使用 malloc 还是 calloc,指针都应该至少使用正确的地址初始化到新分配的空间。使用 calloc 只会用零填充所有分配的空间。
    • 如果您的逻辑是分配内存,然后将(基地址)存储到堆上的另一个内存中。现在,如果您的第一次分配没有发生并且另一个内存仍然存在并尝试释放该内存,则可能会遇到此问题。因此,如果您使用 calloc() ,那么即使您不执行第一次分配调用,您也会很安全,因为在使用第二个堆内存上的存储地址释放内存时 !null 检查会失败。跨度>
    • 您能否通过一个小例子详细说明一下。也许我不知道 calloc 和 malloc 之间的这种区别,也许这就是我的答案。
    • @srsci,我已经用适当的 cmets 举例说明了这一点。您可以从位置github.com/mantosh4u/mkrdscpp/blob/master/stackoverflow/… 查看代码
    猜你喜欢
    • 1970-01-01
    • 2013-12-05
    • 2013-10-05
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 2013-12-04
    • 2015-06-20
    • 1970-01-01
    相关资源
    最近更新 更多