PING^1 [PATCH] Call _dl_open_check after relocation is finished [BZ #24259]

Message ID CAMe9rOpwxNNzfVrj1O77i4NQW76e7U4EkySjzmpaZKWaaEMpNA@mail.gmail.com
State Committed
Headers

Commit Message

H.J. Lu April 9, 2019, 10:56 p.m. UTC
  On Tue, Mar 5, 2019 at 6:00 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> >> If you report the error at this, doesn't this mean the object is still
> >> around and in a bad state?  This looks related to this bug:
> >
> > Yes.
> >
> >>   <https://sourceware.org/bugzilla/show_bug.cgi?id=20839>
> >>
> >> Would the CET bug go away if we got rid after the object without trace
> >> after a failure in _dl_open_check?
> >
> > Yes.
> >
> >> I can look into fixing the other bug, but I don't know how hard that's
> >> going to be.
>
> I did that now and the required infrastructure changes are fairly
> involved.  So I think we should add something that works today.
>
> A natural place for the CET compatibility check would be
> elf_machine_reject_phdr_p (currently used only on MIPS).  This way, we
> can continue searching for a CET-compatible library along the search
> path.

We only want to check it in dlopen path.  A legacy shared library can
loaded before main () and shadow stack will be disabled.

> >> > diff --git a/sysdeps/x86/tst-cet-legacy-5.c b/sysdeps/x86/tst-cet-legacy-5.c
> >> > new file mode 100644
> >> > index 0000000000..fbf640f664
> >> > --- /dev/null
> >> > +++ b/sysdeps/x86/tst-cet-legacy-5.c
> >>
> >> > +static void
> >> > +do_test_1 (const char *modname, bool fail)
> >> > +{
> >> > +  int (*fp) (void);
> >> > +  void *h;
> >> > +
> >> > +  h = dlopen (modname, RTLD_LAZY);
> >> > +  if (h == NULL)
> >> > +    {
> >> > +      if (fail)
> >> > +     {
> >> > +       const char *err = dlerror ();
> >> > +       if (strstr (err, "shadow stack isn't enabled") == NULL)
> >> > +         {
> >> > +           printf ("incorrect dlopen '%s' error: %s\n", modname,
> >> > +                   dlerror ());
> >> > +           exit (1);
> >> > +         }
> >> > +
> >> > +       return;
> >> > +     }
> >>
> >> Is the return supposed to be taken if running on non-CET hardware?  I'm
> >> looking for the UNSUPPORTED case.
> >
> > This path is taken only on CET hardware.  For non-CET hardware, 'h' shouldn't
> > be NULL.
>
> Please add logging to the test for the unsupported case, so that the aim
> of the test is clearer and when it fails to achieve its objective.
>

The tests should run on CET/non-CET kernel/processor.    Here is
the updated patch with FAIL_EXIT1.

OK for master?

Thanks.
  

Comments

H.J. Lu June 26, 2019, 4:57 p.m. UTC | #1
On Tue, Apr 9, 2019 at 3:56 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Mar 5, 2019 at 6:00 AM Florian Weimer <fweimer@redhat.com> wrote:
> >
> > * H. J. Lu:
> >
> > >> If you report the error at this, doesn't this mean the object is still
> > >> around and in a bad state?  This looks related to this bug:
> > >
> > > Yes.
> > >
> > >>   <https://sourceware.org/bugzilla/show_bug.cgi?id=20839>
> > >>
> > >> Would the CET bug go away if we got rid after the object without trace
> > >> after a failure in _dl_open_check?
> > >
> > > Yes.
> > >
> > >> I can look into fixing the other bug, but I don't know how hard that's
> > >> going to be.
> >
> > I did that now and the required infrastructure changes are fairly
> > involved.  So I think we should add something that works today.
> >
> > A natural place for the CET compatibility check would be
> > elf_machine_reject_phdr_p (currently used only on MIPS).  This way, we
> > can continue searching for a CET-compatible library along the search
> > path.
>
> We only want to check it in dlopen path.  A legacy shared library can
> loaded before main () and shadow stack will be disabled.
>
> > >> > diff --git a/sysdeps/x86/tst-cet-legacy-5.c b/sysdeps/x86/tst-cet-legacy-5.c
> > >> > new file mode 100644
> > >> > index 0000000000..fbf640f664
> > >> > --- /dev/null
> > >> > +++ b/sysdeps/x86/tst-cet-legacy-5.c
> > >>
> > >> > +static void
> > >> > +do_test_1 (const char *modname, bool fail)
> > >> > +{
> > >> > +  int (*fp) (void);
> > >> > +  void *h;
> > >> > +
> > >> > +  h = dlopen (modname, RTLD_LAZY);
> > >> > +  if (h == NULL)
> > >> > +    {
> > >> > +      if (fail)
> > >> > +     {
> > >> > +       const char *err = dlerror ();
> > >> > +       if (strstr (err, "shadow stack isn't enabled") == NULL)
> > >> > +         {
> > >> > +           printf ("incorrect dlopen '%s' error: %s\n", modname,
> > >> > +                   dlerror ());
> > >> > +           exit (1);
> > >> > +         }
> > >> > +
> > >> > +       return;
> > >> > +     }
> > >>
> > >> Is the return supposed to be taken if running on non-CET hardware?  I'm
> > >> looking for the UNSUPPORTED case.
> > >
> > > This path is taken only on CET hardware.  For non-CET hardware, 'h' shouldn't
> > > be NULL.
> >
> > Please add logging to the test for the unsupported case, so that the aim
> > of the test is clearer and when it fails to achieve its objective.
> >
>
> The tests should run on CET/non-CET kernel/processor.    Here is
> the updated patch with FAIL_EXIT1.
>
> OK for master?
>

PING:

https://sourceware.org/ml/libc-alpha/2019-04/msg00228.html
  
Florian Weimer June 26, 2019, 5:22 p.m. UTC | #2
* H. J. Lu:

> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index 12a4f8b853..a144a40790 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -292,8 +292,6 @@ dl_open_worker (void *a)
>    _dl_debug_state ();
>    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
>  
> -  _dl_open_check (new);
> -
>    /* Print scope information.  */
>    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
>      _dl_show_scope (new, 0);
> @@ -366,6 +364,11 @@ dl_open_worker (void *a)
>  	_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
>      }
>  
> +  /* NB: Since _dl_open_check may throw an exception, it must be called
> +     after relocation is finished.   Otherwise, a shared object may be
> +     mmapped without relocation.  */
> +  _dl_open_check (new);
> +
>    /* If the file is not loaded now as a dependency, add the search
>       list of the newly loaded object to the scope.  */
>    bool any_tls = false;

Presumably you test this using libpthread because it's NODELETE?

I'm not sure if this correct.  Won't this leave behind a relocated
NODELTE objects whose constructors have not run?  That would still leave
the process in a bad state.

Thanks,
Florian
  
H.J. Lu June 26, 2019, 10:29 p.m. UTC | #3
On Wed, Jun 26, 2019 at 10:22 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> > diff --git a/elf/dl-open.c b/elf/dl-open.c
> > index 12a4f8b853..a144a40790 100644
> > --- a/elf/dl-open.c
> > +++ b/elf/dl-open.c
> > @@ -292,8 +292,6 @@ dl_open_worker (void *a)
> >    _dl_debug_state ();
> >    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
> >
> > -  _dl_open_check (new);
> > -
> >    /* Print scope information.  */
> >    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
> >      _dl_show_scope (new, 0);
> > @@ -366,6 +364,11 @@ dl_open_worker (void *a)
> >       _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
> >      }
> >
> > +  /* NB: Since _dl_open_check may throw an exception, it must be called
> > +     after relocation is finished.   Otherwise, a shared object may be
> > +     mmapped without relocation.  */
> > +  _dl_open_check (new);
> > +
> >    /* If the file is not loaded now as a dependency, add the search
> >       list of the newly loaded object to the scope.  */
> >    bool any_tls = false;
>
> Presumably you test this using libpthread because it's NODELETE?

Yes.

> I'm not sure if this correct.  Won't this leave behind a relocated
> NODELTE objects whose constructors have not run?  That would still leave
> the process in a bad state.

Such library is only mapped and relocated, but nothing else.   Next loading
will continue and finish the rest of loading steps, including calling
constructors.
  
Florian Weimer June 27, 2019, 7:17 a.m. UTC | #4
* H. J. Lu:

> On Wed, Jun 26, 2019 at 10:22 AM Florian Weimer <fweimer@redhat.com> wrote:
>>
>> * H. J. Lu:
>>
>> > diff --git a/elf/dl-open.c b/elf/dl-open.c
>> > index 12a4f8b853..a144a40790 100644
>> > --- a/elf/dl-open.c
>> > +++ b/elf/dl-open.c
>> > @@ -292,8 +292,6 @@ dl_open_worker (void *a)
>> >    _dl_debug_state ();
>> >    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
>> >
>> > -  _dl_open_check (new);
>> > -
>> >    /* Print scope information.  */
>> >    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
>> >      _dl_show_scope (new, 0);
>> > @@ -366,6 +364,11 @@ dl_open_worker (void *a)
>> >       _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
>> >      }
>> >
>> > +  /* NB: Since _dl_open_check may throw an exception, it must be called
>> > +     after relocation is finished.   Otherwise, a shared object may be
>> > +     mmapped without relocation.  */
>> > +  _dl_open_check (new);
>> > +
>> >    /* If the file is not loaded now as a dependency, add the search
>> >       list of the newly loaded object to the scope.  */
>> >    bool any_tls = false;
>>
>> Presumably you test this using libpthread because it's NODELETE?
>
> Yes.

Ah, so it's related to bug 20839.

>> I'm not sure if this correct.  Won't this leave behind a relocated
>> NODELTE objects whose constructors have not run?  That would still leave
>> the process in a bad state.
>
> Such library is only mapped and relocated, but nothing else.   Next loading
> will continue and finish the rest of loading steps, including calling
> constructors.

But relocation can fail and unwind, too, as it happens in bug 20839.  So
at least the comment is misleading.

Clearly, moving around the check is not the proper fix, but maybe it's
the best option we have at present.

Have you investigate if we can remove again NODELETE mappings which have
been partially relocated?

Thanks,
Florian
  
H.J. Lu June 27, 2019, 8 p.m. UTC | #5
On Thu, Jun 27, 2019 at 12:17 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> > On Wed, Jun 26, 2019 at 10:22 AM Florian Weimer <fweimer@redhat.com> wrote:
> >>
> >> * H. J. Lu:
> >>
> >> > diff --git a/elf/dl-open.c b/elf/dl-open.c
> >> > index 12a4f8b853..a144a40790 100644
> >> > --- a/elf/dl-open.c
> >> > +++ b/elf/dl-open.c
> >> > @@ -292,8 +292,6 @@ dl_open_worker (void *a)
> >> >    _dl_debug_state ();
> >> >    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
> >> >
> >> > -  _dl_open_check (new);
> >> > -
> >> >    /* Print scope information.  */
> >> >    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
> >> >      _dl_show_scope (new, 0);
> >> > @@ -366,6 +364,11 @@ dl_open_worker (void *a)
> >> >       _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
> >> >      }
> >> >
> >> > +  /* NB: Since _dl_open_check may throw an exception, it must be called
> >> > +     after relocation is finished.   Otherwise, a shared object may be
> >> > +     mmapped without relocation.  */
> >> > +  _dl_open_check (new);
> >> > +
> >> >    /* If the file is not loaded now as a dependency, add the search
> >> >       list of the newly loaded object to the scope.  */
> >> >    bool any_tls = false;
> >>
> >> Presumably you test this using libpthread because it's NODELETE?
> >
> > Yes.
>
> Ah, so it's related to bug 20839.
>
> >> I'm not sure if this correct.  Won't this leave behind a relocated
> >> NODELTE objects whose constructors have not run?  That would still leave
> >> the process in a bad state.
> >
> > Such library is only mapped and relocated, but nothing else.   Next loading
> > will continue and finish the rest of loading steps, including calling
> > constructors.
>
> But relocation can fail and unwind, too, as it happens in bug 20839.  So
> at least the comment is misleading.

I updated comments.

> Clearly, moving around the check is not the proper fix, but maybe it's
> the best option we have at present.

That is true.

> Have you investigate if we can remove again NODELETE mappings which have
> been partially relocated?

No.

I added 2 testcase, NODELETE and non-NODELETE.  NODELETE fails without
my change.   non-NODELETE works with and without my change.
  
Florian Weimer July 1, 2019, 4:42 p.m. UTC | #6
* H. J. Lu:

> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index 12a4f8b853..e18ee398cb 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -292,8 +292,6 @@ dl_open_worker (void *a)
>    _dl_debug_state ();
>    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
>  
> -  _dl_open_check (new);
> -
>    /* Print scope information.  */
>    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
>      _dl_show_scope (new, 0);
> @@ -366,6 +364,12 @@ dl_open_worker (void *a)
>  	_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
>      }
>  
> +  /* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
> +     object when _dl_open_check throws an exception.  Move it after
> +     relocation to avoid leaving the NODELETE object mapped without
> +     relocation.  */
> +  _dl_open_check (new);
> +
>    /* If the file is not loaded now as a dependency, add the search
>       list of the newly loaded object to the scope.  */
>    bool any_tls = false;

The downside is that an undefined symbol will now mask the CET failure.
Is this a problem?

Overall, I can't say I'm happy about this patch, but if it addresses
important breakage, I think this is okay for now.  If we fix the state
rollback properly, then hopefully we can move the check further up
again, where it probably belongs.

Thnaks,
Florian
  
H.J. Lu July 1, 2019, 7:17 p.m. UTC | #7
On Mon, Jul 1, 2019 at 9:42 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> > diff --git a/elf/dl-open.c b/elf/dl-open.c
> > index 12a4f8b853..e18ee398cb 100644
> > --- a/elf/dl-open.c
> > +++ b/elf/dl-open.c
> > @@ -292,8 +292,6 @@ dl_open_worker (void *a)
> >    _dl_debug_state ();
> >    LIBC_PROBE (map_complete, 3, args->nsid, r, new);
> >
> > -  _dl_open_check (new);
> > -
> >    /* Print scope information.  */
> >    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
> >      _dl_show_scope (new, 0);
> > @@ -366,6 +364,12 @@ dl_open_worker (void *a)
> >       _dl_relocate_object (l, l->l_scope, reloc_mode, 0);
> >      }
> >
> > +  /* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
> > +     object when _dl_open_check throws an exception.  Move it after
> > +     relocation to avoid leaving the NODELETE object mapped without
> > +     relocation.  */
> > +  _dl_open_check (new);
> > +
> >    /* If the file is not loaded now as a dependency, add the search
> >       list of the newly loaded object to the scope.  */
> >    bool any_tls = false;
>
> The downside is that an undefined symbol will now mask the CET failure.
> Is this a problem?

Since both problems will lead to load failure, there is no difference
in run-time
behavior except that the CET failure won't cause program crash now.

> Overall, I can't say I'm happy about this patch, but if it addresses
> important breakage, I think this is okay for now.  If we fix the state
> rollback properly, then hopefully we can move the check further up
> again, where it probably belongs.
>

Sure.  I am checking in my patch now.

Thanks.
  

Patch

From bf2b26ea60c8131ad97a2606a112c1b9231f79b4 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 23 Feb 2019 10:43:17 -0800
Subject: [PATCH] Call _dl_open_check after relocation is finished [BZ #24259]

When dlopening a shared object, _dl_open_check will throw an exception
if CET shadow stack is enabled and the shared object has no shadow stack
support.  dl_open_worker must call _dl_open_check after relocation is
finished.  Otherwise, the dependency shared objects may be mmapped
without relocation.  This will lead to run-time failure later when they
are needed by another dlopened shared object which is shadow stack
enabled.

	[BZ #24259]
	* elf/dl-open.c (dl_open_worker): Call _dl_open_check after
	relocation is finished.
	* sysdeps/x86/Makefile (tests): Add tst-cet-legacy-5a and
	tst-cet-legacy-5b.
	(modules-names): Add tst-cet-legacy-mod-5a and
	tst-cet-legacy-mod-5b.
	(CFLAGS-tst-cet-legacy-5a.c): New.
	(CFLAGS-tst-cet-legacy-5b.c): Likewise.
	(CFLAGS-tst-cet-legacy-mod-5a.c): Likewise.
	(CFLAGS-tst-cet-legacy-mod-5b.c): Likewise.
	($(objpfx)tst-cet-legacy-5a): Likewise.
	($(objpfx)tst-cet-legacy-5a.out): Likewise.
	($(objpfx)tst-cet-legacy-mod-5a.so): Likewise.
	($(objpfx)tst-cet-legacy-mod-5b.so): Likewise.
	($(objpfx)tst-cet-legacy-5b): Likewise.
	($(objpfx)tst-cet-legacy-5b.out): Likewise.
	(tst-cet-legacy-5b-ENV): Likewise.
	* sysdeps/x86/tst-cet-legacy-5.c: New file.
	* sysdeps/x86/tst-cet-legacy-5a.c: Likewise.
	* sysdeps/x86/tst-cet-legacy-5b.c: Likewise.
	* sysdeps/x86/tst-cet-legacy-mod-5.c: Likewise.
	* sysdeps/x86/tst-cet-legacy-mod-5a.c: Likewise.
	* sysdeps/x86/tst-cet-legacy-mod-5b.c: Likewise.
---
 elf/dl-open.c                       |  7 ++-
 sysdeps/x86/Makefile                | 22 ++++++-
 sysdeps/x86/tst-cet-legacy-5.c      | 66 +++++++++++++++++++++
 sysdeps/x86/tst-cet-legacy-5a.c     |  1 +
 sysdeps/x86/tst-cet-legacy-5b.c     |  1 +
 sysdeps/x86/tst-cet-legacy-mod-5.c  | 91 +++++++++++++++++++++++++++++
 sysdeps/x86/tst-cet-legacy-mod-5a.c |  1 +
 sysdeps/x86/tst-cet-legacy-mod-5b.c |  1 +
 8 files changed, 185 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/x86/tst-cet-legacy-5.c
 create mode 100644 sysdeps/x86/tst-cet-legacy-5a.c
 create mode 100644 sysdeps/x86/tst-cet-legacy-5b.c
 create mode 100644 sysdeps/x86/tst-cet-legacy-mod-5.c
 create mode 100644 sysdeps/x86/tst-cet-legacy-mod-5a.c
 create mode 100644 sysdeps/x86/tst-cet-legacy-mod-5b.c

diff --git a/elf/dl-open.c b/elf/dl-open.c
index 12a4f8b853..a144a40790 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -292,8 +292,6 @@  dl_open_worker (void *a)
   _dl_debug_state ();
   LIBC_PROBE (map_complete, 3, args->nsid, r, new);
 
-  _dl_open_check (new);
-
   /* Print scope information.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
     _dl_show_scope (new, 0);
@@ -366,6 +364,11 @@  dl_open_worker (void *a)
 	_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
     }
 
+  /* NB: Since _dl_open_check may throw an exception, it must be called
+     after relocation is finished.   Otherwise, a shared object may be
+     mmapped without relocation.  */
+  _dl_open_check (new);
+
   /* If the file is not loaded now as a dependency, add the search
      list of the newly loaded object to the scope.  */
   bool any_tls = false;
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 7ec46ca100..308bba80e5 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -19,13 +19,16 @@  ifeq ($(subdir),elf)
 sysdep-dl-routines += dl-cet
 
 tests += tst-cet-legacy-1 tst-cet-legacy-1a tst-cet-legacy-2 \
-	 tst-cet-legacy-2a tst-cet-legacy-3 tst-cet-legacy-4
+	 tst-cet-legacy-2a tst-cet-legacy-3 tst-cet-legacy-4 \
+	 tst-cet-legacy-5a
 tst-cet-legacy-1a-ARGS = -- $(host-test-program-cmd)
 ifneq (no,$(have-tunables))
-tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c
+tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \
+	 tst-cet-legacy-5b
 endif
 modules-names += tst-cet-legacy-mod-1 tst-cet-legacy-mod-2 \
-		 tst-cet-legacy-mod-4
+		 tst-cet-legacy-mod-4 tst-cet-legacy-mod-5a \
+		 tst-cet-legacy-mod-5b
 
 CFLAGS-tst-cet-legacy-2.c += -fcf-protection=branch
 CFLAGS-tst-cet-legacy-2a.c += -fcf-protection
@@ -36,6 +39,10 @@  CFLAGS-tst-cet-legacy-4.c += -fcf-protection=branch
 CFLAGS-tst-cet-legacy-4a.c += -fcf-protection
 CFLAGS-tst-cet-legacy-4b.c += -fcf-protection
 CFLAGS-tst-cet-legacy-mod-4.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-5a.c += -fcf-protection
+CFLAGS-tst-cet-legacy-5b.c += -fcf-protection
+CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=none
+CFLAGS-tst-cet-legacy-mod-5b.c += -fcf-protection
 
 $(objpfx)tst-cet-legacy-1: $(objpfx)tst-cet-legacy-mod-1.so \
 		       $(objpfx)tst-cet-legacy-mod-2.so
@@ -47,6 +54,11 @@  $(objpfx)tst-cet-legacy-2a: $(objpfx)tst-cet-legacy-mod-2.so $(libdl)
 $(objpfx)tst-cet-legacy-2a.out: $(objpfx)tst-cet-legacy-mod-1.so
 $(objpfx)tst-cet-legacy-4: $(libdl)
 $(objpfx)tst-cet-legacy-4.out: $(objpfx)tst-cet-legacy-mod-4.so
+$(objpfx)tst-cet-legacy-5a: $(libdl)
+$(objpfx)tst-cet-legacy-5a.out: $(objpfx)tst-cet-legacy-mod-5a.so \
+				$(objpfx)tst-cet-legacy-mod-5b.so
+$(objpfx)tst-cet-legacy-mod-5a.so: $(common-objpfx)nptl/libpthread.so
+$(objpfx)tst-cet-legacy-mod-5b.so: $(common-objpfx)nptl/libpthread.so
 ifneq (no,$(have-tunables))
 $(objpfx)tst-cet-legacy-4a: $(libdl)
 $(objpfx)tst-cet-legacy-4a.out: $(objpfx)tst-cet-legacy-mod-4.so
@@ -57,6 +69,10 @@  tst-cet-legacy-4b-ENV = GLIBC_TUNABLES=glibc.cpu.x86_shstk=on
 $(objpfx)tst-cet-legacy-4c: $(libdl)
 $(objpfx)tst-cet-legacy-4c.out: $(objpfx)tst-cet-legacy-mod-4.so
 tst-cet-legacy-4c-ENV = GLIBC_TUNABLES=glibc.cpu.x86_shstk=off
+$(objpfx)tst-cet-legacy-5b: $(libdl)
+$(objpfx)tst-cet-legacy-5b.out: $(objpfx)tst-cet-legacy-mod-5a.so \
+				$(objpfx)tst-cet-legacy-mod-5b.so
+tst-cet-legacy-5b-ENV = GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK
 endif
 endif
 
diff --git a/sysdeps/x86/tst-cet-legacy-5.c b/sysdeps/x86/tst-cet-legacy-5.c
new file mode 100644
index 0000000000..48bf997e38
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5.c
@@ -0,0 +1,66 @@ 
+/* Check compatibility of CET-enabled executable with dlopened legacy
+   shared object.
+   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 <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <support/check.h>
+
+static void
+do_test_1 (const char *modname, bool fail)
+{
+  int (*fp) (void);
+  void *h;
+
+  h = dlopen (modname, RTLD_LAZY);
+  if (h == NULL)
+    {
+      if (fail)
+	{
+	  const char *err = dlerror ();
+	  if (strstr (err, "shadow stack isn't enabled") == NULL)
+	    FAIL_EXIT1 ("incorrect dlopen '%s' error: %s\n", modname,
+			dlerror ());
+	  return;
+	}
+
+      FAIL_EXIT1 ("cannot open '%s': %s\n", modname, dlerror ());
+    }
+
+  fp = dlsym (h, "test");
+  if (fp == NULL)
+    FAIL_EXIT1 ("cannot get symbol 'test': %s\n", dlerror ());
+
+  if (fp () != 0)
+    FAIL_EXIT1 ("test () != 0");
+
+  dlclose (h);
+}
+
+static int
+do_test (void)
+{
+  do_test_1 ("tst-cet-legacy-mod-5a.so", true);
+  do_test_1 ("tst-cet-legacy-mod-5b.so", false);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/x86/tst-cet-legacy-5a.c b/sysdeps/x86/tst-cet-legacy-5a.c
new file mode 100644
index 0000000000..fc5a609dff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5a.c
@@ -0,0 +1 @@ 
+#include "tst-cet-legacy-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-5b.c b/sysdeps/x86/tst-cet-legacy-5b.c
new file mode 100644
index 0000000000..fc5a609dff
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-5b.c
@@ -0,0 +1 @@ 
+#include "tst-cet-legacy-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5.c b/sysdeps/x86/tst-cet-legacy-mod-5.c
new file mode 100644
index 0000000000..38d0aaa727
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5.c
@@ -0,0 +1,91 @@ 
+/* Check compatibility of CET-enabled executable with dlopened legacy
+   shared object.
+   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 <error.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+
+
+static void *
+tf (void *p)
+{
+  int err;
+
+  err = pthread_mutex_lock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "child: cannot get mutex");
+
+  puts ("child: got mutex; signalling");
+
+  pthread_cond_signal (&cond);
+
+  puts ("child: unlock");
+
+  err = pthread_mutex_unlock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "child: cannot unlock");
+
+  puts ("child: done");
+
+  return NULL;
+}
+
+int
+test (void)
+{
+  pthread_t th;
+  int err;
+
+  printf ("&cond = %p\n&mut = %p\n", &cond, &mut);
+
+  puts ("parent: get mutex");
+
+  err = pthread_mutex_lock (&mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot get mutex");
+
+  puts ("parent: create child");
+
+  err = pthread_create (&th, NULL, tf, NULL);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot create thread");
+
+  puts ("parent: wait for condition");
+
+  /* This test will fail on spurious wake-ups, which are allowed; however,
+     the current implementation shouldn't produce spurious wake-ups in the
+     scenario we are testing here.  */
+  err = pthread_cond_wait (&cond, &mut);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: cannot wait fir signal");
+
+  puts ("parent: got signal");
+
+  err = pthread_join (th, NULL);
+  if (err != 0)
+    error (EXIT_FAILURE, err, "parent: failed to join");
+
+  puts ("done");
+
+  return 0;
+}
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5a.c b/sysdeps/x86/tst-cet-legacy-mod-5a.c
new file mode 100644
index 0000000000..daa43e4e8d
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5a.c
@@ -0,0 +1 @@ 
+#include "tst-cet-legacy-mod-5.c"
diff --git a/sysdeps/x86/tst-cet-legacy-mod-5b.c b/sysdeps/x86/tst-cet-legacy-mod-5b.c
new file mode 100644
index 0000000000..daa43e4e8d
--- /dev/null
+++ b/sysdeps/x86/tst-cet-legacy-mod-5b.c
@@ -0,0 +1 @@ 
+#include "tst-cet-legacy-mod-5.c"
-- 
2.20.1