x86-64: Check if mprotect works before rewriting PLT

Message ID 20240111155849.8976-1-hjl.tools@gmail.com
State Superseded
Headers
Series x86-64: Check if mprotect works before rewriting PLT |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed

Commit Message

H.J. Lu Jan. 11, 2024, 3:58 p.m. UTC
  Systemd execution environment configuration may prohibit changing a memory
mapping to become executable:

MemoryDenyWriteExecute=
Takes a boolean argument. If set, attempts to create memory mappings
that are writable and executable at the same time, or to change existing
memory mappings to become executable, or mapping shared memory segments
as executable, are prohibited.

When it is set, systemd service stops working if PLT rewrite is enabled.
Check if mprotect works before rewriting PLT.  This fixes BZ #31230.
---
 .../unix/sysv/linux/x86_64/dl-plt-rewrite.h   | 43 +++++++++++++++++++
 sysdeps/x86/cpu-features.c                    |  4 +-
 sysdeps/x86_64/dl-plt-rewrite.h               | 25 +++++++++++
 3 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
 create mode 100644 sysdeps/x86_64/dl-plt-rewrite.h
  

Comments

Noah Goldstein Jan. 11, 2024, 7:47 p.m. UTC | #1
On Thu, Jan 11, 2024 at 7:58 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> Systemd execution environment configuration may prohibit changing a memory
> mapping to become executable:
>
> MemoryDenyWriteExecute=
> Takes a boolean argument. If set, attempts to create memory mappings
> that are writable and executable at the same time, or to change existing
> memory mappings to become executable, or mapping shared memory segments
> as executable, are prohibited.
>
> When it is set, systemd service stops working if PLT rewrite is enabled.
> Check if mprotect works before rewriting PLT.  This fixes BZ #31230.
> ---
>  .../unix/sysv/linux/x86_64/dl-plt-rewrite.h   | 43 +++++++++++++++++++
>  sysdeps/x86/cpu-features.c                    |  4 +-
>  sysdeps/x86_64/dl-plt-rewrite.h               | 25 +++++++++++
>  3 files changed, 71 insertions(+), 1 deletion(-)
>  create mode 100644 sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
>  create mode 100644 sysdeps/x86_64/dl-plt-rewrite.h
>
> diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> new file mode 100644
> index 0000000000..6401b7b2f2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> @@ -0,0 +1,43 @@
> +/* PLT rewrite help function.  Linux/x86-64 version.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +
> +   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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdbool.h>
> +#include <sys/mman.h>
> +
> +static __always_inline bool
> +dl_plt_rewrite_supported (void)
> +{
> +  /* PLT rewrite is enabled.  Check if mprotect works.  */
> +  void *plt = (void *) INTERNAL_SYSCALL_CALL (mmap, NULL, 4096,
> +                                             PROT_READ | PROT_WRITE,
> +                                             MAP_PRIVATE | MAP_ANONYMOUS,
> +                                             -1, 0);
> +  if (__glibc_unlikely (plt == MAP_FAILED))
> +    return false;
> +
> +  /* Touch the PROT_READ | PROT_WRITE page.  */
> +  *(int32_t *) plt = 1;
> +
> +  /* If the updated PROT_READ | PROT_WRITE page can be changed to
> +     PROT_EXEC | PROT_READ, rewrite PLT.  */
> +  bool status = (INTERNAL_SYSCALL_CALL (mprotect, plt, 4096,
> +                                       PROT_EXEC | PROT_READ) == 0);
> +
> +  INTERNAL_SYSCALL_CALL (munmap, plt, 4096);
> +
> +  return status;
> +}
> diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c
> index 46bdaffbc2..6aaa750e20 100644
> --- a/sysdeps/x86/cpu-features.c
> +++ b/sysdeps/x86/cpu-features.c
> @@ -28,10 +28,12 @@ extern void TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *)
>    attribute_hidden;
>
>  #if defined SHARED && defined __x86_64__
> +# include <dl-plt-rewrite.h>
> +
>  static void
>  TUNABLE_CALLBACK (set_plt_rewrite) (tunable_val_t *valp)
>  {
> -  if (valp->numval != 0)
> +  if (valp->numval != 0 && dl_plt_rewrite_supported ())
>      {
>        /* Use JMPABS only on APX processors.  */
>        const struct cpu_features *cpu_features = __get_cpu_features ();
> diff --git a/sysdeps/x86_64/dl-plt-rewrite.h b/sysdeps/x86_64/dl-plt-rewrite.h
> new file mode 100644
> index 0000000000..7eaae8f457
> --- /dev/null
> +++ b/sysdeps/x86_64/dl-plt-rewrite.h
> @@ -0,0 +1,25 @@
> +/* PLT rewrite help function.  x86-64 version.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +
> +   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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdbool.h>
> +#include <sys/mman.h>
> +
> +static __always_inline bool
> +dl_plt_rewrite_supported (void)
> +{
> +  return true;
> +}
> --
> 2.43.0
>

I'm in favor of this as I see little downside,
but others may feel differently. Going to wait a bit
to review to allow others to chime in.
  
Carlos O'Donell Jan. 12, 2024, 5:18 p.m. UTC | #2
On 1/11/24 10:58, H.J. Lu wrote:

Looking forward to a v2 so we can resolve this for 2.39.

> Systemd execution environment configuration may prohibit changing a memory
> mapping to become executable:

Agreed.

> MemoryDenyWriteExecute=
> Takes a boolean argument. If set, attempts to create memory mappings
> that are writable and executable at the same time, or to change existing
> memory mappings to become executable, or mapping shared memory segments
> as executable, are prohibited.

Does this also work for SELinux deny_execmem?

What does `getsebool -a | grep deny_execmem` say?

What does your patched glibc do with `semanage boolean --modify deny_execmem --on`?

WARNING: Do not attempt deny_execmen on anything but a VM you can spare having crash
to just a terminal.

> When it is set, systemd service stops working if PLT rewrite is enabled.
> Check if mprotect works before rewriting PLT.  This fixes BZ #31230.
> ---
>  .../unix/sysv/linux/x86_64/dl-plt-rewrite.h   | 43 +++++++++++++++++++
>  sysdeps/x86/cpu-features.c                    |  4 +-
>  sysdeps/x86_64/dl-plt-rewrite.h               | 25 +++++++++++
>  3 files changed, 71 insertions(+), 1 deletion(-)
>  create mode 100644 sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
>  create mode 100644 sysdeps/x86_64/dl-plt-rewrite.h
> 
> diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> new file mode 100644
> index 0000000000..6401b7b2f2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> @@ -0,0 +1,43 @@
> +/* PLT rewrite help function.  Linux/x86-64 version.

s/help/helper/g

> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +
> +   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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdbool.h>
> +#include <sys/mman.h>
> +
> +static __always_inline bool
> +dl_plt_rewrite_supported (void)
> +{
> +  /* PLT rewrite is enabled.  Check if mprotect works.  */
> +  void *plt = (void *) INTERNAL_SYSCALL_CALL (mmap, NULL, 4096,
> +					      PROT_READ | PROT_WRITE,
> +					      MAP_PRIVATE | MAP_ANONYMOUS,
> +					      -1, 0);
> +  if (__glibc_unlikely (plt == MAP_FAILED))
> +    return false;
> +
> +  /* Touch the PROT_READ | PROT_WRITE page.  */
> +  *(int32_t *) plt = 1;
> +
> +  /* If the updated PROT_READ | PROT_WRITE page can be changed to
> +     PROT_EXEC | PROT_READ, rewrite PLT.  */
> +  bool status = (INTERNAL_SYSCALL_CALL (mprotect, plt, 4096,
> +					PROT_EXEC | PROT_READ) == 0);

Is it specifically required that you go from RW to RWE?

Could you start with just an RWE page and go to RE?

> +
> +  INTERNAL_SYSCALL_CALL (munmap, plt, 4096);
> +
> +  return status;
> +}
> diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c
> index 46bdaffbc2..6aaa750e20 100644
> --- a/sysdeps/x86/cpu-features.c
> +++ b/sysdeps/x86/cpu-features.c
> @@ -28,10 +28,12 @@ extern void TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *)
>    attribute_hidden;
>  
>  #if defined SHARED && defined __x86_64__
> +# include <dl-plt-rewrite.h>
> +
>  static void
>  TUNABLE_CALLBACK (set_plt_rewrite) (tunable_val_t *valp)
>  {
> -  if (valp->numval != 0)
> +  if (valp->numval != 0 && dl_plt_rewrite_supported ())

Suggest comment here:

/* We must be careful about where we put the call to
   dl_plt_rewrite_supported() since it may generate
   spurious SELinux log entries.  It should only be
   attempted if the user requested a PLT rewrite.  */

The && short-circuit means we won't test the page support unless the tunable
is non-zero. This is important because we don't want logging to SELinux or
systemd for the rewrite test if the feature isn't being used.

>      {
>        /* Use JMPABS only on APX processors.  */
>        const struct cpu_features *cpu_features = __get_cpu_features ();
> diff --git a/sysdeps/x86_64/dl-plt-rewrite.h b/sysdeps/x86_64/dl-plt-rewrite.h
> new file mode 100644
> index 0000000000..7eaae8f457
> --- /dev/null
> +++ b/sysdeps/x86_64/dl-plt-rewrite.h
> @@ -0,0 +1,25 @@
> +/* PLT rewrite help function.  x86-64 version.

s/help/helper/g

> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +
> +   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
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdbool.h>
> +#include <sys/mman.h>
> +
> +static __always_inline bool
> +dl_plt_rewrite_supported (void)
> +{
> +  return true;
> +}
  
H.J. Lu Jan. 12, 2024, 6:09 p.m. UTC | #3
On Fri, Jan 12, 2024 at 9:18 AM Carlos O'Donell <carlos@redhat.com> wrote:
>
> On 1/11/24 10:58, H.J. Lu wrote:
>
> Looking forward to a v2 so we can resolve this for 2.39.
>
> > Systemd execution environment configuration may prohibit changing a memory
> > mapping to become executable:
>
> Agreed.
>
> > MemoryDenyWriteExecute=
> > Takes a boolean argument. If set, attempts to create memory mappings
> > that are writable and executable at the same time, or to change existing
> > memory mappings to become executable, or mapping shared memory segments
> > as executable, are prohibited.
>
> Does this also work for SELinux deny_execmem?

Yes.

> What does `getsebool -a | grep deny_execmem` say?

[hjl@vm-fedora-1 ~]$ getsebool -a | grep deny_execmem
deny_execmem --> on
[hjl@vm-fedora-1 ~]$ export GLIBC_TUNABLES=glibc.cpu.plt_rewrite=2
[hjl@vm-fedora-1 ~]$ strace ls 2>&1 | grep -E "(mprotect|mmap|munmap)"
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x556e12978000
mprotect(0x556e12978000, 4096, PROT_READ|PROT_EXEC) = -1 EACCES
(Permission denied)
munmap(0x556e12978000, 4096)            = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x556e12977000
mmap(NULL, 80651, PROT_READ, MAP_PRIVATE, 3, 0) = 0x556e12963000
mmap(NULL, 181880, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x556e12936000
mmap(0x556e1293d000, 114688, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x556e1293d000
mmap(0x556e12959000, 24576, PROT_READ,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23000) = 0x556e12959000
mmap(0x556e1295f000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x29000) = 0x556e1295f000
mmap(0x556e12961000, 5752, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x556e12961000
mmap(NULL, 36920, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x556e1292c000
mmap(0x556e1292e000, 16384, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x556e1292e000
mmap(0x556e12932000, 8192, PROT_READ,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x556e12932000
mmap(0x556e12934000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x556e12934000
mmap(NULL, 1973104, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x556e1274a000
mmap(0x556e12770000, 1441792, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x556e12770000
mmap(0x556e128d0000, 319488, PROT_READ,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x186000) = 0x556e128d0000
mmap(0x556e1291e000, 24576, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d3000) = 0x556e1291e000
mmap(0x556e12924000, 31600, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x556e12924000
mmap(NULL, 631344, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x556e126af000
mmap(0x556e126b2000, 450560, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x556e126b2000
mmap(0x556e12720000, 163840, PROT_READ,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x71000) = 0x556e12720000
mmap(0x556e12748000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x98000) = 0x556e12748000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0x556e126ad000
mprotect(0x556e1291e000, 16384, PROT_READ) = 0
mprotect(0x556e12748000, 4096, PROT_READ) = 0
mprotect(0x556e12934000, 4096, PROT_READ) = 0
mprotect(0x556e1295f000, 4096, PROT_READ) = 0
mprotect(0x556e1299b000, 8192, PROT_READ) = 0
mprotect(0x7f11917fa000, 8192, PROT_READ) = 0
munmap(0x556e12963000, 80651)           = 0
mmap(NULL, 224366320, PROT_READ, MAP_PRIVATE, 3, 0) = 0x556e05000000
[hjl@vm-fedora-1 ~]$

> What does your patched glibc do with `semanage boolean --modify deny_execmem --on`?
>
> WARNING: Do not attempt deny_execmen on anything but a VM you can spare having crash
> to just a terminal.
>
> > When it is set, systemd service stops working if PLT rewrite is enabled.
> > Check if mprotect works before rewriting PLT.  This fixes BZ #31230.
> > ---
> >  .../unix/sysv/linux/x86_64/dl-plt-rewrite.h   | 43 +++++++++++++++++++
> >  sysdeps/x86/cpu-features.c                    |  4 +-
> >  sysdeps/x86_64/dl-plt-rewrite.h               | 25 +++++++++++
> >  3 files changed, 71 insertions(+), 1 deletion(-)
> >  create mode 100644 sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> >  create mode 100644 sysdeps/x86_64/dl-plt-rewrite.h
> >
> > diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> > new file mode 100644
> > index 0000000000..6401b7b2f2
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
> > @@ -0,0 +1,43 @@
> > +/* PLT rewrite help function.  Linux/x86-64 version.
>
> s/help/helper/g

Fixed.

> > +   Copyright (C) 2024 Free Software Foundation, Inc.
> > +
> > +   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
> > +   <https://www.gnu.org/licenses/>.  */
> > +
> > +#include <stdbool.h>
> > +#include <sys/mman.h>
> > +
> > +static __always_inline bool
> > +dl_plt_rewrite_supported (void)
> > +{
> > +  /* PLT rewrite is enabled.  Check if mprotect works.  */
> > +  void *plt = (void *) INTERNAL_SYSCALL_CALL (mmap, NULL, 4096,
> > +                                           PROT_READ | PROT_WRITE,
> > +                                           MAP_PRIVATE | MAP_ANONYMOUS,
> > +                                           -1, 0);
> > +  if (__glibc_unlikely (plt == MAP_FAILED))
> > +    return false;
> > +
> > +  /* Touch the PROT_READ | PROT_WRITE page.  */
> > +  *(int32_t *) plt = 1;
> > +
> > +  /* If the updated PROT_READ | PROT_WRITE page can be changed to
> > +     PROT_EXEC | PROT_READ, rewrite PLT.  */
> > +  bool status = (INTERNAL_SYSCALL_CALL (mprotect, plt, 4096,
> > +                                     PROT_EXEC | PROT_READ) == 0);
>
> Is it specifically required that you go from RW to RWE?

That simulates what PLT rewrite does:

static inline void
x86_64_rewrite_plt_in_place (struct link_map *map)
{
  /* Adjust DT_X86_64_PLT address and DT_X86_64_PLTSZ values.  */
  ElfW(Addr) plt = (map->l_info[DT_X86_64 (PLT)]->d_un.d_ptr
                    + map->l_addr);
  size_t pagesize = GLRO(dl_pagesize);
  ElfW(Addr) plt_aligned = ALIGN_DOWN (plt, pagesize);
  size_t pltsz = (map->l_info[DT_X86_64 (PLTSZ)]->d_un.d_val
                  + plt - plt_aligned);

  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
    _dl_debug_printf ("\nchanging PLT in '%s' to writable\n",
                      DSO_FILENAME (map->l_name));

  if (__glibc_unlikely (__mprotect ((void *) plt_aligned, pltsz,
                                    PROT_WRITE | PROT_READ) < 0))
    {
      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
        _dl_debug_printf ("\nfailed to change PLT in '%s' to writable\n",
                          DSO_FILENAME (map->l_name));
      return;
    }

  x86_64_rewrite_plt (map, plt_aligned);

  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
    _dl_debug_printf ("\nchanging PLT in '%s' back to read-only\n",
                      DSO_FILENAME (map->l_name));

  if (__glibc_unlikely (__mprotect ((void *) plt_aligned, pltsz,
                                    PROT_EXEC | PROT_READ) < 0))
    _dl_signal_error (0, DSO_FILENAME (map->l_name), NULL,
                      "failed to change PLT back to read-only");
}

> Could you start with just an RWE page and go to RE?

I'd like to make it as close to PLT rewrite steps as possible.

> > +
> > +  INTERNAL_SYSCALL_CALL (munmap, plt, 4096);
> > +
> > +  return status;
> > +}
> > diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c
> > index 46bdaffbc2..6aaa750e20 100644
> > --- a/sysdeps/x86/cpu-features.c
> > +++ b/sysdeps/x86/cpu-features.c
> > @@ -28,10 +28,12 @@ extern void TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *)
> >    attribute_hidden;
> >
> >  #if defined SHARED && defined __x86_64__
> > +# include <dl-plt-rewrite.h>
> > +
> >  static void
> >  TUNABLE_CALLBACK (set_plt_rewrite) (tunable_val_t *valp)
> >  {
> > -  if (valp->numval != 0)
> > +  if (valp->numval != 0 && dl_plt_rewrite_supported ())
>
> Suggest comment here:
>
> /* We must be careful about where we put the call to
>    dl_plt_rewrite_supported() since it may generate
>    spurious SELinux log entries.  It should only be
>    attempted if the user requested a PLT rewrite.  */

Fixed.

> The && short-circuit means we won't test the page support unless the tunable
> is non-zero. This is important because we don't want logging to SELinux or
> systemd for the rewrite test if the feature isn't being used.
>
> >      {
> >        /* Use JMPABS only on APX processors.  */
> >        const struct cpu_features *cpu_features = __get_cpu_features ();
> > diff --git a/sysdeps/x86_64/dl-plt-rewrite.h b/sysdeps/x86_64/dl-plt-rewrite.h
> > new file mode 100644
> > index 0000000000..7eaae8f457
> > --- /dev/null
> > +++ b/sysdeps/x86_64/dl-plt-rewrite.h
> > @@ -0,0 +1,25 @@
> > +/* PLT rewrite help function.  x86-64 version.
>
> s/help/helper/g

Fixed.

> > +   Copyright (C) 2024 Free Software Foundation, Inc.
> > +
> > +   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
> > +   <https://www.gnu.org/licenses/>.  */
> > +
> > +#include <stdbool.h>
> > +#include <sys/mman.h>
> > +
> > +static __always_inline bool
> > +dl_plt_rewrite_supported (void)
> > +{
> > +  return true;
> > +}
>
> --
> Cheers,
> Carlos.
>

Will send the v2 shortly.

Thanks.
  
H.J. Lu Jan. 12, 2024, 6:21 p.m. UTC | #4
On Fri, Jan 12, 2024 at 10:09 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> Will send the v2 shortly.

Here it is:

https://patchwork.sourceware.org/project/glibc/list/?series=29649
  

Patch

diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
new file mode 100644
index 0000000000..6401b7b2f2
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/dl-plt-rewrite.h
@@ -0,0 +1,43 @@ 
+/* PLT rewrite help function.  Linux/x86-64 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdbool.h>
+#include <sys/mman.h>
+
+static __always_inline bool
+dl_plt_rewrite_supported (void)
+{
+  /* PLT rewrite is enabled.  Check if mprotect works.  */
+  void *plt = (void *) INTERNAL_SYSCALL_CALL (mmap, NULL, 4096,
+					      PROT_READ | PROT_WRITE,
+					      MAP_PRIVATE | MAP_ANONYMOUS,
+					      -1, 0);
+  if (__glibc_unlikely (plt == MAP_FAILED))
+    return false;
+
+  /* Touch the PROT_READ | PROT_WRITE page.  */
+  *(int32_t *) plt = 1;
+
+  /* If the updated PROT_READ | PROT_WRITE page can be changed to
+     PROT_EXEC | PROT_READ, rewrite PLT.  */
+  bool status = (INTERNAL_SYSCALL_CALL (mprotect, plt, 4096,
+					PROT_EXEC | PROT_READ) == 0);
+
+  INTERNAL_SYSCALL_CALL (munmap, plt, 4096);
+
+  return status;
+}
diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c
index 46bdaffbc2..6aaa750e20 100644
--- a/sysdeps/x86/cpu-features.c
+++ b/sysdeps/x86/cpu-features.c
@@ -28,10 +28,12 @@  extern void TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *)
   attribute_hidden;
 
 #if defined SHARED && defined __x86_64__
+# include <dl-plt-rewrite.h>
+
 static void
 TUNABLE_CALLBACK (set_plt_rewrite) (tunable_val_t *valp)
 {
-  if (valp->numval != 0)
+  if (valp->numval != 0 && dl_plt_rewrite_supported ())
     {
       /* Use JMPABS only on APX processors.  */
       const struct cpu_features *cpu_features = __get_cpu_features ();
diff --git a/sysdeps/x86_64/dl-plt-rewrite.h b/sysdeps/x86_64/dl-plt-rewrite.h
new file mode 100644
index 0000000000..7eaae8f457
--- /dev/null
+++ b/sysdeps/x86_64/dl-plt-rewrite.h
@@ -0,0 +1,25 @@ 
+/* PLT rewrite help function.  x86-64 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdbool.h>
+#include <sys/mman.h>
+
+static __always_inline bool
+dl_plt_rewrite_supported (void)
+{
+  return true;
+}