[v4,08/17] riscv/cfi: Enable CFI on dynamic binaries

Message ID 20260526061703.2188042-9-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 dynamic binaries, CFI features are parsed from GNU properties, store
in to GLRO(dl_riscv_feature_1) and later enabled in RTLD_START.

Co-authored-by: Deepak Gupta <debug@rivosinc.com>
---
 sysdeps/riscv/Makefile                 |  2 +-
 sysdeps/riscv/dl-cfi.c                 | 72 ++++++++++++++++++++++++++
 sysdeps/riscv/dl-machine.h             |  2 +
 sysdeps/riscv/dl-prop.h                |  9 ++++
 sysdeps/riscv/features-offsets.sym     |  5 ++
 sysdeps/unix/sysv/linux/riscv/dl-cfi.h | 30 +++++++++++
 6 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 sysdeps/riscv/features-offsets.sym
  

Patch

diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
index 4752bdb1a0..11e48be02e 100644
--- a/sysdeps/riscv/Makefile
+++ b/sysdeps/riscv/Makefile
@@ -3,7 +3,7 @@  sysdep_headers += sys/asm.h
 endif
 
 ifeq ($(subdir),elf)
-gen-as-const-headers += dl-link.sym
+gen-as-const-headers += dl-link.sym features-offsets.sym
 endif
 
 # RISC-V's assembler also needs to know about PIC as it changes the definition
diff --git a/sysdeps/riscv/dl-cfi.c b/sysdeps/riscv/dl-cfi.c
index e9beec4b8f..9bb0000103 100644
--- a/sysdeps/riscv/dl-cfi.c
+++ b/sysdeps/riscv/dl-cfi.c
@@ -22,6 +22,54 @@ 
 #include <dl-cfi.h>
 #include <sys/mman.h>
 
+static void
+dl_check_legacy_object (struct link_map *m, unsigned int *feature_1)
+{
+  /* Iterate through the dependencies and disable if needed here  */
+  struct link_map *l = NULL;
+  unsigned int i;
+  i = m->l_searchlist.r_nlist;
+  while (i-- > 0)
+    {
+      /* Check each shared object to see if shadow stack and landing pad
+         are enabled.  */
+      l = m->l_initfini[i];
+
+      if (l->l_init_called)
+        continue;
+
+#ifdef SHARED
+      /* Skip check for ld.so since it has the features enabled. The
+         features will be disabled later if they are not enabled in
+	 executable.  */
+      if (l == &GL(dl_rtld_map)
+          || l->l_real == &GL(dl_rtld_map))
+        continue;
+#endif /* SHARED  */
+
+      *feature_1 &= l->l_riscv_feature_1_and;
+    }
+}
+
+#ifdef SHARED
+static void
+dl_cfi_check_startup (struct link_map *m, unsigned int *feature_1)
+{
+  /* FIXME: Add tunables here  */
+  if (!*feature_1)
+    return;
+  dl_check_legacy_object (m, feature_1);
+
+  /* Update GL(dl_riscv_feature_1)  */
+  GL(dl_riscv_feature_1) = *feature_1;
+}
+#endif /* SHARED  */
+
+static void
+dl_cfi_check_dlopen (struct link_map *m)
+{
+}
+
 attribute_hidden void
 _dl_cfi_setup_features (unsigned int feature_1)
 {
@@ -34,3 +82,27 @@  _dl_cfi_setup_features (unsigned int feature_1)
 #endif /* __riscv_landing_pad  */
   /* FIXME: Read enabled features from kernel and re-sync  */
 }
+
+/* Enable CFI for l and its dependencies.  */
+void
+_dl_cfi_check (struct link_map *l, const char *program)
+{
+    /* As this point we have parsed the gnu properties,
+       for dynamic binary we should verify the dependencies here.  */
+    /* FIXME: Implement different policy for supporting legacy binaries  */
+  unsigned int feature_1;
+#if defined SHARED && defined RTLD_START_ENABLE_RISCV_CFI
+  if (program)
+    {
+      GL(dl_riscv_feature_1) = l->l_riscv_feature_1_and;
+      feature_1 = l->l_riscv_feature_1_and;
+    }
+#endif /* SHARED  */
+
+#ifdef SHARED
+  if (program)
+    dl_cfi_check_startup (l, &feature_1);
+  else
+#endif /* SHARED  */
+    dl_cfi_check_dlopen (l);
+}
diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
index b7b9959d58..b64e7e67d8 100644
--- a/sysdeps/riscv/dl-machine.h
+++ b/sysdeps/riscv/dl-machine.h
@@ -121,6 +121,8 @@  elf_machine_dynamic (void)
 	" _RTLD_PROLOGUE (_dl_start_user) "\
 	# Stash user entry point in s0.\n\
 	mv s0, a0\n\
+	# Setup CFI features\n\
+	" RTLD_START_ENABLE_RISCV_CFI "\
 	# Load the adjusted argument count.\n\
 	" STRINGXP (REG_L) " a1, 0(sp)\n\
 	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
diff --git a/sysdeps/riscv/dl-prop.h b/sysdeps/riscv/dl-prop.h
index a183d3148a..f6cbf4c59c 100644
--- a/sysdeps/riscv/dl-prop.h
+++ b/sysdeps/riscv/dl-prop.h
@@ -19,14 +19,23 @@ 
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+extern void _dl_cfi_check (struct link_map *, const char *)
+    attribute_hidden;
+
 static inline void __attribute__ ((always_inline))
 _rtld_main_check (struct link_map *m, const char *program)
 {
+#if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack)
+  _dl_cfi_check(m, program);
+#endif /* __riscv_landing_pad || __riscv_shadow_stack */
 }
 
 static inline void __attribute__ ((always_inline))
 _dl_open_check (struct link_map *m, int dl_openmode)
 {
+#if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack)
+  _dl_cfi_check(m, NULL);
+#endif /* __riscv_landing_pad || __riscv_shadow_stack */
 }
 
 static inline void __attribute__ ((always_inline))
diff --git a/sysdeps/riscv/features-offsets.sym b/sysdeps/riscv/features-offsets.sym
new file mode 100644
index 0000000000..3320cde83f
--- /dev/null
+++ b/sysdeps/riscv/features-offsets.sym
@@ -0,0 +1,5 @@ 
+#define SHARED 1
+
+#include <ldsodefs.h>
+
+RTLD_GLOBAL_DL_RISCV_FEATURE_1_OFFSET offsetof (struct rtld_global, _dl_riscv_feature_1)
diff --git a/sysdeps/unix/sysv/linux/riscv/dl-cfi.h b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
index 86ba6eaafb..53df470930 100644
--- a/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
+++ b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
@@ -18,3 +18,33 @@ 
 /* FIXME: Should be remove after they are included in the kernel header  */
 #include <asm/prctl.h>
 #include <sys/prctl.h>
+#include <features-offsets.h>
+
+#ifdef __riscv_shadow_stack
+# define CHECK_AND_ENABLE_SHADOW_STACK \
+"\
+    andi  a0, s1, " STRINGXP (GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS) "\n\
+    beqz  a0, 1f \n\
+    li    a0, " STRINGXP (PR_SET_SHADOW_STACK_STATUS) "\n\
+    li    a1, " STRINGXP (PR_SHADOW_STACK_ENABLE) "\n\
+    li    a2, 0 \n\
+    li    a3, 0 \n\
+    li    a4, 0 \n\
+    li    a7, " STRINGXP (__NR_prctl) "\n\
+    ecall \n\
+1: \n\
+"
+#else
+# define CHECK_AND_ENABLE_SHADOW_STACK
+#endif
+
+#define RTLD_START_ENABLE_RISCV_CFI \
+"\
+    lw   s1, _rtld_local + " STRINGXP (RTLD_GLOBAL_DL_RISCV_FEATURE_1_OFFSET) " \n\
+    # We need to enable shadow stack in the assembly code to avoid underflow \n\
+    # Checking for landing pad is left to _dl_cfi_setup_features \n\
+    " CHECK_AND_ENABLE_SHADOW_STACK "\n\
+    mv   a0, s1 \n\
+    jal  _dl_cfi_setup_features \n\
+    \n\
+"