Message ID | ME2P282MB224207E3DA9DD0B845062C28B4469@ME2P282MB2242.AUSP282.PROD.OUTLOOK.COM |
---|---|
State | New |
Headers | show |
Series | [v3] elf: Add test for locating libraries in root dir (bug 30435) | expand |
On 2023-05-25 10:56, Qixing ksyx Xue wrote: > When dlopen is being called, efforts have been made to improve > future lookup performance. This includes marking a search path > as non-existent using `stat`. However, if the root directory > is given as a search path, there exists a bug which erroneously > marks it as non-existing. > > The bug is reproduced under the following sequence: > > 1. dlopen is called to open a shared library, with at least: > 1) a dependency 'A.so' not directly under the '/' directory > (e.g. /lib/A.so), and > 2) another dependency 'B.so' resides in '/'. > 2. for this bug to reproduce, 'A.so' should be searched *before* 'B.so'. > 3. it first tries to find 'A.so' in /, (e.g. /A.so): > - this will (obviously) fail, > - since it's the first time we have seen the '/' directory, > its 'status' is 'unknown'. > 4. `buf[buflen - namelen - 1] = '\0'` is executed: > - it intends to remove the leaf and its final slash, > - because of the speciality of '/', its buflen == namelen + 1, > - it erroneously clears the entire buffer. > 6. it then calls 'stat' with the empty buffer: > - which will result in an error. > 7. so it marks '/' as 'nonexisting', future lookups will not consider > this path. > 8. while /B.so *does* exist, failure to look it up in the '/' > directory leads to a 'cannot open shared object file' error. > > This patch fixes the bug by preventing 'buflen', an index to put '\0', > from being set to 0, so that the root '/' is always kept. > Relative search paths are always considered as 'existing' so this > wont be affected. > > Writeup by Moody Liu <mooodyhunter@outlook.com> > > Suggested-by: Carlos O'Donell <carlos@redhat.com> > Signed-off-by: Qixing ksyx Xue <qixingxue@outlook.com> Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org> I'll test and commit this shortly. Thanks, Sid > --- > elf/Makefile | 3 +++ > elf/tst-rootdir-lib.c | 23 +++++++++++++++++++ > elf/tst-rootdir.c | 37 +++++++++++++++++++++++++++++++ > elf/tst-rootdir.root/preclean.req | 0 > elf/tst-rootdir.script | 1 + > 5 files changed, 64 insertions(+) > create mode 100644 elf/tst-rootdir-lib.c > create mode 100644 elf/tst-rootdir.c > create mode 100644 elf/tst-rootdir.root/preclean.req > create mode 100644 elf/tst-rootdir.script > > diff --git a/elf/Makefile b/elf/Makefile > index e262f3e6b1..8640138a72 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -325,6 +325,7 @@ static-dlopen-environment = \ > LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn > tst-tls9-static-ENV = $(static-dlopen-environment) > tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment) > +tst-rootdir-ENV = LD_LIBRARY_PATH=/ > > tests += \ > argv0test \ > @@ -506,6 +507,7 @@ tests-container += \ > tst-dlopen-tlsmodid-container \ > tst-pldd \ > tst-preload-pthread-libc \ > + tst-rootdir \ > # tests-container > > test-srcs = \ > @@ -855,6 +857,7 @@ modules-names += \ > tst-relsort1mod1 \ > tst-relsort1mod2 \ > tst-ro-dynamic-mod \ > + tst-rootdir-lib \ > tst-single_threaded-mod1 \ > tst-single_threaded-mod2 \ > tst-single_threaded-mod3 \ > diff --git a/elf/tst-rootdir-lib.c b/elf/tst-rootdir-lib.c > new file mode 100644 > index 0000000000..007c4b0095 > --- /dev/null > +++ b/elf/tst-rootdir-lib.c > @@ -0,0 +1,23 @@ > +/* Simple library for testing locating library in root directories. > + Copyright The GNU Toolchain Authors. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +const char * > +test_func (void) > +{ > + return "Success"; > +} > diff --git a/elf/tst-rootdir.c b/elf/tst-rootdir.c > new file mode 100644 > index 0000000000..83284ea59e > --- /dev/null > +++ b/elf/tst-rootdir.c > @@ -0,0 +1,37 @@ > +/* Test code for locating libraries in root directories. > + Copyright The GNU Toolchain Authors. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +#include <support/test-driver.h> > +#include <support/check.h> > +#include <dlfcn.h> > +#include <assert.h> > + > +static int > +do_test (void) > +{ > + void *handle = dlopen ("libtest.so", RTLD_LAZY); > + TEST_VERIFY_EXIT (handle != NULL); > + typedef const char *(test_func_t) (void); > + test_func_t *func = dlsym (handle, "test_func"); > + assert (func != NULL); > + TEST_COMPARE_STRING (func (), "Success"); > + dlclose (handle); > + return 0; > +} > + > +#include <support/test-driver.c> > diff --git a/elf/tst-rootdir.root/preclean.req b/elf/tst-rootdir.root/preclean.req > new file mode 100644 > index 0000000000..e69de29bb2 > diff --git a/elf/tst-rootdir.script b/elf/tst-rootdir.script > new file mode 100644 > index 0000000000..f852c0ec34 > --- /dev/null > +++ b/elf/tst-rootdir.script > @@ -0,0 +1 @@ > +cp $B/elf/tst-rootdir-lib.so /libtest.so
diff --git a/elf/Makefile b/elf/Makefile index e262f3e6b1..8640138a72 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -325,6 +325,7 @@ static-dlopen-environment = \ LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn tst-tls9-static-ENV = $(static-dlopen-environment) tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment) +tst-rootdir-ENV = LD_LIBRARY_PATH=/ tests += \ argv0test \ @@ -506,6 +507,7 @@ tests-container += \ tst-dlopen-tlsmodid-container \ tst-pldd \ tst-preload-pthread-libc \ + tst-rootdir \ # tests-container test-srcs = \ @@ -855,6 +857,7 @@ modules-names += \ tst-relsort1mod1 \ tst-relsort1mod2 \ tst-ro-dynamic-mod \ + tst-rootdir-lib \ tst-single_threaded-mod1 \ tst-single_threaded-mod2 \ tst-single_threaded-mod3 \ diff --git a/elf/tst-rootdir-lib.c b/elf/tst-rootdir-lib.c new file mode 100644 index 0000000000..007c4b0095 --- /dev/null +++ b/elf/tst-rootdir-lib.c @@ -0,0 +1,23 @@ +/* Simple library for testing locating library in root directories. + Copyright The GNU Toolchain Authors. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +const char * +test_func (void) +{ + return "Success"; +} diff --git a/elf/tst-rootdir.c b/elf/tst-rootdir.c new file mode 100644 index 0000000000..83284ea59e --- /dev/null +++ b/elf/tst-rootdir.c @@ -0,0 +1,37 @@ +/* Test code for locating libraries in root directories. + Copyright The GNU Toolchain Authors. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <support/test-driver.h> +#include <support/check.h> +#include <dlfcn.h> +#include <assert.h> + +static int +do_test (void) +{ + void *handle = dlopen ("libtest.so", RTLD_LAZY); + TEST_VERIFY_EXIT (handle != NULL); + typedef const char *(test_func_t) (void); + test_func_t *func = dlsym (handle, "test_func"); + assert (func != NULL); + TEST_COMPARE_STRING (func (), "Success"); + dlclose (handle); + return 0; +} + +#include <support/test-driver.c> diff --git a/elf/tst-rootdir.root/preclean.req b/elf/tst-rootdir.root/preclean.req new file mode 100644 index 0000000000..e69de29bb2 diff --git a/elf/tst-rootdir.script b/elf/tst-rootdir.script new file mode 100644 index 0000000000..f852c0ec34 --- /dev/null +++ b/elf/tst-rootdir.script @@ -0,0 +1 @@ +cp $B/elf/tst-rootdir-lib.so /libtest.so
When dlopen is being called, efforts have been made to improve future lookup performance. This includes marking a search path as non-existent using `stat`. However, if the root directory is given as a search path, there exists a bug which erroneously marks it as non-existing. The bug is reproduced under the following sequence: 1. dlopen is called to open a shared library, with at least: 1) a dependency 'A.so' not directly under the '/' directory (e.g. /lib/A.so), and 2) another dependency 'B.so' resides in '/'. 2. for this bug to reproduce, 'A.so' should be searched *before* 'B.so'. 3. it first tries to find 'A.so' in /, (e.g. /A.so): - this will (obviously) fail, - since it's the first time we have seen the '/' directory, its 'status' is 'unknown'. 4. `buf[buflen - namelen - 1] = '\0'` is executed: - it intends to remove the leaf and its final slash, - because of the speciality of '/', its buflen == namelen + 1, - it erroneously clears the entire buffer. 6. it then calls 'stat' with the empty buffer: - which will result in an error. 7. so it marks '/' as 'nonexisting', future lookups will not consider this path. 8. while /B.so *does* exist, failure to look it up in the '/' directory leads to a 'cannot open shared object file' error. This patch fixes the bug by preventing 'buflen', an index to put '\0', from being set to 0, so that the root '/' is always kept. Relative search paths are always considered as 'existing' so this wont be affected. Writeup by Moody Liu <mooodyhunter@outlook.com> Suggested-by: Carlos O'Donell <carlos@redhat.com> Signed-off-by: Qixing ksyx Xue <qixingxue@outlook.com> --- elf/Makefile | 3 +++ elf/tst-rootdir-lib.c | 23 +++++++++++++++++++ elf/tst-rootdir.c | 37 +++++++++++++++++++++++++++++++ elf/tst-rootdir.root/preclean.req | 0 elf/tst-rootdir.script | 1 + 5 files changed, 64 insertions(+) create mode 100644 elf/tst-rootdir-lib.c create mode 100644 elf/tst-rootdir.c create mode 100644 elf/tst-rootdir.root/preclean.req create mode 100644 elf/tst-rootdir.script