[LAD] ladspa: dlopen failed for tap ladspa plugins

Cedric Roux sed at free.fr
Sun May 18 13:06:23 UTC 2014

On 05/18/2014 01:11 PM, Cedric Roux wrote:
> On 05/18/2014 12:57 PM, Cedric Roux wrote:
>> but this is really strange that the tap .so files
>> are not linked against the libm.
> okay, I downloaded KXStudio_14.04b_64bit.iso and indeed
> all the /usr/lib/ladspa/tap_*.so in it are *not* linked against
> the libm.
> The official tap plugins Makefile for 0.7.2 has the -lm thing.
> You should send a bug report to the kxstudio team. It looks like
> a bug to me.

and to continue a bit on the subject, there is a very funny and weird
behavior with dlopen(plugpath, RTLD_LAZY);
I tried with tap_rotspeak.so.
Let me extract the funky behavior for the completeness of this email.
Let's write tap.c, which does something similar to tap_rotspeak in its
_init and _fini functions.

<< EOF
#include <stdlib.h>
#include <math.h>

/* in the plugin, a is a struct and b in inside a
  * and may have whatever value after the malloc of a
  * to simulate this, we set b to 1 (we want a crash!)
float *a;
float *b = (float *)1;

/* dlopen will call _init before it returns to the caller */
void _init()
   int i;
   float table[10];

   a = malloc(10*sizeof(float)); if (!a) abort();

   /* we never link with -lm, so cos fails and dlopen somehow
    * "aborts" _init at this point and right after calls _fini
    * (I didn't check the details in dlopen)
   for (i = 0; i < 10; i++) table[i] = cos(i);
   a[0] = table[0]; /* gcc shut up */

   /* this is never reached */
   b = malloc(10*sizeof(float*)); if (!b) abort();

/* so this function is also called by dlopen because _init
  * called "cos" and we didn't link against libm
void _fini()
   if (!a) return;
   /* we come here because a is not 0, it was malloc'd */
   /* but b was not, so guess what happens... */

And here comes my-bug.c
<< EOF
#include <stdio.h>
#include <dlfcn.h>
int main (int argc, char ** argv)
         char plugpath [] = "./tap.so";
         void * pvPluginHandle = dlopen(plugpath, RTLD_LAZY);
         if (pvPluginHandle) dlclose (pvPluginHandle);
         else printf ("dlopen failed: %s (dlerror: %s)\n",
                        plugpath, dlerror());
         printf("dlerror? %s\n", dlerror());
         return 0;

compile with:
gcc -nostartfiles -shared -fPIC tap.c -o tap.so -Wall -g
gcc -Wall -o my-bug my-bug.c -ldl -g

and have fun.

Here is a backtrace in gdb:

Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x1) at malloc.c:2891
2891    malloc.c: No such file or directory.
(gdb) bt
#0  __GI___libc_free (mem=0x1) at malloc.c:2891
#1  0x00007ffff762e566 in _fini () at tap.c:23
#2  0x00007ffff7df0739 in _dl_close_worker (map=<optimized out>)
     at dl-close.c:277
#3  0x00007ffff7def42e in _dl_open (file=0x7fffffffe450 "./tap.so",
     mode=<optimized out>, caller_dlopen=<optimized out>, 
nsid=<optimized out>,
     argc=1, argv=0x7fffffffe558, env=0x7fffffffe568) at dl-open.c:686
#4  0x00007ffff7bda02b in dlopen_doit (a=a at entry=0x7fffffffe410) at 
#5  0x00007ffff7deb7c4 in _dl_catch_error (
     objname=0x7ffff7ddc0f0 <last_result+16>,
     errstring=0x7ffff7ddc0f8 <last_result+24>,
     mallocedp=0x7ffff7ddc0e8 <last_result+8>,
     operate=0x7ffff7bd9fd0 <dlopen_doit>, args=0x7fffffffe410) at 
#6  0x00007ffff7bda5dd in _dlerror_run (
     operate=operate at entry=0x7ffff7bd9fd0 <dlopen_doit>,
     args=args at entry=0x7fffffffe410) at dlerror.c:163
#7  0x00007ffff7bda0c1 in __dlopen (file=<optimized out>, 
mode=<optimized out>)
     at dlopen.c:87
#8  0x00000000004006cf in main (argc=1, argv=0x7fffffffe558) at my-bug.c:6

It looks very similar to the one of Nikita:

(gdb) backtrace
#0  0x00007ffff762f425 in __GI_raise (sig=<optimized out>) at 
#1  0x00007ffff7632b8b in __GI_abort () at abort.c:91
#2  0x00007ffff766d39e in __libc_message (do_abort=2, fmt=0x7ffff7777748 
"*** glibc detected *** %s: %s: 0x%s ***\n") at 
#3  0x00007ffff7677b96 in malloc_printerr (action=3, str=0x7ffff77778a8 
"double free or corruption (out)", ptr=<optimized out>) at malloc.c:5039
#4  0x00007ffff61b3957 in delete_descriptor () from 
#5  0x00007ffff7dee70e in _dl_close_worker (map=<optimized out>) at 
#6  0x00007ffff7ded3b4 in _dl_open (file=0x6217e0 
"/usr/lib/ladspa/tap_reflector.so", mode=<optimized out>, 
caller_dlopen=0x400c3e, nsid=<optimized out>, argc=1, argv=<optimized 
out>, env=0x7fffffffd998) at dl-open.c:664
#7  0x00007ffff7bd6f26 in dlopen_doit (a=0x7fffffffd7b0) at dlopen.c:67
#8  0x00007ffff7de9176 in _dl_catch_error (objname=0x60b0b0, 
errstring=0x60b0b8, mallocedp=0x60b0a8, operate=0x7ffff7bd6ec0 
<dlopen_doit>, args=0x7fffffffd7b0) at dl-error.c:178
#9  0x00007ffff7bd752f in _dlerror_run (operate=0x7ffff7bd6ec0 
<dlopen_doit>, args=0x7fffffffd7b0) at dlerror.c:164
#10 0x00007ffff7bd6fc1 in __dlopen (file=<optimized out>, 
mode=<optimized out>) at dlopen.c:88
#11 0x0000000000400c3e in LADSPADirectoryPluginSearch 
(pcDirectory=0x603040 "/usr/lib/ladspa", fCallbackFunction=0x400a5c 
<describePluginLibrary>) at ladspa/search.c:64
#12 0x0000000000400db9 in LADSPAPluginSearch (fCallbackFunction=0x400a5c 
<describePluginLibrary>) at ladspa/search.c:118
#13 0x0000000000400afd in main (argc=1, argv=0x7fffffffd988) at main.c:32

It would be possible to modify the tap plugins to avoid such behaviors.
Simply be sure in the _init to set every pointer to 0 at beginning or
just after a malloc of a struct. And test against 0 in the _fini before
any call to free.

But the libm should have been linked against the plugin in the first
place, so...

Another solution is to use RTLD_NOW, which might be problematic.

Anyway, so be it. Sorry for the noise and have a nice sunday!

And plugin writers, be very careful with your _init/_fini stuff...

More information about the Linux-audio-dev mailing list