[v4,07/17] riscv/cfi: Enable CFI on static binaries

Message ID 20260526061703.2188042-8-jesse.huang@sifive.com (mailing list archive)
State New
Headers
Series Support RISC-V Control Flow Integrifty (CFI) |

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-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Jesse Huang May 26, 2026, 6:16 a.m. UTC
  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.

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
  

Patch

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..e9beec4b8f
--- /dev/null
+++ b/sysdeps/riscv/dl-cfi.c
@@ -0,0 +1,36 @@ 
+/* RISC-V CFI extensions (zicfilp/zicfiss) functions.
+   Copyright (C) 2026 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 05992c8705..b7b9959d58 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
 /* This is a marker to remind us to add real expansion to setup the label
    for the function signature label scheme in the future  */
 #ifdef __riscv_landing_pad_unlabeled
diff --git a/sysdeps/riscv/dl-prop.h b/sysdeps/riscv/dl-prop.h
new file mode 100644
index 0000000000..a183d3148a
--- /dev/null
+++ b/sysdeps/riscv/dl-prop.h
@@ -0,0 +1,65 @@ 
+/* Support for GNU properties.  RISC-V version.
+   Copyright (C) 2026 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, int dl_openmode)
+{
+}
+
+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..91d094cfb5
--- /dev/null
+++ b/sysdeps/riscv/libc-start.h
@@ -0,0 +1,88 @@ 
+/* RISC-V libc main startup.
+   Copyright (C) 2026 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..86ba6eaafb
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
@@ -0,0 +1,20 @@ 
+/* Linux/RISC-V CFI initializers function.
+   Copyright (C) 2026 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>