Skip to content

Fix cac::ipToAEngine memory leak and dangling thread

Heinz Junkes requested to merge github/fork/hdante/3.16 into 3.16

Created by: hdante

The attribute ipToAEngine was previously defined as a reference type, so it was not destroyed by the cac object destructor. This resulted in a memory leak. Also, the helper thread instantiated by ipToAEngine continued running, possibly trying to call callbacks from the destroyed cac object. This eventually resulted in an abort in the case of a msgForMultiplyDefinedPV object also being instantiated, as shown in the following execution of "caget":

sol5-linux:~ $ valgrind caget SOL5:MEDIPIX:SizeX
==8163== Memcheck, a memory error detector
==8163== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==8163== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==8163== Command: caget SOL5:MEDIPIX:SizeX
==8163==
SOL5:MEDIPIX:SizeX             1
==8163== Thread 2:
==8163== Invalid read of size 8
==8163==    at 0x4EACBB1: ipAddrToAsciiEnginePrivate::run() (ipAddrToAsciiAsynchronous.cpp:273)
==8163==    by 0x4EAEC28: epicsThreadCallEntryPoint (epicsThread.cpp:85)
==8163==    by 0x4EB4F9B: start_routine (osdThread.c:403)
==8163==    by 0x36AEA079D0: start_thread (in /lib64/libpthread-2.12.so)
==8163==    by 0x36AE2E89DC: clone (in /lib64/libc-2.12.so)
==8163==  Address 0x51ff550 is 0 bytes inside a block of size 2,440 free'd
==8163==    at 0x4A06016: operator delete(void*) (vg_replace_malloc.c:480)
==8163==    by 0x4C3844F: cac::~cac() (tsFreeList.h:128)
==8163==    by 0x4C38848: cac::~cac() (cac.cpp:343)
==8163==    by 0x4C5317D: ca_client_context::~ca_client_context() (epicsMemory.h:52)
==8163==    by 0x4C53558: ca_client_context::~ca_client_context() (ca_client_context.cpp:193)
==8163==    by 0x4C3B942: ca_context_destroy (access.cpp:252)
==8163==    by 0x40215F: main (caget.c:551)
==8163==
==8163== Invalid read of size 8
==8163==    at 0x4C5C170: msgForMultiplyDefinedPV::transactionComplete(char const*) (msgForMultiplyDefinedPV.cpp:58)
==8163==    by 0x4EACBB5: ipAddrToAsciiEnginePrivate::run() (ipAddrToAsciiAsynchronous.cpp:273)
==8163==    by 0x4EAEC28: epicsThreadCallEntryPoint (epicsThread.cpp:85)
==8163==    by 0x4EB4F9B: start_routine (osdThread.c:403)
==8163==    by 0x36AEA079D0: start_thread (in /lib64/libpthread-2.12.so)
==8163==    by 0x36AE2E89DC: clone (in /lib64/libc-2.12.so)
==8163==  Address 0x51ff5e0 is 144 bytes inside a block of size 2,440 free'd
==8163==    at 0x4A06016: operator delete(void*) (vg_replace_malloc.c:480)
==8163==    by 0x4C3844F: cac::~cac() (tsFreeList.h:128)
==8163==    by 0x4C38848: cac::~cac() (cac.cpp:343)
==8163==    by 0x4C5317D: ca_client_context::~ca_client_context() (epicsMemory.h:52)
==8163==    by 0x4C53558: ca_client_context::~ca_client_context() (ca_client_context.cpp:193)
==8163==    by 0x4C3B942: ca_context_destroy (access.cpp:252)
==8163==    by 0x40215F: main (caget.c:551)
==8163==
==8163== Invalid read of size 8
==8163==    at 0x4C5C185: msgForMultiplyDefinedPV::transactionComplete(char const*) (msgForMultiplyDefinedPV.cpp:58)
==8163==    by 0x4EACBB5: ipAddrToAsciiEnginePrivate::run() (ipAddrToAsciiAsynchronous.cpp:273)
==8163==    by 0x4EAEC28: epicsThreadCallEntryPoint (epicsThread.cpp:85)
==8163==    by 0x4EB4F9B: start_routine (osdThread.c:403)
==8163==    by 0x36AEA079D0: start_thread (in /lib64/libpthread-2.12.so)
==8163==    by 0x36AE2E89DC: clone (in /lib64/libc-2.12.so)
==8163==  Address 0x510dfe0 is 16 bytes inside a block of size 608 free'd
==8163==    at 0x4A06016: operator delete(void*) (vg_replace_malloc.c:480)
==8163==    by 0x4C5317D: ca_client_context::~ca_client_context() (epicsMemory.h:52)
==8163==    by 0x4C53558: ca_client_context::~ca_client_context() (ca_client_context.cpp:193)
==8163==    by 0x4C3B942: ca_context_destroy (access.cpp:252)
==8163==    by 0x40215F: main (caget.c:551)
==8163==
pure virtual method called
terminate called without an active exception
==8163==
==8163== HEAP SUMMARY:
==8163==     in use at exit: 107,835 bytes in 535 blocks
==8163==   total heap usage: 8,613 allocs, 8,025 frees, 5,192,523 bytes allocated
==8163==
==8163== LEAK SUMMARY:
==8163==    definitely lost: 107 bytes in 2 blocks
==8163==    indirectly lost: 16 bytes in 1 blocks
==8163==      possibly lost: 664 bytes in 4 blocks
==8163==    still reachable: 106,712 bytes in 528 blocks
==8163==         suppressed: 0 bytes in 0 blocks
==8163== Rerun with --leak-check=full to see details of leaked memory
==8163==
==8163== For counts of detected and suppressed errors, rerun with: -v
==8163== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 6 from 6)
Morto

This patch fixes the leak and the abort by redefining the attribute as a pointer and deleting it in the destructor. The destructor, in this case, waits for the thread to finish and correctly deallocates the object, resulting in a clean exit. In the trace below, the amount of memory in use at exit is reduced to ~35 KiB from ~107 Kib:

sol5-linux:~ $ valgrind caget SOL5:MEDIPIX:SizeX
==15446== Memcheck, a memory error detector
==15446== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==15446== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==15446== Command: caget SOL5:MEDIPIX:SizeX
==15446==
SOL5:MEDIPIX:SizeX             1
CA.Client.Exception...............................................
    Warning: "Identical process variable names on multiple servers"
    Context: "Channel: "SOL5:MEDIPIX:SizeX", Connecting to: 127.0.0.1:5064, Ignored: 10.2.101.40:5064"
    Source File: ../cac.cpp line 1299
    Current Time: Mon May 09 2016 11:29:23.525137893
..................................................................
==15446==
==15446== HEAP SUMMARY:
==15446==     in use at exit: 35,078 bytes in 321 blocks
==15446==   total heap usage: 8,626 allocs, 8,248 frees, 5,202,815 bytes allocated
==15446==
==15446== LEAK SUMMARY:
==15446==    definitely lost: 8,339 bytes in 36 blocks
==15446==    indirectly lost: 3,888 bytes in 45 blocks
==15446==      possibly lost: 0 bytes in 0 blocks
==15446==    still reachable: 22,851 bytes in 240 blocks
==15446==         suppressed: 0 bytes in 0 blocks
==15446== Rerun with --leak-check=full to see details of leaked memory
==15446==
==15446== For counts of detected and suppressed errors, rerun with: -v
==15446== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

Merge request reports

Loading