| Summary: | dlopen/dlclose with ctors/dtors and on_exit handler -> using uninitialized memory | ||
|---|---|---|---|
| Product: | uClibc | Reporter: | Ronald Wahl <rwahl> |
| Component: | Shared Library Support | Assignee: | Carmelo Amoroso <carmelo.amoroso> |
| Status: | REOPENED --- | ||
| Severity: | normal | CC: | uclibc-cvs |
| Priority: | P3 | ||
| Version: | 0.9.31 | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | Linux | ||
| Host: | Target: | ||
| Build: | |||
| Attachments: | Better patch fixing the uninitialized value issue. | ||
Created attachment 1399 [details]
Better patch fixing the uninitialized value issue.
Just noticed that my original patch was probably not optimal. This one is better I think.
I cannot reproduce this with 0.9.31-rc1 nor current master. Please reopen the bug if you can reproduce it with 0.9.30.3 thanks, Well unfortunately I cannot test this quickly. But probably you'll at least see the logical bug (or show me that I'm wrong):
In the following I talk about libc/stdlib/_atexit.c
(1) When registering an exit handler with on_exit or __cxa_atexit a new exit
slot is acquired in __new_exitfn - in the end it is allocated with realloc
there and this memory is actually not initialized.
(2) When a handler is registered with __cxa_atexit we initialize
efp->funcs.cxa_atexit.dso_handle (note efp->funcs is actually a union!)
(3) When a handler is registered with on_exit the
efp->funcs.cxa_atexit.dso_handle field is not initialized because it is in
the wrong part of the union which is perfecly ok.
(4) Now look at the __cxa_finalize function: We iterate over the exit handlers
and happily assume that efp->funcs.cxa_atexit.dso_handle is initialized. But
this is only true for slots of type ef_cxa_atexit. If one registered an
on_exit handler it will access uninitialized memory.
I'm not sure why you can't reproduce this. Either the uClibc has this __cxa_atexit stuff disabled, the toolchain does not support it, valgrind surpresses the output or whatever.
So can you tell me if I missed something or is this really a bug?
As I already suspected - the bug is still present in 0.9.31. Maybe this is relevant as well: uClibc must be configured with UCLIBC_DYNAMIC_ATEXIT=y and the compiler must be compiled to use cxa_atexit via --enable-__cxa_atexit configure option. |
The appended test program registers an exit_handler with on_exit() and calls dlopen and dlclose on a plugin (can be compiled using an empty source file). It only needs to be compiled with a toolchain supporting ctors/dtors using __cxa_exit. Valgrind will report: ==1264== Conditional jump or move depends on uninitialised value(s) ==1264== at 0x405675F: __cxa_finalize (_atexit.c:203) ==1264== by 0x4463354: (within /lib/test_plugin.so) ==1264== by 0x446344B: (within /lib/test_plugin.so) ==1264== by 0x40149F4: do_dlclose (libdl.c:545) ==1264== by 0x8048522: main (dlclose_atexit_test_main.c:17) This is because on_exit does not initialize the dso_handle to NULL in on_exit(). The memory of the structure containing dso_handle is allocated with realloc() and is uninitialized. Maybe this can crash the program on dlclose() or even be used to call arbitrary code. Current uClibc seem to be affected as well - at least the code looks the same. This patch fixes this: Index: libc/stdlib/_atexit.c =================================================================== --- libc/stdlib/_atexit.c (revision 198509) +++ libc/stdlib/_atexit.c (revision 198510) @@ -145,6 +145,7 @@ efp->funcs.on_exit.func = func; efp->funcs.on_exit.arg = arg; + efp->funcs.cxa_atexit.dso_handle = NULL; /* assign last for thread safety, since we're now unlocked */ efp->type = ef_on_exit; --8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<-- #include <assert.h> #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> static void exit_handler(int i, void* arg) { (void)i; (void)arg; } int main(void) { on_exit(exit_handler, NULL); void* dl_handle = dlopen("test_plugin.so", RTLD_LAZY); assert(dl_handle); dlclose(dl_handle); return 0; } --8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--