Problem with atexit and _dl_fini

Nat! nat@mulle-kybernetik.com
Sun Jun 9 20:59:00 GMT 2019


Another datapoint to support my claim that _dl-fini breaks atexit. This 
time its very easy to reproduce ;)

Here 's the README.md from the Github Repo 
https://github.com/mulle-nat/atexit-breakage-linux


```

# Shows another breakage involving `atexit` on linux

Here the `atexit` callback is invoked mistakenly multiple times.


## Build

Build with [mulle-make](//github.com/mulle-sde/mulle-make) or alternatively :

```
(
    mkdir build &&
    cd build &&
    cmake .. &&
    make
)
```

## Run

Use `ldd` to trigger the misbehaviour:

```
LD_PRELOAD="${PWD}/build/libld-preload.so" ldd ./build/main
```

## Output

```
load
unload
unload
unload
    linux-vdso.so.1 (0x00007ffd2b2bd000)
    /home/src/srcO/mulle-core/mulle-testallocator/research/ld-preload/build/libld-preload.so (0x00007f83853c1000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f838518c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f83853cd000)
unload
unload
```


Ciao
    Nat!


On 19.05.19 21:37, Nat! wrote:
>
> On 19.05.19 18:23, Florian Weimer wrote:
>> * Nat!:
>>
>>> So my problem is, that I observe that my atexit calls are not executed
>>> in the correct order.
>>>
>>> i.e. atexit( a); atexit( b);  should result in b(), a() being called in
>>> that order. To quote the man page,
>>>
>>> "the registered functions are invoked in reverse order".
>>>
>>>
>>> When I register with `atexit` I can see my functions being added
>>> properly within `__internal_atexit` in the
>>>
>>> correct order. Finally after my functions, the elf-loader ? also adds
>>> itself there. So it is being called first by
>>>
>>> `__run_exit_handlers`.
>>>
>>>
>>> Then comes the part where it goes wrong. I registered my two function
>>> with `__internal_atexit`, but for some reason
>>>
>>> `_dl_fini` is calling `__cxa_finalize` and that is calling the wrong
>>> function first.
>> When atexit is called from a DSO, glibc calls the registered function
>> before the DSO is unloaded.  This choice was made because after
>> unloading, the function pointer becomes invalid.
>>
>> I haven't checked, but I suspect atexit still works this way even if
>> it doesn't have to (because the DSO is never unloaded).
>>
>
> I understand, but the behavior is wrong :) The C standard (or the C++ 
> standard for this matter) 
> http://www.cplusplus.com/reference/cstdlib/atexit/ states that
>
>
> ```
>
> If more than one atexit function has been specified by different calls 
> to this function, they are all executed in reverse order as a stack 
> (i.e. the last function specified is the first to be executed at exit).
>
> ```
>
> I think its been shown that glibc can violate this C standard, so for 
> me the argument would be over here already. That one should unwind in 
> the reverse order is, I assume, not a interesting discussion topic. 
> Currently atexit as a reliable mechanism is broken.
>
>
> But I also don't think the way this is currently handled in glibc, 
> can't be of much use to anyone.
>
> Case 1: a regular exe linked with shared libraries, nothing gets 
> unloaded at exit, so what's the point ?
>
> Case 2: someone manually unloads a shared library, that contains 
> atexit code. The bug is either using `atexit` for a shared library 
> that gets unloaded, or unloading a shared library that contains atexit 
> code. But it's not really glibcs business IMO.
>
> Case 3: some automatism unloads shared libraries. Then the automatism 
> should check if atexit code is affected and not unload, because the 
> shared library is still clearly needed. It's a bug in the automatism.
>
> If one was hellbent on trying to support atexit for unloading shared 
> libraries, an atexit contained in a shared library should up the 
> reference count of the shared library during the atexit call and 
> decrement after the callback has executed.
>
> Ciao
>
>    Nat!
>
>



More information about the Libc-help mailing list