Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

heapusage detects Memory leak possibly caused by printf

I am implementing a priority queue with linked list in C, however I am getting memory leaks when I print out my pop operations. I have another memory leak and I am trying to find it too.

As a side note, I am using heapusage by d99kris instead of Valgrind.

This is the heap summary when I use printf:

HEAP SUMMARY:
in use at exit: 4112 bytes in 2 blocks
total heap usage: 10 allocs, 17 frees, 4536 bytes allocated
peak heap usage: 4256 bytes allocated
16 bytes in 1 block(s) are lost, originally allocated at:
LEAK SUMMARY:
definitely lost: 4112 bytes in 2 blocks

This is the heap summary without printf:

HEAP SUMMARY:
in use at exit: 16 bytes in 1 blocks
total heap usage: 9 allocs, 10 frees, 440 bytes allocated
peak heap usage: 256 bytes allocated
LEAK SUMMARY:
definitely lost: 16 bytes in 1 blocks

My pop function:

void *prio_q_pop(struct prio_q *q) {
     q->size--;
     struct elem *temp = q->first;
     (q->first) = (q->first)->next;
     void *asd = temp->datei;
     free(temp);
     return asd;
 }

And my main function where I call printf

struct prio_q *queue;
     char *s;
     int i;

     queue = prio_q_create();

     push(queue, "Bye World", 0);

     for (i = 0; i < 5; i++) {
         s = prio_q_pop(queue);
         //printf("%s\n", s);
     }
     s = prio_q_front(queue);
     //printf("%s\n", s);                                                                          

reason

Problem is not caused by my code, It is the memory checker. The following program leaks 1 block with a heap usage of 2 allocs and 4 frees.

#include <stdio.h>

int main() {
    printf("omer");
    return 0;
}
like image 594
keser Avatar asked Dec 17 '22 17:12

keser


2 Answers

This is a false positive. If anything, the problem is that heapusage does not have good enough documentation. I recommend using a better leak checker like the leak sanitizer or Valgrind.

I created a file test.c.

#include <stdio.h>
int main(int argc, char **argv) {
    puts("Hello, world!");
}

With leak sanitizer, no errors.

$ cc -fsanitize=leak -g test.c
$ ./a.out 
Hello, world!

With address sanitizer, no errors.

$ cc -fsanitize=address -g test.c
$ ./a.out 
Hello, world!

With Valgrind, no errors.

$ cc -g test.c
$ valgrind ./a.out
==189174== Memcheck, a memory error detector
==189174== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==189174== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==189174== Command: ./a.out
==189174== 
Hello, world!
==189174== 
==189174== HEAP SUMMARY:
==189174==     in use at exit: 0 bytes in 0 blocks
==189174==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==189174== 
==189174== All heap blocks were freed -- no leaks are possible
==189174== 
==189174== For counts of detected and suppressed errors, rerun with: -v
==189174== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

With heapusage, a leak!

$ cc -g test.c                  
$ ./heapusage ./a.out
Hello, world!
==189005== Heapusage - https://github.com/d99kris/heapusage
==189005== 
==189005== HEAP SUMMARY:
==189005==     in use at exit: 1024 bytes in 1 blocks
==189005==   total heap usage: 1 allocs, 0 frees, 1024 bytes allocated
==189005==    peak heap usage: 1024 bytes allocated
==189005== 
==189005== 1024 bytes in 1 block(s) are lost, originally allocated at:
==189005==    at 0x00007f99f0de56a7: malloc + 49
==189005==    at 0x00007f99f0a96a32: _IO_file_doallocate + 114
==189005==    at 0x00007f99f0aa4a46: _IO_doallocbuf + 70
==189005==    at 0x00007f99f0aa3da8: _IO_file_overflow + 472
==189005==    at 0x00007f99f0aa2e86: _IO_file_xsputn + 182
==189005==    at 0x00007f99f0a99033: _IO_puts + 211
==189005==    at 0x000055f667ee7655: 
==189005==    at 0x00007f99f0a502b1: __libc_start_main + 241
==189005==    at 0x000055f667ee755a: 
==189005== 
==189005== LEAK SUMMARY:
==189005==    definitely lost: 1024 bytes in 1 blocks
==189005== 

Analysis

Heapusage works by hooking malloc and free (and doesn't scan memory for pointers). Heapusage doesn't explain the advantages or disadvantages of this approach fully in the documentation. One advantage is that it's fast, but a disadvantage is that it's not precise.

In particular, I would call out heapusage as giving incorrect messages: the words "definitely lost" don't apply here!

If you want better error messages, use one of the tools recommended above: leak sanitizer or Valgrind (memcheck).

In general, I would also like to remind people that false positives are a fact of life with tools like these. Whether a program is "Valgrind clean" is a different question from whether the program is correct.

like image 140
Dietrich Epp Avatar answered Dec 31 '22 01:12

Dietrich Epp


Unlike Valgrind, heapusage does not track memory allocated by the C library for its own purposes. printf indirectly causes this as the stream stdout is line buffered to the terminal and fully buffered to a file. The stream buffer is allocated (by printf or any other output function) only when you actually produce output.

You can try and work around this limitation by making stdout unbuffered at the start of your main function. Try this for example:

#include <stdio.h>

int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("omer\n");
    return 0;
}

If the above code still shows a leak, try this alternative:

#include <stdio.h>

int main() {
    char buf[BUFSIZ];
    setvbuf(stdout, buf, _IONBF, BUFSIZ);
    printf("omer\n");
    fclose(stdout);
    return 0;
}

Notice also that reading input from stdin will allocate a buffer for the input stream too. Any other stream opened by the program should have been closed prior to leaving the main() function. Closing a stream releases any memory allocated for it behind the scenes.

like image 37
chqrlie Avatar answered Dec 31 '22 02:12

chqrlie