[PATCH 07/12] riscv/cfi: Enable CFI on static binaries

Deepak Gupta debug@rivosinc.com
Wed Jun 25 22:11:41 GMT 2025


On Wed, Jun 18, 2025 at 01:42:53AM -0700, Jesse Huang wrote:
>For static binaries, CFI are enabled inside ARCH_SETUP_TLS, with a macro
>to enable the shadow stack to prevent underflowing the shadow stack on
>return, and with a function _dl_cfi_setup_features to enable landing
>pad.
>
>It scans backward of the program header to find the PT_GNU_PROPERTY note
>first, then enable CFI features corresponding to the feature bits.

Overall comment on `feature_1` checking. We are checking
for the presence of flag `GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED`.

I believe we should make it configurable. Binary can have 
`GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG` flag as well and if all the
binaryies in address space function signature label, then also libc should
be able to send the prctl to kernel enabling landing pad. Please note that
kernel is not aware of landing pad scheme in userspace, all it knows is to
enable landing pad for userspace.

>
>Co-authored-by: Deepak Gupta <debug@rivosinc.com>
>---
> sysdeps/riscv/Makefile                 |  1 +
> sysdeps/riscv/dl-cfi.c                 | 36 +++++++++++
> sysdeps/riscv/dl-machine.h             |  6 ++
> sysdeps/riscv/dl-prop.h                | 65 +++++++++++++++++++
> sysdeps/riscv/libc-start.h             | 88 ++++++++++++++++++++++++++
> sysdeps/unix/sysv/linux/riscv/dl-cfi.h | 20 ++++++
> 6 files changed, 216 insertions(+)
> create mode 100644 sysdeps/riscv/dl-cfi.c
> create mode 100644 sysdeps/riscv/dl-prop.h
> create mode 100644 sysdeps/riscv/libc-start.h
> create mode 100644 sysdeps/unix/sysv/linux/riscv/dl-cfi.h
>
>diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
>index 99976fddad..4752bdb1a0 100644
>--- a/sysdeps/riscv/Makefile
>+++ b/sysdeps/riscv/Makefile
>@@ -18,6 +18,7 @@ endif
>
> # Enable RISC-V CFI
> ifeq (yes,$(riscv-enable-cfi))
>+sysdep-dl-routines += dl-cfi
> CFLAGS-.o += -fcf-protection=full
> CFLAGS-.os += -fcf-protection=full
> CFLAGS-.op += -fcf-protection=full
>diff --git a/sysdeps/riscv/dl-cfi.c b/sysdeps/riscv/dl-cfi.c
>new file mode 100644
>index 0000000000..77bac24a1b
>--- /dev/null
>+++ b/sysdeps/riscv/dl-cfi.c
>@@ -0,0 +1,36 @@
>+/* RISC-V CFI extensions (zicfilp/zicfiss) functions.
>+   Copyright (C) 2025 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 <asm-generic/errno-base.h>
>+#include <unistd.h>
>+#include <libintl.h>
>+#include <ldsodefs.h>
>+#include <dl-cfi.h>
>+#include <sys/mman.h>
>+
>+attribute_hidden void
>+_dl_cfi_setup_features (unsigned int feature_1)
>+{
>+  /* Since prctl could fail to enable some features
>+     use prctl to get enabled features again and sync it back.  */
>+#ifdef __riscv_landing_pad
>+  if (feature_1 & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
>+    INTERNAL_SYSCALL_CALL (prctl, PR_SET_INDIR_BR_LP_STATUS,
>+                           PR_INDIR_BR_LP_ENABLE, 0, 0, 0);
>+#endif /* __riscv_landing_pad  */
>+  /* FIXME: Read enabled features from kernel and re-sync  */
>+}
>diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
>index 716cc65cd6..69872d4614 100644
>--- a/sysdeps/riscv/dl-machine.h
>+++ b/sysdeps/riscv/dl-machine.h
>@@ -28,6 +28,12 @@
> #include <dl-irel.h>
> #include <dl-static-tls.h>
> #include <dl-machine-rel.h>
>+#if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack)
>+# include <dl-cfi.h>
>+extern void _dl_cfi_setup_features (unsigned int features);
>+#else
>+# define RTLD_START_ENABLE_RISCV_CFI
>+#endif
> /* FIXME: Use the definition from sysdep.h  */
> #ifdef __riscv_landing_pad_unlabeled
> # define SET_LPAD   lui  t2, 0
>diff --git a/sysdeps/riscv/dl-prop.h b/sysdeps/riscv/dl-prop.h
>new file mode 100644
>index 0000000000..a832e4aea6
>--- /dev/null
>+++ b/sysdeps/riscv/dl-prop.h
>@@ -0,0 +1,65 @@
>+/* Support for GNU properties.  RISC-V version.
>+   Copyright (C) 2025 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
>+   <https://www.gnu.org/licenses/>.  */
>+
>+#ifndef _DL_PROP_H
>+#define _DL_PROP_H
>+
>+static inline void __attribute__ ((always_inline))
>+_rtld_main_check (struct link_map *m, const char *program)
>+{
>+}
>+
>+static inline void __attribute__ ((always_inline))
>+_dl_open_check (struct link_map *m)
>+{
>+}
>+
>+static inline void __attribute__ ((always_inline))
>+_dl_process_pt_note (struct link_map *l, int fd, const ElfW(Phdr) *ph)
>+{
>+}
>+
>+static inline int
>+_dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
>+			  uint32_t datasz, void *data)
>+{
>+  /* FIXME: Detect cpu features after we have it implemented in glibc  */
>+
>+  if (type == GNU_PROPERTY_RISCV_FEATURE_1_AND)
>+    {
>+      /* Stop if the property note is ill-formed.  */
>+      if (datasz != 4)
>+        return -1;
>+
>+#if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack)
>+      unsigned int feature_1 = *(unsigned int *) data;
>+#endif
>+#ifdef __riscv_landing_pad
>+      if (feature_1 & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
>+        l->l_riscv_feature_1_and |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
>+#endif
>+#ifdef __riscv_shadow_stack
>+      if (feature_1 & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
>+        l->l_riscv_feature_1_and |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
>+#endif
>+    }
>+  /* Continue.  */
>+  return 1;
>+}
>+
>+#endif /* _DL_PROP_H */
>diff --git a/sysdeps/riscv/libc-start.h b/sysdeps/riscv/libc-start.h
>new file mode 100644
>index 0000000000..7b898f5308
>--- /dev/null
>+++ b/sysdeps/riscv/libc-start.h
>@@ -0,0 +1,88 @@
>+/* RISC-V libc main startup.
>+   Copyright (C) 2025 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
>+   <https://www.gnu.org/licenses/>.  */
>+
>+#ifndef SHARED
>+# define ARCH_SETUP_IREL() apply_irel ()
>+# define ARCH_APPLY_IREL()
>+
>+# if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack)
>+/* Get shadow stack features enabled in the static executable.  */
>+#  include <asm/prctl.h>
>+#  include <sys/prctl.h>
>+extern void _dl_cfi_setup_features (unsigned int);
>+
>+static inline unsigned int
>+get_cfi_feature (void)
>+{
>+  unsigned int cfi_feature = 0;
>+  /* FIXME: check if cfi feature is supported by CPU  */
>+  struct link_map *main_map = _dl_get_dl_main_map ();
>+
>+  /* Scan program headers backward to check PT_GNU_PROPERTY early for
>+     feature bits on static executable.  */
>+  const ElfW(Phdr) *phdr = GL(dl_phdr);
>+  const ElfW(Phdr) *ph;
>+  for (ph = phdr + GL(dl_phnum); ph != phdr; ph--)
>+    if (ph[-1].p_type == PT_GNU_PROPERTY)
>+      {
>+        _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
>+        /* Enable landing pad and shstk only if they are enabled on a static
>+           executable.  */
>+        /* FIXME: change to &= to mask off other features after cpu_feature
>+           is implemented  */
>+        cfi_feature = (main_map->l_riscv_feature_1_and
>+                       & (GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
>+                          | GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS));
>+
>+        GL(dl_riscv_feature_1) = cfi_feature;
>+        return cfi_feature;
>+      }
>+  GL(dl_riscv_feature_1) = 0;
>+  return 0;
>+}
>+
>+/* The function using this macro to enable shadow stack must not return
>+   to avoid shadow stack underflow.  */
>+#  ifdef __riscv_shadow_stack
>+#   define ENABLE_RISCV_SHADOW_STACK                                    \
>+  do                                                                    \
>+    {                                                                   \
>+      if (feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)                \
>+        {                                                               \
>+          INTERNAL_SYSCALL_CALL (prctl, PR_SET_SHADOW_STACK_STATUS,     \
>+                                 PR_SHADOW_STACK_ENABLE, 0, 0, 0);      \
>+        }                                                               \
>+    }                                                                   \
>+  while (0)
>+#  else
>+#   define ENABLE_RISCV_SHADOW_STACK
>+#  endif
>+
>+#  define ARCH_SETUP_TLS()                                              \
>+  {                                                                     \
>+    __libc_setup_tls ();                                                \
>+                                                                        \
>+    unsigned int feature = get_cfi_feature ();                          \
>+    ENABLE_RISCV_SHADOW_STACK;                                          \
>+    /* Landing pad will be enabled in _dl_cfi_setup_features  */        \
>+    _dl_cfi_setup_features(feature);                                    \
>+  }
>+# else
>+#  define ARCH_SETUP_TLS()      __libc_setup_tls ()
>+# endif /* __riscv_landing_pad || __riscv_shadow_stack */
>+#endif /* !SHARED */
>diff --git a/sysdeps/unix/sysv/linux/riscv/dl-cfi.h b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
>new file mode 100644
>index 0000000000..cd184bac61
>--- /dev/null
>+++ b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
>@@ -0,0 +1,20 @@
>+/* Linux/RISC-V CFI initializers function.
>+   Copyright (C) 2025 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/>.  */
>+
>+/* FIXME: Should be remove after they are included in the kernel header  */
>+#include <asm/prctl.h>
>+#include <sys/prctl.h>
>-- 
>2.39.3
>


More information about the Libc-alpha mailing list