diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-04-11 18:12:00 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-04-23 18:13:08 -0300 |
commit | 1a4c27355e146b6d8cc6487b998462c7fdd1048f (patch) | |
tree | f4e0729fa653ce0724858156b63e53ab7a1b87e6 /elf/tst-pldd.c | |
parent | 2d398aa272648a6078b56899db84f86f7b3bdf39 (diff) | |
download | glibc-1a4c27355e146b6d8cc6487b998462c7fdd1048f.tar.gz |
elf: Fix pldd (BZ#18035)
Since 9182aa67994 (Fix vDSO l_name for GDB's, BZ#387) the initial link_map
for executable itself and loader will have both l_name and l_libname->name
holding the same value due:
elf/dl-object.c
95 new->l_name = *realname ? realname : (char *) newname->name + libname_len - 1;
Since newname->name points to new->l_libname->name.
This leads to pldd to an infinite call at:
elf/pldd-xx.c
203 again:
204 while (1)
205 {
206 ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset);
228 /* Try the l_libname element. */
229 struct E(libname_list) ln;
230 if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln))
231 {
232 name_offset = ln.name;
233 goto again;
234 }
Since the value at ln.name (l_libname->name) will be the same as previously
read. The straightforward fix is just avoid the check and read the new list
entry.
I checked also against binaries issues with old loaders with fix for BZ#387,
and pldd could dump the shared objects.
Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu, and
powerpc64le-linux-gnu.
[BZ #18035]
* elf/Makefile (tests-container): Add tst-pldd.
* elf/pldd-xx.c: Use _Static_assert in of pldd_assert.
(E(find_maps)): Avoid use alloca, use default read file operations
instead of explicit LFS names, and fix infinite loop.
* elf/pldd.c: Explicit set _FILE_OFFSET_BITS, cleanup headers.
(get_process_info): Use _Static_assert instead of assert, use default
directory operations instead of explicit LFS names, and free some
leadek pointers.
* elf/tst-pldd.c: New file.
Diffstat (limited to 'elf/tst-pldd.c')
-rw-r--r-- | elf/tst-pldd.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c new file mode 100644 index 0000000000..ed19cedd05 --- /dev/null +++ b/elf/tst-pldd.c @@ -0,0 +1,118 @@ +/* Basic tests for pldd program. + Copyright (C) 2019 Free Software Foundation, Inc. + 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 <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <libgen.h> +#include <stdbool.h> + +#include <array_length.h> +#include <gnu/lib-names.h> + +#include <support/subprocess.h> +#include <support/capture_subprocess.h> +#include <support/check.h> + +static void +target_process (void *arg) +{ + pause (); +} + +/* The test runs in a container because pldd does not support tracing + a binary started by the loader iself (as with testrun.sh). */ + +static int +do_test (void) +{ + /* Create a copy of current test to check with pldd. */ + struct support_subprocess target = support_subprocess (target_process, NULL); + + /* Run 'pldd' on test subprocess. */ + struct support_capture_subprocess pldd; + { + /* Three digits per byte plus null terminator. */ + char pid[3 * sizeof (uint32_t) + 1]; + snprintf (pid, array_length (pid), "%d", target.pid); + + const char prog[] = "/usr/bin/pldd"; + + pldd = support_capture_subprogram (prog, + (char *const []) { (char *) prog, pid, NULL }); + + support_capture_subprocess_check (&pldd, "pldd", 0, sc_allow_stdout); + } + + /* Check 'pldd' output. The test is expected to be linked against only + loader and libc. */ + { + pid_t pid; + char buffer[512]; +#define STRINPUT(size) "%" # size "s" + + FILE *out = fmemopen (pldd.out.buffer, pldd.out.length, "r"); + TEST_VERIFY (out != NULL); + + /* First line is in the form of <pid>: <full path of executable> */ + TEST_COMPARE (fscanf (out, "%u: " STRINPUT (512), &pid, buffer), 2); + + TEST_COMPARE (pid, target.pid); + TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0); + + /* It expects only one loader and libc loaded by the program. */ + bool interpreter_found = false, libc_found = false; + while (fgets (buffer, array_length (buffer), out) != NULL) + { + /* Ignore vDSO. */ + if (buffer[0] != '/') + continue; + + /* Remove newline so baseline (buffer) can compare against the + LD_SO and LIBC_SO macros unmodified. */ + if (buffer[strlen(buffer)-1] == '\n') + buffer[strlen(buffer)-1] = '\0'; + + if (strcmp (basename (buffer), LD_SO) == 0) + { + TEST_COMPARE (interpreter_found, false); + interpreter_found = true; + continue; + } + + if (strcmp (basename (buffer), LIBC_SO) == 0) + { + TEST_COMPARE (libc_found, false); + libc_found = true; + continue; + } + } + TEST_COMPARE (interpreter_found, true); + TEST_COMPARE (libc_found, true); + + fclose (out); + } + + support_capture_subprocess_free (&pldd); + support_process_terminate (&target); + + return 0; +} + +#include <support/test-driver.c> |