[v4,11/17] riscv/cfi: Support locking/disabling CFI and move OS depedent code

Message ID 20260526061703.2188042-12-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
  ---
 sysdeps/riscv/dl-cfi.c                 | 40 +++++++++++-----
 sysdeps/unix/sysv/linux/riscv/dl-cfi.h | 65 ++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 11 deletions(-)
  

Patch

diff --git a/sysdeps/riscv/dl-cfi.c b/sysdeps/riscv/dl-cfi.c
index 275faddcc9..27ac4cff10 100644
--- a/sysdeps/riscv/dl-cfi.c
+++ b/sysdeps/riscv/dl-cfi.c
@@ -57,7 +57,7 @@  struct dl_cfi_info
 static void
 dl_check_legacy_object (struct link_map *m, struct dl_cfi_info *info)
 {
-  /* Iterate through the dependencies and disable if needed here  */
+  /* Iterate through the dependencies and record legacy objects  */
   struct link_map *l = NULL;
   unsigned int i;
   i = m->l_searchlist.r_nlist;
@@ -86,7 +86,11 @@  dl_check_legacy_object (struct link_map *m, struct dl_cfi_info *info)
                                   | ~(GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
                                      | GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS));
 
-      /* Bookkeeping legacy objects  */
+      /* Bookkeeping first found mismatch object for both lp/ss.
+         These information would only be used by dlopen check for now.
+         A dependency with a feature on will be record as legacy if the task
+         did not enable the feature, however it is safe because the following
+         check will only be performed if the task has the feature on.  */
 #ifdef __riscv_landing_pad
       if ((info->feature_1_legacy & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) == 0
           && ((info->enable_feature_1 & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
@@ -208,8 +212,8 @@  dl_cfi_check_dlopen (struct link_map *m, struct dl_cfi_info *info)
     _dl_signal_error (0, m->l_initfini[legacy_obj]->l_name, "dlopen", msg);
 
   if (disable_feature_1 != 0)
-      // FIXME: Disable CFI here
-      int res = -1;
+    {
+      int res = dl_cfi_disable_cfi (disable_feature_1);
       if (res)
         {
           msg = N_("can't disable CFI feature");
@@ -223,14 +227,29 @@  dl_cfi_check_dlopen (struct link_map *m, struct dl_cfi_info *info)
 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.  */
+  /* Enable features. Shadow stack is enabled earlier as it should
+   * be enabled in a function that never returns.  */
+#ifdef __riscv_landing_pad
+  dl_cfi_enable_lp (feature_1);
+#endif /* __riscv_landing_pad  */
+
+  /* Since we could failed to enable some features,
+     get enabled features from system again and sync it back.  */
+  int status = dl_cfi_get_cfi_status ();
+  GL(dl_riscv_feature_1) = status | (GL(dl_riscv_feature_1) &
+                           ~(GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS
+                             | GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED));
+  
+  /* Lock features if set to always_on  */
 #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);
+  if (GL(dl_riscv_feature_control).lp == cfi_always_on)
+      dl_cfi_lock_cfi (GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED);
 #endif /* __riscv_landing_pad  */
-  /* FIXME: Read enabled features from kernel and re-sync  */
+#ifdef __riscv_shadow_stack
+  if (GL(dl_riscv_feature_control).ss == cfi_always_on)
+      dl_cfi_lock_cfi (GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS);
+#endif /* __riscv_shadow_stack  */
+  /* FIXME: Should we terminate if failed to lock under always on mode? */
 }
 
 /* Enable CFI for l and its dependencies.  */
@@ -287,7 +306,6 @@  _dl_cfi_check (struct link_map *l, const char *program)
   info.feature_1_legacy_ss = 0;
 #endif
 
-  info.feature_1_enabled = GL(dl_riscv_feature_1);
   info.feature_1_legacy = 0;
 
 #ifdef SHARED
diff --git a/sysdeps/unix/sysv/linux/riscv/dl-cfi.h b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
index 53df470930..9758fbf0e3 100644
--- a/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
+++ b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h
@@ -48,3 +48,68 @@ 
     jal  _dl_cfi_setup_features \n\
     \n\
 "
+
+static __always_inline int
+dl_cfi_disable_cfi (unsigned int feature) {
+  int res = 0;
+#ifdef __riscv_landing_pad
+  if (feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
+    {
+      res = prctl (PR_SET_INDIR_BR_LP_STATUS, 0, 0, 0, 0);
+      if (res)
+        return res;
+    }
+#endif /* __riscv_landing_pad  */
+#ifdef __riscv_shadow_stack
+  if (feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
+    {
+      res |= prctl (PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
+      if (res)
+        return res;
+    }
+#endif /* __riscv_shadow_stack  */
+  return 0;
+}
+
+static __always_inline int
+dl_cfi_lock_cfi (unsigned int feature)
+{
+  int res = 0;
+#ifdef __riscv_landing_pad
+  if (feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
+    res |= prctl (PR_LOCK_INDIR_BR_LP_STATUS, 0, 0, 0, 0);
+#endif /* __riscv_landing_pad  */
+#ifdef __riscv_shadow_stack
+  if (feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
+    res |= prctl (PR_LOCK_SHADOW_STACK_STATUS, 0, 0, 0, 0);
+#endif /* __riscv_shadow_stack  */
+  return res;
+}
+
+static __always_inline int
+dl_cfi_get_cfi_status (void) {
+  int status = 0;
+  unsigned long buf = 0;
+  int ret = 0;
+#ifdef __riscv_landing_pad
+    ret = prctl (PR_GET_INDIR_BR_LP_STATUS, &buf, 0, 0, 0);
+    if (!ret && buf)
+      status |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
+#endif /* __riscv_landing_pad  */
+#ifdef __riscv_shadow_stack
+    ret = prctl (PR_GET_SHADOW_STACK_STATUS, &buf, 0, 0, 0);
+    if (!ret && buf)
+      status |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
+#endif /* __riscv_shadow_stack  */
+  return status;
+}
+
+#ifdef __riscv_landing_pad
+static __always_inline int
+dl_cfi_enable_lp (unsigned int feature) {
+  if (!(feature & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED))
+    return -1;
+  return INTERNAL_SYSCALL_CALL (prctl, PR_SET_INDIR_BR_LP_STATUS,
+                                PR_INDIR_BR_LP_ENABLE, 0, 0, 0);
+}
+#endif /* __riscv_landing_pad  */