Bug #46573 Memory leak in lf_alloc_new
Submitted: 5 Aug 2009 18:06 Modified: 5 Aug 2009 18:25
Reporter: Marc ALFF Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version: OS:Any
Assigned to: CPU Architecture:Any

[5 Aug 2009 18:06] Marc ALFF
Description:
From code analysis:

void *_lf_alloc_new(LF_PINS *pins)
{
  LF_ALLOCATOR *allocator= (LF_ALLOCATOR *)(pins->pinbox->free_func_arg);
  uchar *node;
  for (;;)
  {
    do
    {

(1)

      node= allocator->top;
      _lf_pin(pins, 0, node);
    } while (node != allocator->top && LF_BACKOFF);
    if (!node)
    {

(2)

      node= (void *)my_malloc(allocator->element_size, MYF(MY_WME));
      if (allocator->constructor)
        allocator->constructor(node);
#ifdef MY_LF_EXTRA_DEBUG
      if (likely(node != 0))
        my_atomic_add32(&allocator->mallocs, 1);
#endif
      break;
    }

(3)

    if (my_atomic_casptr((void **)(char *)&allocator->top,
                         (void *)&node, anext_node(node)))
      break;
  }
  _lf_unpin(pins, 0);
  return node;
}

When the atomic CAS operation from (3) fails, the code loops again in the
for (;;) loop.
In this case, memory allocated in (2) is lost, since 'node' is assigned to
a new value in (1).

This seems to be the root cause of spurious memory leaks such as:

==23899==
==23899== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 3 from 1)
==23899== malloc/free: in use at exit: 48 bytes in 2 blocks.
==23899== malloc/free: 5,483 allocs, 5,481 frees, 66,622,748 bytes allocated.
==23899== For counts of detected errors, rerun with: -v
==23899== searching for pointers to 2 not-freed blocks.
==23899== checked 1,596,840 bytes.
==23899==
==23899== 20 bytes in 1 blocks are definitely lost in loss record 1 of 2
==23899==    at 0x4024D5E: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==23899==    by 0x873C90B: my_malloc (my_malloc.c:37)
==23899==    by 0x874A9D0: _lf_alloc_new (lf_alloc-pin.c:503)
==23899==    by 0x87B2BBB: lf_hash_insert (lf_hash.c:373)
==23899==    by 0x87D43CB: find_or_create_file(PFS_thread*, PFS_file_class*, char const*, unsigned) (pfs_instr.cc:802)
==23899==    by 0x87E0F23: _ZL30get_thread_file_name_locker_v1j18PSI_file_operationPKcPKv (pfs.cc:1297)
==23899==    by 0x82F44BD: _ZL24inline_mysql_file_createjPKcjS0_iii (mysql_file.h:861)
==23899==    by 0x82F470F: _ZL15create_pid_filev (mysqld.cc:9547)
==23899==    by 0x82F91BE: signal_hand (mysqld.cc:2926)
==23899==    by 0x87E138B: pfs_spawn_thread(void*) (pfs.cc:1007)
==23899==    by 0x406A174: start_thread (in /lib/libpthread-2.8.so)
==23899==    by 0x42BFDAD: clone (in /lib/libc-2.8.so)
==23899==
==23899== LEAK SUMMARY:
==23899==    definitely lost: 20 bytes in 1 blocks.
==23899==      possibly lost: 0 bytes in 0 blocks.
==23899==    still reachable: 0 bytes in 0 blocks.
==23899==         suppressed: 28 bytes in 1 blocks.

which was detected in mysql-azalea-perfschema.

How to repeat:
Read the code.

Note that for atomic operations to fail, you need a multi core machine,
when using native atomic operations.

Suggested fix:
Reuse the allocated node instead of allocating a new one in each loop iteration
[5 Aug 2009 18:25] Marc ALFF
The analysis is wrong, since the code exits (break) after (2).
Closing as not a bug.