Bug 17833 - STB_GNU_UNIQUE symbols can lead to wrong behaviour of the dynamic loader
Summary: STB_GNU_UNIQUE symbols can lead to wrong behaviour of the dynamic loader
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.21
: P2 critical
Target Milestone: 2.22
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on: 18778
Blocks:
  Show dependency treegraph
 
Reported: 2015-01-12 20:42 UTC by Pavel Kopyl
Modified: 2015-08-06 19:44 UTC (History)
5 users (show)

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


Attachments
Test case (6.58 KB, application/x-bzip2)
2015-01-12 20:42 UTC, Pavel Kopyl
Details
Patch to fix this problem (2.81 KB, text/plain)
2015-06-17 21:58 UTC, Pavel Kopyl
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Pavel Kopyl 2015-01-12 20:42:31 UTC
Created attachment 8064 [details]
Test case

I have a shared library that contains both undefined and unique symbols. Then I try to call the following sequence of dlopen:

1. dlopen("./libfoo.so", RTLD_NOW)
2. dlopen("./libfoo.so", RTLD_LAZY | RTLD_GLOBAL)

Firs dlopen call failes because of undefined symbols, but STB_GNU_UNIQUE ones lead to set-up of DF_1_NODELETE flag and hence block library in the memory.
The library goes into inconsistent state as several structures remain uninitialized. For instance, relocations in the GOT table were not performed. 
By the time of second dlopen call this library looks like as it would be fully initialized but this is not true: any call through incorrect GOT table leads to segmentation fault.
On some systems this inconsistency breaks assertions in the dynamic linker.

This bug appears on different targets(ARMv7, x86_64) for different glibc versions(2.15, 2.18, 2.20.90)

Here is an example:

#ifdef EXE
#include <stdio.h>
#include <dlfcn.h>

int main() {
	void *handle;

	handle = dlopen("./libfoo.so", RTLD_NOW);
	if (handle == NULL) {
		fprintf(stderr, "Can not dlopen libfoo.so at 1st time\n");
	}

	handle = dlopen("./libfoo.so", RTLD_LAZY | RTLD_GLOBAL);
	if (handle == NULL) {
		fprintf(stderr, "Can not open libfoo.so at 2nd time\n");
	}

	dlclose(handle);

	return 0;
}
#else
/* doo - is undefined symbol in libfoo.so
Only doo@plt instance exists, but no real symbol. */
extern int doo();

inline int goo() {
	static int xyz;
	return xyz++;
}
	
int boo() {
	return goo() + doo();
}
#endif //_FOO_

g++ -g -O2 -fPIC -shared test_case.cc -o libfoo.so
g++ -g -O2 -DEXE test_case.cc -ldl

./a.out
Can not dlopen libfoo.so at 1st time
Inconsistency detected by ld.so: dl-close.c: 764: _dl_close: Assertion `map->l_init_called' failed!
Comment 1 Pavel Kopyl 2015-06-17 21:58:44 UTC
Created attachment 8372 [details]
Patch to fix this problem
Comment 2 Sourceware Commits 2015-07-07 18:08:01 UTC
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  02d5e5d94a78d32e940dfb3b58ab7f06c31b0f76 (commit)
      from  890b7a4b33d482b5c768ab47d70758b80227e9bc (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=02d5e5d94a78d32e940dfb3b58ab7f06c31b0f76

commit 02d5e5d94a78d32e940dfb3b58ab7f06c31b0f76
Author: Pavel Kopyl <p.kopyl@samsung.com>
Date:   Tue Jul 7 18:45:46 2015 +0300

    Add forced deletion support to _dl_close_worker
    
    https://sourceware.org/bugzilla/show_bug.cgi?id=17833
    
    I've a shared library that contains both undefined and unique symbols.
    Then I try to call the following sequence of dlopen:
    
    1. dlopen("./libfoo.so", RTLD_NOW)
    2. dlopen("./libfoo.so", RTLD_LAZY | RTLD_GLOBAL)
    
    First dlopen call terminates with error because of undefined symbols,
    but STB_GNU_UNIQUE ones set DF_1_NODELETE flag and hence block library
    in the memory.
    
    The library goes into inconsistent state as several structures remain
    uninitialized. For instance, relocations for GOT table were not performed.
    
    By the time of second dlopen call this library looks like as it would be
    fully initialized but this is not true: any call through incorrect GOT
    table leads to segmentation fault.  On some systems this inconsistency
    triggers assertions in the dynamic linker.
    
    This patch adds a parameter to _dl_close_worker to implement forced object
    deletion in case of dlopen() failure:
    
    1. Clears DF_1_NODELETE bit if forced, to allow library to be removed from
    memory.
    2. For each unique symbol that is defined in this object clears
    appropriate entry in _ns_unique_sym_table.
    
    	[BZ #17833]
    	* elf/Makefile (tests): Add tst-nodelete.
    	(modules-names): Add tst-nodelete-uniquemod.
    	(tst-nodelete-uniquemod.so-no-z-defs): New.
    	(tst-nodelete-rtldmod.so-no-z-defs): Likewise.
    	(tst-nodelete-zmod.so-no-z-defs): Likewise.
    	($(objpfx)tst-nodelete): Likewise.
    	($(objpfx)tst-nodelete.out): Likewise.
    	(LDFLAGS-tst-nodelete): Likewise.
    	(LDFLAGS-tst-nodelete-zmod.so): Likewise.
    	* elf/dl-close.c (_dl_close_worker): Add a parameter to
    	implement forced object deletion.
    	(_dl_close): Pass false to _dl_close_worker.
    	* elf/dl-open.c (_dl_open): Pass true to _dl_close_worker.
    	* elf/tst-nodelete.cc: New file.
    	* elf/tst-nodeletelib.cc: Likewise.
    	* elf/tst-znodeletelib.cc: Likewise.
    	* include/dlfcn.h (_dl_close_worker): Add a new parameter.

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                     |   22 +++++++++++++++++
 NEWS                          |   26 ++++++++++----------
 elf/Makefile                  |   17 ++++++++++++-
 elf/dl-close.c                |   33 +++++++++++++++++++++++++-
 elf/dl-open.c                 |    2 +-
 elf/tst-nodelete-rtldmod.cc   |    6 +++++
 elf/tst-nodelete-uniquemod.cc |   14 +++++++++++
 elf/tst-nodelete-zmod.cc      |    6 +++++
 elf/tst-nodelete.cc           |   51 +++++++++++++++++++++++++++++++++++++++++
 elf/tst-znodelete-zlib.cc     |    6 +++++
 include/dlfcn.h               |    3 +-
 11 files changed, 167 insertions(+), 19 deletions(-)
 create mode 100644 elf/tst-nodelete-rtldmod.cc
 create mode 100644 elf/tst-nodelete-uniquemod.cc
 create mode 100644 elf/tst-nodelete-zmod.cc
 create mode 100644 elf/tst-nodelete.cc
 create mode 100644 elf/tst-znodelete-zlib.cc
Comment 3 Pavel Kopyl 2015-07-10 14:50:35 UTC
Fixed for 2.22.