2

I am trying to find a memory leak in my code but for the life of me, I cannot figure out or identify the reason for this leak. Which is why I am reaching out to your all. Thanks in advance.

The following code, creates a kerberos cache from keytab. The code is an extract of a bigger code, but this also has the same memory leak. The full code is as following

#include <iostream>
#include <cstring>

#include <krb5/krb5.h>

bool renew(krb5_context &_ctx, krb5_keytab &_keytab, krb5_ccache &_cache, std::string &_principal)
{
    long int retval;
    bool success = false;
    
    krb5_principal principal;
    krb5_creds *creds;

    if ((retval = krb5_parse_name(_ctx, _principal.c_str(), &principal)))
        throw "cannot parse principal string";

    creds = (krb5_creds*) malloc(sizeof(*creds));
    memset(creds, 0, sizeof(creds));

    if ((retval = krb5_get_init_creds_keytab(_ctx, creds, principal, _keytab, 0, NULL, NULL)))
    {
        free(creds);
        krb5_free_principal(_ctx, principal);
        throw "cannot initialize keytab credentials - ";
    }

    if ((retval = krb5_cc_initialize(_ctx, _cache, principal)))
    {
        free(creds);
        krb5_free_principal(_ctx, principal);
        throw " cannot initialize cache - ";
    }

    if ((retval = krb5_cc_store_cred(_ctx, _cache, creds)))
    {
        free(creds);
        krb5_free_principal(_ctx, principal);
        throw "cannot store credentials - ";
    }

    free(creds);
    krb5_free_principal(_ctx, principal);

    return success;
}

int main()
{
    long int retval;

    std::string _keytab_file, _cache_file, _realm, _principal;

    krb5_context _ctx = NULL;
    krb5_keytab _keytab = NULL;
    krb5_ccache _cache = NULL;

    _keytab_file = "/location/to/the/keytab/file";
    _cache_file = "/location/to/the/cache/file";
    _principal = "user.name@DOMAIN.COM";

    if ((retval = krb5_init_context(&_ctx)))
        throw "cannot initialize context";

    if ((retval = krb5_kt_resolve(_ctx, _keytab_file.c_str(), &_keytab)))
        throw "cannot resolve keytab";

    if ((retval = krb5_cc_resolve(_ctx, _cache_file.c_str(), &_cache)))
        throw "cannot open/initialize kerberos cache";

    try {
        renew(_ctx, _keytab, _cache, _principal);
    } catch (std::exception &e) {
        std::cerr<<e.what()<<std::endl;
    }

bailout:

    if (_cache)
        krb5_cc_close(_ctx, _cache);

    if (_keytab)
        krb5_kt_close(_ctx, _keytab);

    if (_ctx)
        krb5_free_context(_ctx);

    return 0;
}

in a linux (or similar) box following used to compile and run with valgrind to check memory leak

g++ krb.cpp -o krb -lkrb5 -g
valgrind --leak-check=full ./krb

the valgrind report is as following-

==257623== 
==257623== HEAP SUMMARY:
==257623==     in use at exit: 4,964 bytes in 19 blocks
==257623==   total heap usage: 9,165 allocs, 9,146 frees, 3,702,330 bytes allocated
==257623== 
==257623== 16 bytes in 1 blocks are definitely lost in loss record 6 of 14
==257623==    at 0x4C37135: malloc (vg_replace_malloc.c:381)
==257623==    by 0x5E31F8D: krb5int_c_copy_keyblock_contents (in /usr/lib64/libk5crypto.so.3.1)
==257623==    by 0x4E91146: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C65B: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C6F5: krb5_get_init_creds_keytab (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4012EF: renew(_krb5_context*&, _krb5_kt*&, _krb5_ccache*&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (krb.cpp:25)
==257623==    by 0x401612: main (krb.cpp:76)
==257623== 
==257623== 78 (40 direct, 38 indirect) bytes in 1 blocks are definitely lost in loss record 8 of 14
==257623==    at 0x4C37135: malloc (vg_replace_malloc.c:381)
==257623==    by 0x4E91574: krb5_copy_principal (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E910EB: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C65B: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C6F5: krb5_get_init_creds_keytab (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4012EF: renew(_krb5_context*&, _krb5_kt*&, _krb5_ccache*&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (krb.cpp:25)
==257623==    by 0x401612: main (krb.cpp:76)
==257623== 
==257623== 97 (40 direct, 57 indirect) bytes in 1 blocks are definitely lost in loss record 9 of 14
==257623==    at 0x4C37135: malloc (vg_replace_malloc.c:381)
==257623==    by 0x4E91574: krb5_copy_principal (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9112F: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C65B: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C6F5: krb5_get_init_creds_keytab (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4012EF: renew(_krb5_context*&, _krb5_kt*&, _krb5_ccache*&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (krb.cpp:25)
==257623==    by 0x401612: main (krb.cpp:76)
==257623== 
==257623== 1,646 bytes in 1 blocks are definitely lost in loss record 13 of 14
==257623==    at 0x4C37135: malloc (vg_replace_malloc.c:381)
==257623==    by 0x4E91369: krb5int_copy_data_contents (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E913EB: krb5_copy_data (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E91198: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C65B: ??? (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4E9C6F5: krb5_get_init_creds_keytab (in /usr/lib64/libkrb5.so.3.3)
==257623==    by 0x4012EF: renew(_krb5_context*&, _krb5_kt*&, _krb5_ccache*&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (krb.cpp:25)
==257623==    by 0x401612: main (krb.cpp:76)
==257623== 
==257623== LEAK SUMMARY:
==257623==    definitely lost: 1,742 bytes in 4 blocks
==257623==    indirectly lost: 95 bytes in 7 blocks
==257623==      possibly lost: 0 bytes in 0 blocks
==257623==    still reachable: 3,127 bytes in 8 blocks
==257623==         suppressed: 0 bytes in 0 blocks
==257623== Reachable blocks (those to which a pointer was found) are not shown.
==257623== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==257623== 
==257623== For lists of detected and suppressed errors, rerun with: -s
==257623== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)

looking forward to your input :)

Amirul I
  • 191
  • 3
  • 15
  • Did you try reading `krb5_get_init_creds_keytab`'s documentation? Valgrind's leak report seems pretty straightforward. – Sam Varshavchik Sep 13 '22 at 11:52
  • 3
    Aren't you getting tired of all this repeated clean-up code (`free(creds); krb5_free_principal(_ctx, principal);`) that could be dealt with using RAII? – Krzysiek Karbowiak Sep 13 '22 at 12:06
  • @KrzysiekKarbowiak yes, that is a TODO for me.. will refactor.. this is a poc code for the time. – Amirul I Sep 13 '22 at 12:09
  • @SamVarshavchik the krb5 document for ```krb5_get_init_creds_keytab``` dont say anything about freeing. Ref: https://web.mit.edu/kerberos/www/krb5-latest/doc/appdev/refs/api/krb5_get_init_creds_keytab.html what am I missing here not sure – Amirul I Sep 13 '22 at 12:17
  • 1
    The structure is explicitly marked as an "out" parameter, and it also doesn't say that the initialized structure should NOT be freed, either: that it is owned by the library and that it is responsible for deleting it. The preponderance of the evidence shows that it's your responsibility to do so, probably using `krb5_free_creds`. – Sam Varshavchik Sep 13 '22 at 12:22
  • thanks a lot @SamVarshavchik not sure how I missed ```krb5_free_creds``` – Amirul I Sep 16 '22 at 16:57

0 Answers0