ldso libdl dl_cleanup issue - Possible Fix

Kevin Day thekevinday at gmail.com
Thu May 15 15:54:11 UTC 2008


I distantly remember swinging at everything in sight trying to find
and resolve this problem.
This happened on a number of projects and is shown by valgrind, but
I've historically got from uClibc that it was "X Project"'s problem
and when I went to "X Project" they said it was uClibc's problem.

So, I was recently looking at the same problem again, this time with
abiword as the "X project".
I got the usual:
==2807== Invalid read of size 4
==2807==    at 0x4600D8C: dl_cleanup (in /lib/libdl-0.9.28.so)
==2807==    by 0x4000ADC: (within /lib/ld-uClibc-0.9.28.so)
==2807==    by 0x4B4E021: exit (in /lib/libuClibc-0.9.28.so)
==2807==    by 0x4B2C46D: __uClibc_main (in /lib/libuClibc-0.9.28.so)
==2807==    by 0x8171D1F: _start (in /bin/abiword)
==2807==  Address 0x5a0f96c is 4 bytes inside a block of size 24 free'd
==2807==    at 0x401933C: free (in
/toolchain/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==2807==    by 0x4600C8A: (within /lib/libdl-0.9.28.so)
==2807==    by 0x4600D8B: dl_cleanup (in /lib/libdl-0.9.28.so)
==2807==    by 0x4000ADC: (within /lib/ld-uClibc-0.9.28.so)
==2807==    by 0x4B4E021: exit (in /lib/libuClibc-0.9.28.so)
==2807==    by 0x4B2C46D: __uClibc_main (in /lib/libuClibc-0.9.28.so)
==2807==    by 0x8171D1F: _start (in /bin/abiword)

again, dl_cleanup seems to be reading unallocated memory according to valgrind.
After recompiling uClibc with debug symbols, I noticed that it was
caused by two lines in: uClibc-0.9.28.3/ldso/libdl/libdl.c
Line 136: do_dlclose(d, 1);
and in depth, Line 578: free(handle);

Considering that handle was being used during this entire process it
should be allocated..

I decided to look at the logic of the dl_cleanup function and the
do_dlclose function.

Here is a simple explanation of the problem I see:

dl_cleanup will call do_dlclose with the pointer "d"
Inside of do_dlclose, "d" will ultimately get free'd
After do_dlclose finishes executing, the for loop from dl_cleanup will
then take the pointer "d" and move it to the next pointer.
And this is where I am confused, if "d" was free'd by the do_dlclose
function, then how can it use the "d" pointer to iterate along the
next location?
So it makes sense that d(NULL)->(Next Pointer) generates an invalid
read according to valgrind.

To test this, I changed the contents of dl_cleanup to:
{
    struct dyn_elf *d = _dl_handles;
    struct dyn_elf *n;

    if (d){
        n = d->next;
        do_dlclose(d, 1);

        while (n){
            d = n;
            n = d->next;
            do_dlclose(d, 1);
        }
    }
}

I then installed libdl.so, and re-ran valgrind on abiword and the
invalid read no longer appeared.

Does this sound right or is there some loophole in my logic or some
aspect of do_dlclose(..) I am missing?

-- 
Kevin Day



More information about the uClibc mailing list