What is the difference between a direct and indirect leak?
Asked Answered
H

2

31

I got the following output from the LeakSanitizer tool. What is the difference between a direct and indirect leak, as the tool understands it?

13: ==29107==ERROR: LeakSanitizer: detected memory leaks
13: 
13: Direct leak of 288 byte(s) in 6 object(s) allocated from:
13:     #0 0x7f2ce0089050 in __interceptor_malloc (/nix/store/zahs1kwq4742f6l6h7yy4mdj44zzc1kd-gcc-7-20170409-lib/lib/libasan.so+0xd9050)
13:     #1 0x7f2cdfb974fe in qdr_core_subscribe ../src/router_core/route_tables.c:149
13:     #2 0x7f2cdfb47ff0 in IoAdapter_init ../src/python_embedded.c:548
13:     #3 0x7f2cde966ecd in type_call (/nix/store/1snk2wkpv97an87pk1842fgskl1vqhkr-python-2.7.14/lib/libpython2.7.so.1.0+0x9fecd)
13: 
13: Indirect leak of 2368 byte(s) in 1 object(s) allocated from:
13:     #0 0x7f2ce0089b88 in __interceptor_posix_memalign (/nix/store/zahs1kwq4742f6l6h7yy4mdj44zzc1kd-gcc-7-20170409-lib/lib/libasan.so+0xd9b88)
13:     #1 0x7f2cdfbcc8ea in qd_alloc ../src/alloc_pool.c:182
13:     #2 0x7f2cdfbb6c6b in qd_server_connection ../src/server.c:500
13:     #3 0x7f2cdfbbe27d in on_accept ../src/server.c:531
13:     #4 0x7f2cdfbbe27d in handle_listener ../src/server.c:701
13:     #5 0x7f2cdfbbe27d in handle ../src/server.c:844
13:     #6 0x7f2cdfbc2837 in thread_run ../src/server.c:921
13:     #7 0x7f2cdf0ba233 in start_thread (/nix/store/zpg78y1mf0di6127q6r51kgx2q8cxsvv-glibc-2.25-49/lib/libpthread.so.0+0x7233)
[...]
Hinkle answered 4/10, 2017 at 20:14 Comment(4)
Check valgrind docu.Iggy
How do the AddressSanitizer categories match up to Valgrind's definitely lost, indirectly lost, possibly lost, still reachable?Hinkle
If I understand this here correctly, it only shows direct and indirect leaks, but not reachable blocks.Iggy
Thank you, it answers my question. That documentation page seems outdated, but in this it seems still relevant.Hinkle
P
14

The accepted answer isn't quite correct. In particular

Stated another way, indirect leaks are a result of direct leaks.

is wrong in that it's possible to have only indirect leaks. This situation may arise when a self-referential structure is built and leaked.

Example:

#include <malloc.h>

struct Foo {
  struct Foo *other;
};

void fn(int depth)
{
  if (depth > 0) {
    // recursion is only necessary to avoid LSan finding "stray" pointers
    // and not reporting any leaks at all.
    fn(depth - 1);
  } else {
    struct Foo *f1 = malloc(sizeof(*f1));
    struct Foo *f2 = malloc(sizeof(*f2));
    f1->other = f2;
    f2->other = f1;
  }
}

int main()
{
  fn(10);
  return 0;
}
clang -g -fsanitize=address  t.c && ./a.out

=================================================================
==845196==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 8 byte(s) in 1 object(s) allocated from:
    #0 0x49832d in malloc (/tmp/a.out+0x49832d)
    #1 0x4c7f8e in fn /tmp/t.c:15:22
    #2 0x4c7f71 in fn /tmp/t.c:12:5
    #3 0x4c7f71 in fn /tmp/t.c:12:5
    #4 0x4c7f71 in fn /tmp/t.c:12:5
    #5 0x4c7f71 in fn /tmp/t.c:12:5
    #6 0x4c7f71 in fn /tmp/t.c:12:5
    #7 0x4c7f71 in fn /tmp/t.c:12:5
    #8 0x4c7f71 in fn /tmp/t.c:12:5
    #9 0x4c7f71 in fn /tmp/t.c:12:5
    #10 0x4c7f71 in fn /tmp/t.c:12:5
    #11 0x4c7f71 in fn /tmp/t.c:12:5
    #12 0x4c8028 in main /tmp/t.c:23:3
    #13 0x7f147d3a7d09 in __libc_start_main csu/../csu/libc-start.c:308:16

Indirect leak of 8 byte(s) in 1 object(s) allocated from:
    #0 0x49832d in malloc (/tmp/a.out+0x49832d)
    #1 0x4c7f80 in fn /tmp/t.c:14:22
    #2 0x4c7f71 in fn /tmp/t.c:12:5
    #3 0x4c7f71 in fn /tmp/t.c:12:5
    #4 0x4c7f71 in fn /tmp/t.c:12:5
    #5 0x4c7f71 in fn /tmp/t.c:12:5
    #6 0x4c7f71 in fn /tmp/t.c:12:5
    #7 0x4c7f71 in fn /tmp/t.c:12:5
    #8 0x4c7f71 in fn /tmp/t.c:12:5
    #9 0x4c7f71 in fn /tmp/t.c:12:5
    #10 0x4c7f71 in fn /tmp/t.c:12:5
    #11 0x4c7f71 in fn /tmp/t.c:12:5
    #12 0x4c8028 in main /tmp/t.c:23:3
    #13 0x7f147d3a7d09 in __libc_start_main csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: 16 byte(s) leaked in 2 allocation(s).

Note: no direct leaks, only indirect ones.

Postgraduate answered 5/10, 2021 at 22:28 Comment(1)
Great answer. Another example is retain cycle issue with shared_ptrs : #22186396Maynardmayne
T
30

The LSan wiki design document states:

Another useful feature is being able to distinguish between directly leaked blocks (not reachable from anywhere) and indirectly leaked blocks (reachable from other leaked blocks).

Stated another way, indirect leaks are a result of direct leaks. Fixing direct leaks should make the indirect leaks become either fixed or direct leaks themselves (depending on whether their memory management is implemented correctly or not, respectively).

Timetable answered 28/8, 2018 at 18:12 Comment(1)
This answer is helpful, but not entirely correct. See the second answer.Postgraduate
P
14

The accepted answer isn't quite correct. In particular

Stated another way, indirect leaks are a result of direct leaks.

is wrong in that it's possible to have only indirect leaks. This situation may arise when a self-referential structure is built and leaked.

Example:

#include <malloc.h>

struct Foo {
  struct Foo *other;
};

void fn(int depth)
{
  if (depth > 0) {
    // recursion is only necessary to avoid LSan finding "stray" pointers
    // and not reporting any leaks at all.
    fn(depth - 1);
  } else {
    struct Foo *f1 = malloc(sizeof(*f1));
    struct Foo *f2 = malloc(sizeof(*f2));
    f1->other = f2;
    f2->other = f1;
  }
}

int main()
{
  fn(10);
  return 0;
}
clang -g -fsanitize=address  t.c && ./a.out

=================================================================
==845196==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 8 byte(s) in 1 object(s) allocated from:
    #0 0x49832d in malloc (/tmp/a.out+0x49832d)
    #1 0x4c7f8e in fn /tmp/t.c:15:22
    #2 0x4c7f71 in fn /tmp/t.c:12:5
    #3 0x4c7f71 in fn /tmp/t.c:12:5
    #4 0x4c7f71 in fn /tmp/t.c:12:5
    #5 0x4c7f71 in fn /tmp/t.c:12:5
    #6 0x4c7f71 in fn /tmp/t.c:12:5
    #7 0x4c7f71 in fn /tmp/t.c:12:5
    #8 0x4c7f71 in fn /tmp/t.c:12:5
    #9 0x4c7f71 in fn /tmp/t.c:12:5
    #10 0x4c7f71 in fn /tmp/t.c:12:5
    #11 0x4c7f71 in fn /tmp/t.c:12:5
    #12 0x4c8028 in main /tmp/t.c:23:3
    #13 0x7f147d3a7d09 in __libc_start_main csu/../csu/libc-start.c:308:16

Indirect leak of 8 byte(s) in 1 object(s) allocated from:
    #0 0x49832d in malloc (/tmp/a.out+0x49832d)
    #1 0x4c7f80 in fn /tmp/t.c:14:22
    #2 0x4c7f71 in fn /tmp/t.c:12:5
    #3 0x4c7f71 in fn /tmp/t.c:12:5
    #4 0x4c7f71 in fn /tmp/t.c:12:5
    #5 0x4c7f71 in fn /tmp/t.c:12:5
    #6 0x4c7f71 in fn /tmp/t.c:12:5
    #7 0x4c7f71 in fn /tmp/t.c:12:5
    #8 0x4c7f71 in fn /tmp/t.c:12:5
    #9 0x4c7f71 in fn /tmp/t.c:12:5
    #10 0x4c7f71 in fn /tmp/t.c:12:5
    #11 0x4c7f71 in fn /tmp/t.c:12:5
    #12 0x4c8028 in main /tmp/t.c:23:3
    #13 0x7f147d3a7d09 in __libc_start_main csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: 16 byte(s) leaked in 2 allocation(s).

Note: no direct leaks, only indirect ones.

Postgraduate answered 5/10, 2021 at 22:28 Comment(1)
Great answer. Another example is retain cycle issue with shared_ptrs : #22186396Maynardmayne

© 2022 - 2024 — McMap. All rights reserved.