fswebcam - debugging memory leaks

Hi there,

Coming here from this thread: This kernel is very sick – had a horrible death - openSUSE Forums. fswebcam probably crashed my server due to memory leaks. I use it to take snapshots once a minute and over the months the leaks are accumulating. fswebcam is of version 20070108 which I got here: FireStorm.cx - fswebcam.

I used valgrind to see what happens, like this:

valgrind --tool=memcheck --leak-check=full --show-reachable=yes
fswebcam -q --log syslog -F 3 -S 2 -d V4L2:/dev/video0
-r 960x720 -p YUYV testpic.jpg 2> valgrind.log

… and the result is:

==31110== Memcheck, a memory error detector.
==31110== Copyright (C) 2002-2007, and GNU GPL’d, by Julian Seward et al.
==31110== Using LibVEX rev 1854, a library for dynamic binary translation.
==31110== Copyright (C) 2004-2007, and GNU GPL’d, by OpenWorks LLP.
==31110== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==31110== Copyright (C) 2000-2007, and GNU GPL’d, by Julian Seward et al.
==31110== For more details, rerun with: -v
==31110==
==31110== Invalid write of size 4
==31110== at 0x804A8AC: fswc_add_job (fswebcam.c:1643)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110== Address 0x44c9298 is 0 bytes inside a block of size 1 alloc’d
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804A898: fswc_add_job (fswebcam.c:1629)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110==
==31110== Syscall param ioctl(generic) points to uninitialised byte(s)
==31110== at 0x4000832: (within /lib/ld-2.9.so)
==31110== by 0x805360B: src_v4l2_open (src_v4l2.c:795)
==31110== by 0x804F73E: src_open (src.c:110)
==31110== by 0x804C213: fswc_grab (fswebcam.c:1068)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110== Address 0xbe92ecf0 is on thread 1’s stack
==31110==
==31110== Invalid read of size 4
==31110== at 0x804C726: fswc_grab (fswebcam.c:1245)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110== Address 0x44c9298 is 0 bytes inside a block of size 1 alloc’d
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804A898: fswc_add_job (fswebcam.c:1629)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110==
==31110== Invalid read of size 4
==31110== at 0x804A10B: fswc_free_jobs (fswebcam.c:1655)
==31110== by 0x804A214: fswc_free_config (fswebcam.c:2057)
==31110== by 0x804D990: main (fswebcam.c:2157)
==31110== Address 0x44c9298 is 0 bytes inside a block of size 1 alloc’d
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804A898: fswc_add_job (fswebcam.c:1629)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110==
==31110== Invalid read of size 4
==31110== at 0x804A120: fswc_free_jobs (fswebcam.c:1655)
==31110== by 0x804A214: fswc_free_config (fswebcam.c:2057)
==31110== by 0x804D990: main (fswebcam.c:2157)
==31110== Address 0x44c9298 is 0 bytes inside a block of size 1 alloc’d
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804A898: fswc_add_job (fswebcam.c:1629)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110==
==31110== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 5 from 1)
==31110== malloc/free: in use at exit: 36,008 bytes in 73 blocks.
==31110== malloc/free: 2,582 allocs, 2,509 frees, 12,592,771 bytes allocated.
==31110== For counts of detected errors, rerun with: -v
==31110== searching for pointers to 73 not-freed blocks.
==31110== checked 306,276 bytes.
==31110==
==31110==
==31110== 7 bytes in 1 blocks are still reachable in loss record 1 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x410845F: strdup (in /lib/libc-2.9.so)
==31110== by 0x406BF90: fontFetch (gdft.c:456)
==31110== by 0x406A1B9: gdCacheGet (gdcache.c:128)
==31110== by 0x406A7B9: gdImageStringFTEx (gdft.c:911)
==31110== by 0x406BE0C: gdImageStringFT (gdft.c:813)
==31110== by 0x804B776: fswc_DrawText (fswebcam.c:307)
==31110== by 0x804B868: fswc_DrawText (fswebcam.c:298)
==31110== by 0x804BD16: fswc_draw_banner (fswebcam.c:907)
==31110== by 0x804C083: fswc_output (fswebcam.c:966)
==31110== by 0x804CEA2: fswc_grab (fswebcam.c:1252)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110==
==31110==
==31110== 16 bytes in 1 blocks are still reachable in loss record 2 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4393FCD: FT_New_Memory (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x43943C3: FT_Init_FreeType (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x406A653: gdFontCacheSetup (gdft.c:825)
==31110== by 0x406BAFC: gdImageStringFTEx (gdft.c:895)
==31110== by 0x406BE0C: gdImageStringFT (gdft.c:813)
==31110== by 0x804B776: fswc_DrawText (fswebcam.c:307)
==31110== by 0x804B868: fswc_DrawText (fswebcam.c:298)
==31110== by 0x804BD16: fswc_draw_banner (fswebcam.c:907)
==31110== by 0x804C083: fswc_output (fswebcam.c:966)
==31110== by 0x804CEA2: fswc_grab (fswebcam.c:1252)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110==
==31110==
==31110== 17 bytes in 1 blocks are definitely lost in loss record 3 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804F63C: src_open (src.c:81)
==31110== by 0x804C213: fswc_grab (fswebcam.c:1068)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110==
==31110==
==31110== 52 bytes in 3 blocks are still reachable in loss record 4 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x406C71C: gdMalloc (gdhelpers.c:85)
==31110== by 0x406A25D: gdCacheCreate (gdcache.c:73)
==31110== by 0x406A699: gdFontCacheSetup (gdft.c:830)
==31110== by 0x406BAFC: gdImageStringFTEx (gdft.c:895)
==31110== by 0x406BE0C: gdImageStringFT (gdft.c:813)
==31110== by 0x804B776: fswc_DrawText (fswebcam.c:307)
==31110== by 0x804B868: fswc_DrawText (fswebcam.c:298)
==31110== by 0x804BD16: fswc_draw_banner (fswebcam.c:907)
==31110== by 0x804C083: fswc_output (fswebcam.c:966)
==31110== by 0x804CEA2: fswc_grab (fswebcam.c:1252)
==31110== by 0x804D97C: main (fswebcam.c:2092)
==31110==
==31110==
==31110== 287 bytes in 1 blocks are still reachable in loss record 5 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x406C6F3: gdRealloc (gdhelpers.c:91)
==31110== by 0x406C08C: fontFetch (gdft.c:1582)
==31110== by 0x406A1B9: gdCacheGet (gdcache.c:128)
==31110== by 0x406A7B9: gdImageStringFTEx (gdft.c:911)
==31110== by 0x406BE0C: gdImageStringFT (gdft.c:813)
==31110== by 0x804B776: fswc_DrawText (fswebcam.c:307)
==31110== by 0x804B868: fswc_DrawText (fswebcam.c:298)
==31110== by 0x804BD16: fswc_draw_banner (fswebcam.c:907)
==31110== by 0x804C083: fswc_output (fswebcam.c:966)
==31110== by 0x804CEA2: fswc_grab (fswebcam.c:1252)
==31110==
==31110==
==31110== 1,808 bytes in 4 blocks are still reachable in loss record 6 of 7
==31110== at 0x4027EFC: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x43940D3: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x4399DF1: ft_mem_qrealloc (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439A63E: ft_mem_realloc (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439AD25: FT_CMap_New (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x43D18EF: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x43D1C70: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x43A8CAF: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439A313: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439C6F4: FT_Open_Face (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439D647: FT_New_Face (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x406C49A: fontFetch (gdft.c:487)
==31110==
==31110==
==31110== 33,821 bytes in 62 blocks are still reachable in loss record 7 of 7
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x439401C: (within /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439822A: ft_mem_qalloc (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x4399CC2: ft_mem_alloc (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x439A052: FT_New_Library (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x43943D8: FT_Init_FreeType (in /usr/lib/libfreetype.so.6.3.18)
==31110== by 0x406A653: gdFontCacheSetup (gdft.c:825)
==31110== by 0x406BAFC: gdImageStringFTEx (gdft.c:895)
==31110== by 0x406BE0C: gdImageStringFT (gdft.c:813)
==31110== by 0x804B776: fswc_DrawText (fswebcam.c:307)
==31110== by 0x804B868: fswc_DrawText (fswebcam.c:298)
==31110== by 0x804BD16: fswc_draw_banner (fswebcam.c:907)
==31110==
==31110== LEAK SUMMARY:
==31110== definitely lost: 17 bytes in 1 blocks.
==31110== possibly lost: 0 bytes in 0 blocks.
==31110== still reachable: 35,991 bytes in 72 blocks.
==31110== suppressed: 0 bytes in 0 blocks.

Now, what next? What does ‘still reachable’ mean? As it looks the gd library (gd-2.0.35) isn’t clean as well. Can someone kick me in the right direction, please? (BTW I’m a user, not an expert C/C++ programmer).

Ok, I think that I’ve found one of the bugs. Valgrind complains like this:

==31110== Invalid write of size 4
==31110== at 0x804A8AC: fswc_add_job (fswebcam.c:1643)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)
==31110== Address 0x44c9298 is 0 bytes inside a block of size 1 alloc’d
==31110== at 0x4027DDE: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x4027F5F: realloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==31110== by 0x804A898: fswc_add_job (fswebcam.c:1629)
==31110== by 0x804AABF: fswc_getopts (fswebcam.c:2011)
==31110== by 0x804D6EC: main (fswebcam.c:2077)

fswebcam.c line 1629 reads like this:

n = realloc(config->job, sizeof(fswebcam_job_t *) * config->jobs + 1);

and I think that should rather be:

n = realloc(config->job, sizeof(fswebcam_job_t *) * (config->jobs + 1));

Do you think I’m on the right track here? I wonder how it worked at all.

Making some further progress in debugging this. After adding some free() in src.c (bug is in function src_open()) I get now:

==16139== LEAK SUMMARY:
==16139== definitely lost: 0 bytes in 0 blocks.
==16139== possibly lost: 0 bytes in 0 blocks.
==16139== still reachable: 35,591 bytes in 72 blocks.
==16139== suppressed: 0 bytes in 0 blocks.

Looks quite good considering this advice from the valgrind FAQ:

“still reachable” means your program is probably ok – it didn’t free some memory it could have. This is quite common and often reasonable. Don’t use --show-reachable=yes if you don’t want to see these reports.

There is one remaining bug I can’t figure out:

==16139== Syscall param ioctl(generic) points to uninitialised byte(s)
==16139== at 0x4000832: (within /lib/ld-2.9.so)
==16139== by 0x805362B: src_v4l2_open (src_v4l2.c:795)
==16139== by 0x804F73E: src_open (src.c:110)
==16139== by 0x804C213: fswc_grab (fswebcam.c:1068)
==16139== by 0x804D97C: main (fswebcam.c:2092)

I think the relevant snippet of code is:

int src_v4l2_set_input(src_t *src)
{
        src_v4l2_t *s = (src_v4l2_t *) src->state;
        struct v4l2_input input;
        int count = 0, i = -1;

        if(src->list & SRC_LIST_INPUTS)
        {
                HEAD("--- Avaliable inputs:");

                input.index = count;
                while(!ioctl(s->fd, VIDIOC_ENUMINPUT, &input))
                {
                        MSG("%i: %s", count, input.name);
                        input.index = ++count;
                }
        }

Are there any C-programmers around who can see what’s wrong here?

Found this one too. Should initialize the struct:

int src_v4l2_set_input(src_t *src)
{
        src_v4l2_t *s = (src_v4l2_t *) src->state;
        struct v4l2_input input;
        int count = 0, i = -1;
        memset(&input, 0, sizeof input);
        if(src->list & SRC_LIST_INPUTS)
        {
                HEAD("--- Avaliable inputs:");

                input.index = count;
                while(!ioctl(s->fd, VIDIOC_ENUMINPUT, &input))
                {
                        MSG("%i: %s", count, input.name);
                        input.index = ++count;
                }
        }

fswebcam seems to work now for me, but I suspect there are more leaks in functions I just don’t use. I have contacted the author and asked for a bugfix release.

BTW: Anyone knows another command line tool capable of taking a snapshot from an UVC camera and making use of the functions to adjust the focus?

Addendum: The author of fswebcam - Philip Heron - has published a bugfix release. See: FireStorm.cx - fswebcam.

All known issues are solved. Long live open software!