[v3] elf: Add tst-ldconfig-bad-aux-cache test [BZ #18093]
diff mbox series

Message ID 20190513133417.22988-1-ahajkova@redhat.com
State New
Headers show
Series
  • [v3] elf: Add tst-ldconfig-bad-aux-cache test [BZ #18093]
Related show

Commit Message

Alexandra Hájková May 13, 2019, 1:34 p.m. UTC
From: Alexandra Hájková <ahajkova@redhat.com>

This test corrupts /var/cache/ldconfig/aux-cache and executes ldconfig
to check it will not segfault using the corrupted aux_cache. The test
uses the test-in-container framework. Verified no regressions on
x86_64.

2019-05-13 Alexandra Hajkova  <ahajkova@redhat.com>

       * elf/Makefile (test-container): Add tst-ldconfig-bad-aux-cache.
       * elf/tst-ldconfig-bad-aux-cache.c: New file.
       * elf/tst-ldconfig_aux-cache.root: New directory.
       * elf/tst-ldconfig-bad-aux-cache.root/postclean.req: New file

 v3
 - use pid == 0 instead of !pid to follow glibc style rules
 - call _exit after execve
---
 elf/Makefile                                  |   3 +
 elf/tst-ldconfig-bad-aux-cache.c              | 136 ++++++++++++++++++
 .../postclean.req                             |   0
 3 files changed, 139 insertions(+)
 create mode 100644 elf/tst-ldconfig-bad-aux-cache.c
 create mode 100644 elf/tst-ldconfig-bad-aux-cache.root/postclean.req

Comments

Carlos O'Donell May 13, 2019, 3:48 p.m. UTC | #1
On 5/13/19 9:34 AM, Alexandra Hájková wrote:
> From: Alexandra Hájková <ahajkova@redhat.com>
> 
> This test corrupts /var/cache/ldconfig/aux-cache and executes ldconfig
> to check it will not segfault using the corrupted aux_cache. The test
> uses the test-in-container framework. Verified no regressions on
> x86_64.

OK.

> 2019-05-13 Alexandra Hajkova  <ahajkova@redhat.com>
> 
>        * elf/Makefile (test-container): Add tst-ldconfig-bad-aux-cache.
>        * elf/tst-ldconfig-bad-aux-cache.c: New file.
>        * elf/tst-ldconfig_aux-cache.root: New directory.
>        * elf/tst-ldconfig-bad-aux-cache.root/postclean.req: New file
> 
>  v3
>  - use pid == 0 instead of !pid to follow glibc style rules
>  - call _exit after execve

Looking forward to v4 and a patch to add rootsbindir.

> ---
>  elf/Makefile                                  |   3 +
>  elf/tst-ldconfig-bad-aux-cache.c              | 136 ++++++++++++++++++
>  .../postclean.req                             |   0
>  3 files changed, 139 insertions(+)
>  create mode 100644 elf/tst-ldconfig-bad-aux-cache.c
>  create mode 100644 elf/tst-ldconfig-bad-aux-cache.root/postclean.req
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 4895489208..08e2f999bf 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -156,6 +156,9 @@ tests-static-internal := tst-tls1-static tst-tls2-static \
>  CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
>  tst-tls1-static-non-pie-no-pie = yes
>  
> +tests-container = \
> +			  tst-ldconfig-bad-aux-cache

OK.

> +
>  tests := tst-tls9 tst-leaks1 \
>  	tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
>  	tst-auxv
> diff --git a/elf/tst-ldconfig-bad-aux-cache.c b/elf/tst-ldconfig-bad-aux-cache.c
> new file mode 100644
> index 0000000000..a1ed7a0877
> --- /dev/null
> +++ b/elf/tst-ldconfig-bad-aux-cache.c
> @@ -0,0 +1,136 @@
> +/* Test ldconfig does not segfault when aux-cache is corrupted (Bug 18093).

OK.

> +   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; see the file COPYING.LIB.  If
> +   not, see <http://www.gnu.org/licenses/>.  */
> +
> +/* This test does the following:
> +   Run ldconfig to create the caches.
> +   Corrupt the caches.
> +   Run ldconfig again.
> +   At each step we verify that ldconfig does not crash.  */

OK.

> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <sys/wait.h>
> +#include <ftw.h>
> +#include <stdint.h>
> +
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +
> +#include <dirent.h>
> +
> +static int
> +display_info (const char *fpath, const struct stat *sb,
> +              int tflag, struct FTW *ftwbuf)
> +{
> +  printf ("%-3s %2d %7jd   %-40s %d %s\n",
> +          (tflag == FTW_D) ? "d" : (tflag == FTW_DNR) ? "dnr" :
> +          (tflag == FTW_DP) ? "dp" : (tflag == FTW_F) ? "f" :
> +          (tflag == FTW_NS) ? "ns" : (tflag == FTW_SL) ? "sl" :
> +          (tflag == FTW_SLN) ? "sln" : "???",
> +          ftwbuf->level, (intmax_t) sb->st_size,
> +          fpath, ftwbuf->base, fpath + ftwbuf->base);
> +  /* To tell nftw() to continue */
> +  return 0;
> +}

OK.

> +
> +/* Run ldconfig with a corrupt aux-cache, in particular we test for size
> +   truncation that might happen if a previous ldconfig run failed or if
> +   there were storage or power issues while we were writing the file.
> +   We want ldconfig not to crash, and it should be able to do so by
> +   computing the expected size of the file (bug 18093).  */
> +static int
> +do_test (void)
> +{
> +  char *args[] = { (char *) "/sbin/ldconfig", NULL };

We have a problem here which I didn't realize until we had this bug
reported: https://sourceware.org/bugzilla/show_bug.cgi?id=24544

Where we used to run /usr/bin/pldd, but with an alterate --prefix
it's not the correct path in the chroot. Users can also configure --bindir
also (part of the GNU Coding Standard).

The ldconfig binary is installed into the root sbin directory, and so
we should in the test use that directory to call ldconfig.

Therefore we need to fix this in the same way:

* In support/Makefile for the CFLAGS-support_paths.c rule add a new
  define e.g. -DROOTSBINDIR_PATH=\"$(rootsbindir)\"
* Add support_install_sbindir to support/support_paths.h which is defined as
  being SBINDIR_PATH.
* In your new test use strcpy/strcat to assemble the name of the test
  to call e.g. snprintf (prog, sizeof prog, "%s/pldd", support_install_rootsbindir) 

Want to submit a patch which just adds support_install_rootsbindir
and then submit v4 of this test and then I think we're done :-)

> +  const char *path = "/var/cache/ldconfig/aux-cache";

Interestingly enough I see a bug in glibc, not your test case though.

sysdeps/generic/ldconfig.h:

 48 /* Name of auxiliary cache.  */
 49 #define _PATH_LDCONFIG_AUX_CACHE "/var/cache/ldconfig/aux-cache"
 50 

However, we do allow users to override with --localstatedir and provide
an alternative path instead of the default /var. Nobody has probably
tried to do this or they just worked around it.

So the build of glibc itself will appear to ignore --localstatedir
even when specified and read the aux-cache from a fixed location.

This is not your problem, your test is fine.

> +  struct stat fs;
> +  long int size, new_size, i;
> +  int status;
> +  pid_t pid;
> +
> +  /* Create the needed directories. */
> +  xmkdirp ("/var/cache/ldconfig", 0777);

OK.

> +
> +  pid = xfork ();
> +  /* Run ldconfig fist to generate the aux-cache.  */
> +  if (pid == 0)
> +    {
> +      execv (args[0], args);
> +       _exit(1);

Spacing doesn't look right, please review.

> +    }
> +  else
> +    {
> +      if (pid)
> +        {
> +          xwaitpid (pid, &status, 0);
> +          if (!(WIFEXITED (status)))
> +            FAIL_EXIT1 ("ldconfig was aborted");
> +          if (stat (path, &fs) < 0)
> +          {
> +              if (errno == ENOENT)
> +                FAIL_EXIT1 ("aux-cache does not exist");
> +              else
> +                FAIL_EXIT1 ("Failed to open aux-cache.");
> +              return -1;
> +          }
> +
> +          size = fs.st_size;
> +          /* Run 3 tests, each truncating aux-cache shorter and shorter.  */
> +          for (i = 3; i > 0; i--)
> +            {
> +              new_size = size * i / 4;
> +              if (truncate (path, new_size))
> +              {
> +                  if (errno == ENOENT)
> +                      FAIL_EXIT1 ("aux-cache does not exist");
> +                  else
> +                      FAIL_EXIT1 ("truncation failed.");
> +                  return -1;
> +              }
> +              if (nftw (path, display_info, 1000, 0) == -1)
> +                {
> +                  FAIL_EXIT1 ("nftw failed.");
> +                  return -1;
> +                }
> +
> +              pid = xfork ();
> +              /* Verify that ldconfig can run with a truncated
> +                 aux-cache and doesn't crash.  */
> +              if (pid == 0)
> +                {
> +                  execv (args[0], args);
> +                  _exit(1);
> +                }
> +              else
> +                {
> +                  xwaitpid (pid, &status, 0);
> +                  if (!(WIFEXITED (status)))
> +                    FAIL_EXIT1 ("ldconfig exited with non-zero status");
> +                }
> +
> +            }
> +        }
> +    }
> +
> +  return 0;
> +}

OK.

> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-ldconfig-bad-aux-cache.root/postclean.req b/elf/tst-ldconfig-bad-aux-cache.root/postclean.req
> new file mode 100644
> index 0000000000..e69de29bb2

OK.

>
Joseph Myers May 13, 2019, 5:01 p.m. UTC | #2
On Mon, 13 May 2019, Carlos O'Donell wrote:

> sysdeps/generic/ldconfig.h:
> 
>  48 /* Name of auxiliary cache.  */
>  49 #define _PATH_LDCONFIG_AUX_CACHE "/var/cache/ldconfig/aux-cache"
>  50 
> 
> However, we do allow users to override with --localstatedir and provide
> an alternative path instead of the default /var. Nobody has probably
> tried to do this or they just worked around it.
> 
> So the build of glibc itself will appear to ignore --localstatedir
> even when specified and read the aux-cache from a fixed location.

There are lots of other places with such hardcoded paths.  See e.g. bug 
20138 asking for --sysconfdir to be respected.  Note that some such paths 
are in installed headers, including paths.h; should those paths in 
installed headers also depend on configure options?
Carlos O'Donell May 14, 2019, 2:15 a.m. UTC | #3
On 5/13/19 1:01 PM, Joseph Myers wrote:
> On Mon, 13 May 2019, Carlos O'Donell wrote:
> 
>> sysdeps/generic/ldconfig.h:
>>
>>  48 /* Name of auxiliary cache.  */
>>  49 #define _PATH_LDCONFIG_AUX_CACHE "/var/cache/ldconfig/aux-cache"
>>  50 
>>
>> However, we do allow users to override with --localstatedir and provide
>> an alternative path instead of the default /var. Nobody has probably
>> tried to do this or they just worked around it.
>>
>> So the build of glibc itself will appear to ignore --localstatedir
>> even when specified and read the aux-cache from a fixed location.
> 
> There are lots of other places with such hardcoded paths.  See e.g. bug 
> 20138 asking for --sysconfdir to be respected.  Note that some such paths 
> are in installed headers, including paths.h; should those paths in 
> installed headers also depend on configure options?
 
Would the _nl_default_dirname discussion set some precedent for this?

https://sourceware.org/bugzilla/show_bug.cgi?id=14664
https://sourceware.org/ml/libc-alpha/2012-12/msg00192.html

Namely that paths.h is not a part of the ABI, and therefore configure
options can and should change the values in the header to match.

The general agreement in _nl_default_dirname was:

* Configure options should not change ABI.
* Any option that changes ABI was likely a broken API design e.g. _nl_default_dirname
  and the generated COPY relocations it exposes are part of the ABI.

If you want a substantially different ABI then I feel you should be
creating a new target with it's own ABI.
Joseph Myers May 16, 2019, 7:41 p.m. UTC | #4
On Mon, 13 May 2019, Carlos O'Donell wrote:

> > There are lots of other places with such hardcoded paths.  See e.g. bug 
> > 20138 asking for --sysconfdir to be respected.  Note that some such paths 
> > are in installed headers, including paths.h; should those paths in 
> > installed headers also depend on configure options?
>  
> Would the _nl_default_dirname discussion set some precedent for this?
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=14664
> https://sourceware.org/ml/libc-alpha/2012-12/msg00192.html

It's certainly relevant.

> Namely that paths.h is not a part of the ABI, and therefore configure
> options can and should change the values in the header to match.

There's a general question here of when installed headers should depend on 
configure options - because when they do, that sets bounds on what 
multilib configurations can share a single set of installed headers.  We 
expect multilib configurations for e.g. -m32 and -m64 to be able to share 
headers.  Changing paths.h would imply that glibc builds with different 
paths can't share installed headers.  (An edge case for the question of 
whether headers can be shared would be e.g. powerpc64 BE and LE - right 
now I think they can as there are no LE-specific headers, and GCC does 
have some multilib configurations with both BE and LE multilibs, but those 
configurations use their own lib* directory names nothing else in the 
toolchain knows about, so might not work well for other reasons.)
Carlos O'Donell May 16, 2019, 8:12 p.m. UTC | #5
On 5/16/19 3:41 PM, Joseph Myers wrote:
> On Mon, 13 May 2019, Carlos O'Donell wrote:
> 
>>> There are lots of other places with such hardcoded paths.  See e.g. bug 
>>> 20138 asking for --sysconfdir to be respected.  Note that some such paths 
>>> are in installed headers, including paths.h; should those paths in 
>>> installed headers also depend on configure options?
>>  
>> Would the _nl_default_dirname discussion set some precedent for this?
>>
>> https://sourceware.org/bugzilla/show_bug.cgi?id=14664
>> https://sourceware.org/ml/libc-alpha/2012-12/msg00192.html
> 
> It's certainly relevant.
> 
>> Namely that paths.h is not a part of the ABI, and therefore configure
>> options can and should change the values in the header to match.
> 
> There's a general question here of when installed headers should depend on 
> configure options - because when they do, that sets bounds on what 
> multilib configurations can share a single set of installed headers.  We 
> expect multilib configurations for e.g. -m32 and -m64 to be able to share 
> headers.  Changing paths.h would imply that glibc builds with different 
> paths can't share installed headers.  (An edge case for the question of 
> whether headers can be shared would be e.g. powerpc64 BE and LE - right 
> now I think they can as there are no LE-specific headers, and GCC does 
> have some multilib configurations with both BE and LE multilibs, but those 
> configurations use their own lib* directory names nothing else in the 
> toolchain knows about, so might not work well for other reasons.)
 
My opinion is that having multlibs share headers, particularly for -m32
and -m64 was a design mistake. We should have had completely distinct
headers and keyed the search directory based on the machine being used.

What historical need drove the requirement to share headers? 

Likewise my opinion is that two ABI-compatible toolchains, but compiled
with distinct installed artifact locations, are two distinct runtimes,
and they should have no expectation to be able to share headers.
Joseph Myers May 16, 2019, 8:35 p.m. UTC | #6
On Thu, 16 May 2019, Carlos O'Donell wrote:

> What historical need drove the requirement to share headers? 

My guess would be that it was more that multilib support (long before 
sysroots) originally introduced a requirement for separate directories for 
libraries without introducing such a requirement for headers.  In that 
context, it was natural to set up headers so they could be shared (with, 
in glibc's case, special handling for gnu/lib-names.h and gnu/stubs.h).  
Thus, there is in general in GCC no multilib-specific include directory 
(no analogue of lib / lib64 / libx32 etc. directories for headers), 
although it's possible to define SYSROOT_HEADERS_SUFFIX_SPEC (but most 
configurations defining SYSROOT_SUFFIX_SPEC, possibly to an 
automatically-generated definition based on the multilib configuration, do 
not define SYSROOT_HEADERS_SUFFIX_SPEC).
Carlos O'Donell May 16, 2019, 8:46 p.m. UTC | #7
On 5/16/19 4:35 PM, Joseph Myers wrote:
> On Thu, 16 May 2019, Carlos O'Donell wrote:
> 
>> What historical need drove the requirement to share headers? 
> 
> My guess would be that it was more that multilib support (long before 
> sysroots) originally introduced a requirement for separate directories for 
> libraries without introducing such a requirement for headers.  In that 
> context, it was natural to set up headers so they could be shared (with, 
> in glibc's case, special handling for gnu/lib-names.h and gnu/stubs.h).  
> Thus, there is in general in GCC no multilib-specific include directory 
> (no analogue of lib / lib64 / libx32 etc. directories for headers), 
> although it's possible to define SYSROOT_HEADERS_SUFFIX_SPEC (but most 
> configurations defining SYSROOT_SUFFIX_SPEC, possibly to an 
> automatically-generated definition based on the multilib configuration, do 
> not define SYSROOT_HEADERS_SUFFIX_SPEC).

This is exactly the answer I expected and in line with my own experience.

In summary then, I think we should do no additional work to allow multiple
differently configured glibc's to share the same headers, particularly
/usr/include/paths.h (default install directory). If you wish to have
differently configured installs then you will need a uniquely configured
sysroot that is possibly triggered via a compiler spec file given a special
option (reminds me of -msgxx-glibc in the CodeSourcery toolchains).

I also think we should encourage the GNU Toolchain to move in a direction
where the various multilib targets do not need to share headers. This sharing
causes real problems in downstream where tooling cannot use "noarch" because
the headers are really "only 2 arch" and then we need special support for
i686 vs. x86_64 headers being interchangeable and correct. Basically the
upstream GNU toolchain never considered how a downstream would package this
kind of shared-header support.
Joseph Myers May 16, 2019, 9:04 p.m. UTC | #8
On Thu, 16 May 2019, Carlos O'Donell wrote:

> I also think we should encourage the GNU Toolchain to move in a direction
> where the various multilib targets do not need to share headers. This sharing
> causes real problems in downstream where tooling cannot use "noarch" because
> the headers are really "only 2 arch" and then we need special support for
> i686 vs. x86_64 headers being interchangeable and correct. Basically the
> upstream GNU toolchain never considered how a downstream would package this
> kind of shared-header support.

There is certainly a case for more consistently ensuring that headers 
outside of bits/ and sys/ are architecture-independent (simply as a naming 
convention, independent of sharing installed header directories).  Most of 
them are, but at least fpu_control.h and ieee754.h have architecture 
dependencies despite not being in one of the subdirectories normally used 
when there are such dependencies.
Carlos O'Donell May 16, 2019, 9:08 p.m. UTC | #9
On 5/16/19 5:04 PM, Joseph Myers wrote:
> On Thu, 16 May 2019, Carlos O'Donell wrote:
> 
>> I also think we should encourage the GNU Toolchain to move in a direction
>> where the various multilib targets do not need to share headers. This sharing
>> causes real problems in downstream where tooling cannot use "noarch" because
>> the headers are really "only 2 arch" and then we need special support for
>> i686 vs. x86_64 headers being interchangeable and correct. Basically the
>> upstream GNU toolchain never considered how a downstream would package this
>> kind of shared-header support.
> 
> There is certainly a case for more consistently ensuring that headers 
> outside of bits/ and sys/ are architecture-independent (simply as a naming 
> convention, independent of sharing installed header directories).  Most of 
> them are, but at least fpu_control.h and ieee754.h have architecture 
> dependencies despite not being in one of the subdirectories normally used 
> when there are such dependencies.
 
The purpose of such consistency is to reduce maintenance burden, and can
be a natural consequence of such community activities, and from that
perspective, you're right, it should be encouraged. However, there should
be no explicit or implicit requirement to share headers, since there is
no use case for it.

Patch
diff mbox series

diff --git a/elf/Makefile b/elf/Makefile
index 4895489208..08e2f999bf 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -156,6 +156,9 @@  tests-static-internal := tst-tls1-static tst-tls2-static \
 CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
 tst-tls1-static-non-pie-no-pie = yes
 
+tests-container = \
+			  tst-ldconfig-bad-aux-cache
+
 tests := tst-tls9 tst-leaks1 \
 	tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
 	tst-auxv
diff --git a/elf/tst-ldconfig-bad-aux-cache.c b/elf/tst-ldconfig-bad-aux-cache.c
new file mode 100644
index 0000000000..a1ed7a0877
--- /dev/null
+++ b/elf/tst-ldconfig-bad-aux-cache.c
@@ -0,0 +1,136 @@ 
+/* Test ldconfig does not segfault when aux-cache is corrupted (Bug 18093).
+   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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+/* This test does the following:
+   Run ldconfig to create the caches.
+   Corrupt the caches.
+   Run ldconfig again.
+   At each step we verify that ldconfig does not crash.  */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <ftw.h>
+#include <stdint.h>
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+
+#include <dirent.h>
+
+static int
+display_info (const char *fpath, const struct stat *sb,
+              int tflag, struct FTW *ftwbuf)
+{
+  printf ("%-3s %2d %7jd   %-40s %d %s\n",
+          (tflag == FTW_D) ? "d" : (tflag == FTW_DNR) ? "dnr" :
+          (tflag == FTW_DP) ? "dp" : (tflag == FTW_F) ? "f" :
+          (tflag == FTW_NS) ? "ns" : (tflag == FTW_SL) ? "sl" :
+          (tflag == FTW_SLN) ? "sln" : "???",
+          ftwbuf->level, (intmax_t) sb->st_size,
+          fpath, ftwbuf->base, fpath + ftwbuf->base);
+  /* To tell nftw() to continue */
+  return 0;
+}
+
+/* Run ldconfig with a corrupt aux-cache, in particular we test for size
+   truncation that might happen if a previous ldconfig run failed or if
+   there were storage or power issues while we were writing the file.
+   We want ldconfig not to crash, and it should be able to do so by
+   computing the expected size of the file (bug 18093).  */
+static int
+do_test (void)
+{
+  char *args[] = { (char *) "/sbin/ldconfig", NULL };
+  const char *path = "/var/cache/ldconfig/aux-cache";
+  struct stat fs;
+  long int size, new_size, i;
+  int status;
+  pid_t pid;
+
+  /* Create the needed directories. */
+  xmkdirp ("/var/cache/ldconfig", 0777);
+
+  pid = xfork ();
+  /* Run ldconfig fist to generate the aux-cache.  */
+  if (pid == 0)
+    {
+      execv (args[0], args);
+       _exit(1);
+    }
+  else
+    {
+      if (pid)
+        {
+          xwaitpid (pid, &status, 0);
+          if (!(WIFEXITED (status)))
+            FAIL_EXIT1 ("ldconfig was aborted");
+          if (stat (path, &fs) < 0)
+          {
+              if (errno == ENOENT)
+                FAIL_EXIT1 ("aux-cache does not exist");
+              else
+                FAIL_EXIT1 ("Failed to open aux-cache.");
+              return -1;
+          }
+
+          size = fs.st_size;
+          /* Run 3 tests, each truncating aux-cache shorter and shorter.  */
+          for (i = 3; i > 0; i--)
+            {
+              new_size = size * i / 4;
+              if (truncate (path, new_size))
+              {
+                  if (errno == ENOENT)
+                      FAIL_EXIT1 ("aux-cache does not exist");
+                  else
+                      FAIL_EXIT1 ("truncation failed.");
+                  return -1;
+              }
+              if (nftw (path, display_info, 1000, 0) == -1)
+                {
+                  FAIL_EXIT1 ("nftw failed.");
+                  return -1;
+                }
+
+              pid = xfork ();
+              /* Verify that ldconfig can run with a truncated
+                 aux-cache and doesn't crash.  */
+              if (pid == 0)
+                {
+                  execv (args[0], args);
+                  _exit(1);
+                }
+              else
+                {
+                  xwaitpid (pid, &status, 0);
+                  if (!(WIFEXITED (status)))
+                    FAIL_EXIT1 ("ldconfig exited with non-zero status");
+                }
+
+            }
+        }
+    }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ldconfig-bad-aux-cache.root/postclean.req b/elf/tst-ldconfig-bad-aux-cache.root/postclean.req
new file mode 100644
index 0000000000..e69de29bb2