cvalgrindlibxml2

Memory leak when copying dom and then replacing node (xmlCopyDoc and xmlReplaceNode)


I am building a program that does a merge validation between two xml files using libxml2 v2.11.

In summary, I am creating a copy of a target dom to then replace all nodes with the replacement dom's nodes, only if target node has a property "overloadable" equal to true. Finally, I validate the result dom against a schema and schematron file.

Currently, this code does it (Sorry if it is messy, I had been debugging a lot over it) :

/**
 * replace_nodes:
 * @target_node: A node to be replaced
 * @replace_node: A node to copy
 * @Output:    0 on Success or error code.
 *
 * Replace target_node with a copy of replace_node. This function changes the dom tree.
 */
static xmlNodePtr replace_node(xmlNodePtr target_node, xmlNodePtr replace_node){
    xmlNodePtr new_target_node = xmlCopyNode(replace_node,1);
    xmlSetProp(new_target_node, (xmlChar *) "overloadable", (xmlChar *) "true");
    xmlReplaceNode (target_node, new_target_node);
    DEV_DEBUG_PRINT("Replaced node %s \n", replace_node->name);
    return new_target_node;
}

int validate_overloadables_merge(xmlDocPtr target_dom, xmlDocPtr replace_dom){
    struct xpath_exp {
        char target_xpath_exp[100];
        char replace_xpath_exp[100];
    };
    struct xpath_exp find_xpr_tb =
        {
            .target_xpath_exp = "/configuration",
            .replace_xpath_exp = "/configurations/replacement/*",
        };
    int ret = 0;

    // copy target dom
    xmlDocPtr copy_dom = xmlCopyDoc(target_dom, 1);
    xmlChar xpath_expr_replace[STR_LEN], xpath_expr_target[STR_LEN];
    xmlXPathObjectPtr xpath_obj_target, xpath_obj_replace;
    strcpy((char *)xpath_expr_target, find_xpr_tb.target_xpath_exp);
    strcpy((char *)xpath_expr_replace, find_xpr_tb.replace_xpath_exp);

    // Get all inputs from target dom with an xpath expr
    int ret_target = query_dom(xpath_expr_target, &xpath_obj_target, copy_dom);
    // Get all inputs from replacement dom with an xpath expr
    int ret_replace = query_dom(xpath_expr_replace, &xpath_obj_replace, replace_dom);

    // Error handling in case there is not matches
    if(ret_target == -EACCES  || ret_replace == -EACCES)
    {
        ERR_MSG("Error: Fail to query\n");
        if(ret_target != -EACCES)
        {
            xmlXPathFreeObject(xpath_obj_target);
        }
        xmlFreeDoc(copy_dom);
        return -EACCES;
    }
    else if (ret_target == -ESRCH || ret_replace == -ESRCH ||
            xpath_obj_target->nodesetval->nodeNr < 1 ||
            xpath_obj_replace->nodesetval->nodeNr < 1)
    {
        DEV_DEBUG_PRINT("DEBUG_INFO: No replaceomization nodes found\n");
        xmlXPathFreeObject(xpath_obj_target);
        xmlXPathFreeObject(xpath_obj_replace);
        xmlFreeDoc(copy_dom);
    }

    // Get first node to potentially replace it
    xmlNodePtr target_node =  xpath_obj_target->nodesetval->nodeTab[0];
    while(target_node)
    {
        // Get next sibling to potentially replace it. This is done here as we might free the target node
        xmlNodePtr nx_target_node = target_node->next;
        char * overloadable = (char *) xmlGetProp(target_node, (xmlChar *) "overloadable");
        //If overloadable, we replace it
        if(overloadable != NULL && 0==strcmp(overloadable,"true"))
        {
            for(int replace_idx=0; replace_idx < xpath_obj_replace->nodesetval->nodeNr; replace_idx++){
                xmlNodePtr replace_node = xpath_obj_replace->nodesetval->nodeTab[replace_idx];
                if(0==strcmp((char *)target_node->name,
                             (char *)replace_node->name))
                {
                    xmlNodePtr new_target_node = replace_node(target_node, replace_node);
                    //xmlFreeNode(target_node); 
                    xmlReconciliateNs(copy_dom, new_target_node);
                }
            }
        }
        if(overloadable)
        {
            xmlFree(overloadable);
        }        
        target_node = nx_target_node;
    }
    if( is_conf_sct && ret >= 0){
        DEV_DEBUG_PRINT("DEBUG_INFO: Validating copy sct\n");
        ret = xml_validate_sct(copy_dom, conf_sct_path);
    }
    if(is_conf_xsd && ret >= 0){
        DEV_DEBUG_PRINT("DEBUG_INFO: Validating copy xsd\n");
        ret = xml_validate_xsd(copy_dom, conf_xsd_path);
    }
    xmlXPathFreeObject(xpath_obj_target);
    xmlXPathFreeObject(xpath_obj_replace);
    xmlFreeDoc(copy_dom);
    return ret;
}

The problems comes when I pass valgrind for identifying memory leaks:

jmgs@esswlab3:~/Source/config$ valgrind  --leak-check=full ./config inputs/config.xml -c inputs/custom.xml -x schemas/config.xsd  -s schematrons/config.sct 
==96255== Memcheck, a memory error detector
==96255== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==96255== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==96255== Command: ./config inputs/config.xml -c inputs/custom.xml -x schemas/config.xsd  -s schematrons/config.sct 
==96255==
Document inputs/config.xml loaded
Document complient with schema
Document complient with schematron
Replaced node input_1
Replaced node input_4
Document complient with schematron
Document complient with schema
==96255==
==96255== HEAP SUMMARY:
==96255==     in use at exit: 3,343 bytes in 52 blocks
==96255==   total heap usage: 54,905 allocs, 54,853 frees, 5,176,922 bytes allocated
==96255==
==96255== 3,343 (240 direct, 3,103 indirect) bytes in 2 blocks are definitely lost in loss record 8 of 8
==96255==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96255==    by 0x49B550F: xmlStaticCopyNode (tree.c:4311)
==96255==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
==96255==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
==96255==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
==96255==    by 0x10DFE8: validate_overloadables_merge (in /home/jmgs/Source/config)
==96255==    by 0x10D0DC: config_load_cust_to_shm (in  /home/jmgs/Source/config-manager)
==96255==    by 0x10FBFE: method_load_customization (in /home/jmgs/Source/config)
==96255==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96255==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96255==    by 0x111683: config_register_dbus (in  /home/jmgs/Source/config)
==96255==    by 0x10CD2B: main (in  /home/jmgs/Source/config)
==96255==
==96255== LEAK SUMMARY:
==96255==    definitely lost: 240 bytes in 2 blocks
==96255==    indirectly lost: 3,103 bytes in 50 blocks
==96255==      possibly lost: 0 bytes in 0 blocks
==96255==    still reachable: 0 bytes in 0 blocks
==96255==         suppressed: 0 bytes in 0 blocks
==96255==
==96255== For lists of detected and suppressed errors, rerun with: -s
==96255== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0

My guess is that the memory leak comes from not freeing the target_node when it is replaced. However, when i do this I get the following valgrind error message:

==96577== Memcheck, a memory error detector
==96577== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==96577== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==96577== Command: ./config inputs/config.xml -c inputs/custom.xml -x schemas/config.xsd  -s schematrons/config.sct
==96577==
Document inputs/config.xml loaded
Document complient with schema
Document complient with schematron
Document inputs/custom.xml loaded
Replaced node input_1
==96577== Invalid read of size 8
==96577==    at 0x10E229: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Address 0x54811c0 is 16 bytes inside a block of size 120 free'd
==96577==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x10E298: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Block was alloc'd at
==96577==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49B550F: xmlStaticCopyNode (tree.c:4311)
==96577==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
==96577==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
==96577==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
==96577==    by 0x10DFE8: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==
==96577== Invalid read of size 1
==96577==    at 0x484FBD4: strcmp (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x10E239: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Address 0x5481270 is 0 bytes inside a block of size 7 free'd
==96577==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49B1512: xmlFreeNode (tree.c:3892)
==96577==    by 0x49B1512: xmlFreeNode (tree.c:3838)
==96577==    by 0x10E298: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Block was alloc'd at
==96577==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49CA8B5: xmlStrndup (xmlstring.c:49)
==96577==    by 0x49B58B4: xmlStaticCopyNode (tree.c:4331)
==96577==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
==96577==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
==96577==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
==96577==    by 0x10DFE8: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==
==96577== Invalid read of size 1
==96577==    at 0x484FBE8: strcmp (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x10E239: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Address 0x5481271 is 1 bytes inside a block of size 7 free'd
==96577==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49B1512: xmlFreeNode (tree.c:3892)
==96577==    by 0x49B1512: xmlFreeNode (tree.c:3838)
==96577==    by 0x10E298: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Block was alloc'd at
==96577==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49CA8B5: xmlStrndup (xmlstring.c:49)
==96577==    by 0x49B58B4: xmlStaticCopyNode (tree.c:4331)
==96577==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
==96577==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
==96577==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
==96577==    by 0x10DFE8: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==
Replaced node input_4
Document complient with schematron
Document complient with schema
==96577== Invalid read of size 4
==96577==    at 0x4A4A2FA: xmlXPathFreeNodeSet (xpath.c:4115)
==96577==    by 0x4A4C541: xmlXPathFreeObject (xpath.c:5467)
==96577==    by 0x10E1A6: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Address 0x54811b8 is 8 bytes inside a block of size 120 free'd
==96577==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x10E298: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==  Block was alloc'd at
==96577==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==96577==    by 0x49B550F: xmlStaticCopyNode (tree.c:4311)
==96577==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
==96577==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
==96577==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
==96577==    by 0x10DFE8: validate_overloadables_merge (in /home/jmgs/Source/config)
==96577==    by 0x10D0DC: config_load_cust_to_shm (in /home/jmgs/Source/config)
==96577==    by 0x10FC0E: method_load_customization (in /home/jmgs/Source/config)
==96577==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
==96577==    by 0x111693: config_register_dbus (in /home/jmgs/Source/config)
==96577==    by 0x10CD2B: main (in /home/jmgs/Source/config)
==96577==
Document complient with schematron
Document complient with schema
==96577==
==96577== HEAP SUMMARY:
==96577==     in use at exit: 0 bytes in 0 blocks
==96577==   total heap usage: 54,910 allocs, 54,910 frees, 5,177,146 bytes allocated
==96577==
==96577== All heap blocks were freed -- no leaks are possible
==96577==
==96577== For lists of detected and suppressed errors, rerun with: -s
==96577== ERROR SUMMARY: 9 errors from 4 contexts (suppressed: 0 from 0)

Thanks in advance.


Solution

  • I have discovered the bug that triggers valgrind's Invalid Read message. I am accessing (within the if statement) target_node, once already freed strcmp((char *)target_node->name. A valid solution would be to add a break statement:

                for(int replace_idx=0; replace_idx < xpath_obj_replace->nodesetval->nodeNr; replace_idx++)
                {
                    xmlNodePtr replace_node = xpath_obj_replace->nodesetval->nodeTab[replace_idx];
                    if(0==strcmp((char *)target_node->name,
                                 (char *)replace_node->name))
                    {
                        xmlNodePtr new_target_node = replace_node(target_node, replace_node);
                        xmlFreeNode(target_node); 
                        xmlReconciliateNs(copy_dom, new_target_node);
                        break;
                    }
                }
    

    As piece of advice (that I would have liked to have) for similar situations, do not forget to compile with debug flag -g. This lets valgrind produce a more detailed report. E.g. In my case

      ┆ ==100275== Invalid read of size 8
      ┆ ==100275==    at 0x10EC7F: validate_overloadables_merge (config-parsing.c:465)
      ┆ ==100275==    by 0x10CDCF: config_load_cust_to_shm (config.c:111)
      ┆ ==100275==    by 0x111685: method_load_customization (config-sdbus.c:46)
      ┆ ==100275==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x114CE5: config_register_dbus (config-sdbus.c:807)
      ┆ ==100275==    by 0x10D741: main (config.c:328)
      ┆ ==100275==  Address 0x5cf2460 is 16 bytes inside a block of size 120 free'd
      ┆ ==100275==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
      ┆ ==100275==    by 0x10ECB9: validate_overloadables_merge (config-parsing.c:470)
      ┆ ==100275==    by 0x10CDCF: config_load_cust_to_shm (config.c:111)
      ┆ ==100275==    by 0x111685: method_load_customization (config-sdbus.c:46)
      ┆ ==100275==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x114CE5: config_register_dbus (config-sdbus.c:807)
      ┆ ==100275==    by 0x10D741: main (config.c:328)
      ┆ ==100275==  Block was alloc'd at
      ┆ ==100275==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
      ┆ ==100275==    by 0x49B550F: xmlStaticCopyNode (tree.c:4311)
      ┆ ==100275==    by 0x49B569E: xmlStaticCopyNode (tree.c:4422)
      ┆ ==100275==    by 0x49B5BB3: xmlStaticCopyNodeList (tree.c:4495)
      ┆ ==100275==    by 0x49B60A0: xmlCopyDoc (tree.c:4716)
      ┆ ==100275==    by 0x10E9C5: validate_overloadables_merge (config-parsing.c:423)
      ┆ ==100275==    by 0x10CDCF: config_load_cust_to_shm (config.c:111)
      ┆ ==100275==    by 0x111685: method_load_customization (config-sdbus.c:46)
      ┆ ==100275==    by 0x48B327A: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x48C176C: ??? (in /usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0)
      ┆ ==100275==    by 0x114CE5: config_register_dbus (config-sdbus.c:807)
      ┆ ==100275==    by 0x10D741: main (config.c:328)
    

    I also recommend this other reading: How do I use valgrind to find memory leaks?