Bug 19773 - replacing .so which was opened and closed, leads to segfault on next dlopen/dlsym
Summary: replacing .so which was opened and closed, leads to segfault on next dlopen/d...
Status: RESOLVED INVALID
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-03-06 08:16 UTC by Homutov Vladimir
Modified: 2016-03-10 15:59 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
minimal testcase reproducing issue (852 bytes, application/gzip)
2016-03-06 08:16 UTC, Homutov Vladimir
Details
updated version of testcase to reproduce case2 (797 bytes, application/gzip)
2016-03-06 08:24 UTC, Homutov Vladimir
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Homutov Vladimir 2016-03-06 08:16:18 UTC
Created attachment 9067 [details]
minimal testcase reproducing issue

in pseudocode:

handle = dlopen(foo.so)
dlclose(foo.so)

now replace foo.so with another of itself, which was in another directory,
by external process

handle = dlopen(foo.so) // same handle returned
sym = dlsym(handle, blah) <-- CRASH !


The complete C example is in the attached tarball.
just run 'make fail' to get segfault.
Note that 'make pass' will work normally, despite the ONLY difference
is the path, used to run binary: './bin/dltest' vs './dltest'

Also note that a different sequence of dlopen/dlclose will lead to crash in another place (swap #if condition in example to see)

This was tested on centos6, ubuntu 15.04 and gentoo (glibc 2.21) - same behaviour everywhere.
Also, I've tested with freebsd10 and no issues there with this code.
Comment 1 Homutov Vladimir 2016-03-06 08:20:05 UTC
Case 1:
Core was generated by `./dltest'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000000575 in ?? ()
(gdb) bt
#0  0x0000000000000575 in ?? ()
#1  0x00007f8efbd73c7e in _dl_close_worker () from /lib64/ld-linux-x86-64.so.2
#2  0x00007f8efbd7471e in _dl_close () from /lib64/ld-linux-x86-64.so.2
#3  0x00007f8efbd6ee10 in _dl_catch_error () from /lib64/ld-linux-x86-64.so.2
#4  0x00007f8efbb5d6b5 in _dlerror_run () from /lib64/libdl.so.2
#5  0x00007f8efbb5d0b1 in dlclose () from /lib64/libdl.so.2
#6  0x00000000004008b1 in main ()

Case 2:
Core was generated by `./dltest'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007f9f165cfd42 in check_match () from /lib64/ld-linux-x86-64.so.2
(gdb) bt
#0  0x00007f9f165cfd42 in check_match () from /lib64/ld-linux-x86-64.so.2
#1  0x00007f9f165d067a in do_lookup_x () from /lib64/ld-linux-x86-64.so.2
#2  0x00007f9f165d0ab6 in _dl_lookup_symbol_x () from /lib64/ld-linux-x86-64.so.2
#3  0x00007f9f161423b5 in do_sym () from /lib64/libc.so.6
#4  0x00007f9f163c40da in dlsym_doit () from /lib64/libdl.so.2
#5  0x00007f9f165d5e10 in _dl_catch_error () from /lib64/ld-linux-x86-64.so.2
#6  0x00007f9f163c46b5 in _dlerror_run () from /lib64/libdl.so.2
#7  0x00007f9f163c4128 in dlsym () from /lib64/libdl.so.2
#8  0x000000000040095f in load_module ()
#9  0x00000000004009f3 in main ()
Comment 2 Homutov Vladimir 2016-03-06 08:24:08 UTC
Created attachment 9068 [details]
updated version of testcase to reproduce case2

added an actual symbol to foo.c to trigger different behaviour in main test
Comment 3 Homutov Vladimir 2016-03-06 19:17:25 UTC
what happens is that cp truncates so file, so problem can be avoided if use install instead (removes file, than creates new).

regarding running with different path - this is my fault, in second case wrong path prevented so from copy, so just ignore that part.

anyway, although it seems like this behaviour is known (problems if shared library was changed during use), and solved for binaries (text file in use if you are trying to copy), this looks like a bug:

I would expect after dlclose() anything can happen with binary and next dlopen() will give me new handle.
Comment 4 Florian Weimer 2016-03-10 15:59:56 UTC
There may be two separate issues here:

If the file backing a mapping is truncated, accessing the truncated part of the mapping will result in SIGBUS.  This is probably not the case here because the signal you report is SIGSEGV, not SIGBUS.

dlopen (and the rest of the dynamic linker) does not verify internal consistency of ELF files.  This is a deliberate design decision, to improve performance.  As a result, unmapped memory can be accessed if the file size does not match expectations.