[PATCH v5 09/16] riscv/cfi: Introduce tunables for CFI features

Gabriel Ravier gabravier@gmail.com
Sun Sep 13 19:05:25 GMT 2026


On 6/28/26 9:02 AM, Jesse Huang wrote:
> dl_riscv_feature_control is a structure whose members represent feature
> configurations.  It is currently used only by CFI features.
>
> Each CFI feature is a 2-bit enum with values [on|off|permissive].  These
> values decide whether a newly loaded legacy object should be blocked when
> loaded dynamically, and can be controlled by glibc tunables.
> ---
>   manual/tunables.texi                |  22 +++
>   sysdeps/riscv/Makefile              |   1 +
>   sysdeps/riscv/cpu-features.c        |  46 ++++++
>   sysdeps/riscv/cpu-tunables.c        |  50 +++++++
>   sysdeps/riscv/dl-cfi.c              | 223 ++++++++++++++++++++++++++--
>   sysdeps/riscv/dl-get-cpu-features.c |  27 ++++
>   sysdeps/riscv/dl-machine.h          |  19 +++
>   sysdeps/riscv/dl-procruntime.c      |  17 +++
>   sysdeps/riscv/dl-tunables.list      |  27 ++++
>   sysdeps/riscv/feature-control.h     |  42 ++++++
>   sysdeps/riscv/ldsodefs.h            |   1 +
>   sysdeps/riscv/libc-start.c          |  31 ++++
>   sysdeps/riscv/libc-start.h          |  13 +-
>   13 files changed, 500 insertions(+), 19 deletions(-)
>   create mode 100644 sysdeps/riscv/cpu-features.c
>   create mode 100644 sysdeps/riscv/cpu-tunables.c
>   create mode 100644 sysdeps/riscv/dl-get-cpu-features.c
>   create mode 100644 sysdeps/riscv/dl-tunables.list
>   create mode 100644 sysdeps/riscv/feature-control.h
>   create mode 100644 sysdeps/riscv/libc-start.c
>
> [snip]
>
> diff --git a/sysdeps/riscv/dl-cfi.c b/sysdeps/riscv/dl-cfi.c
> index 74aa8b89ce..4723cde096 100644
> --- a/sysdeps/riscv/dl-cfi.c
> +++ b/sysdeps/riscv/dl-cfi.c
> @@ -15,6 +15,7 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> +#include "feature-control.h"
>   #include <asm-generic/errno-base.h>
>   #include <unistd.h>
>   #include <libintl.h>
> @@ -22,8 +23,39 @@
>   #include <dl-cfi.h>
>   #include <sys/mman.h>
>   
> +struct dl_cfi_info
> +{
> +  const char *program;
> +
> +  /* Check how lp and ss should be enabled.  */
> +#ifdef __riscv_landing_pad
> +  enum dl_riscv_cfi_control enable_lp_type;
> +#endif
> +#ifdef __riscv_shadow_stack
> +  enum dl_riscv_cfi_control enable_ss_type;
> +#endif
> +
> +  /* Previously enabled features.  */
> +  unsigned int feature_1_enabled;
> +
> +  /* Features that should be enabled.  */
> +  unsigned int enable_feature_1;
> +
> +  /* If there are any legacy shared object.  */
> +  unsigned int feature_1_legacy;
> +
> +  /* Which shared object is the first legacy shared object.  */
> +#ifdef __riscv_landing_pad
> +  unsigned int feature_1_legacy_lp;
> +#endif
> +#ifdef __riscv_shadow_stack
> +  unsigned int feature_1_legacy_ss;
> +#endif
> +};
> +
> +
>   static void
> -dl_check_legacy_object (struct link_map *m, unsigned int *feature_1)
> +dl_check_legacy_object (struct link_map *m, struct dl_cfi_info *info)
>   {
>     /* Iterate through the dependencies and disable if needed here  */
>     struct link_map *l = NULL;
> @@ -42,32 +74,150 @@ dl_check_legacy_object (struct link_map *m, unsigned int *feature_1)
>         /* 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))
> +      if (is_rtld_link_map (l)
> +          || is_rtld_link_map (l->l_real)
> +          || (info->program != NULL && l == m))
>           continue;
>   #endif /* SHARED  */
>   
> -      *feature_1 &= l->l_riscv_feature_1_and;
> +      info->enable_feature_1 &= ((l->l_riscv_feature_1_and
> +                                  & (GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
> +                                     | GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS))
> +                                  | ~(GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED
> +                                     | GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS));
> +
> +      /* Bookkeeping legacy objects  */
> +#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)
> +              != (info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED))
> +         )
> +        {
> +          info->feature_1_legacy_lp = i;
> +          info->feature_1_legacy |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
> +        }
> +#endif
> +#ifdef __riscv_shadow_stack
> +      if ((info->feature_1_legacy & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS) == 0
> +          && ((info->enable_feature_1 & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
> +              != (info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS))
> +         )
> +        {
> +          info->feature_1_legacy_ss = i;
> +          info->feature_1_legacy |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
> +        }
> +#endif
>       }
> +
> +  /* Keep bits set if cfi_always_on  */
> +#ifdef __riscv_landing_pad
> +  if ((info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) != 0
> +      && info->enable_lp_type == cfi_always_on)
> +    {
> +      info->enable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
> +    }
> +#endif
> +#ifdef __riscv_shadow_stack
> +  if ((info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS) != 0
> +      && info->enable_ss_type == cfi_always_on)
> +    {
> +      info->enable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
> +    }
> +#endif
>   }
>   
>   #ifdef SHARED
>   static void
> -dl_cfi_check_startup (struct link_map *m, unsigned int *feature_1)
> +dl_cfi_check_startup (struct link_map *m, struct dl_cfi_info *info)
>   {
> -  /* FIXME: Add tunables here  */
> -  if (!*feature_1)
> -    return;
> -  dl_check_legacy_object (m, feature_1);
> +# ifdef __riscv_landing_pad
> +  if (info->enable_lp_type == cfi_always_on)
> +    info->enable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
> +  else if (info->enable_lp_type == cfi_always_off)
> +    info->enable_feature_1 &= ~GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
> +  else
> +    info->enable_feature_1 &= ((m->l_riscv_feature_1_and
> +                                & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
> +                               | ~GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED);
> +# endif
> +# ifdef __riscv_shadow_stack
> +  if (info->enable_ss_type == cfi_always_on)
> +    info->enable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
> +  else if (info->enable_ss_type == cfi_always_off)
> +    info->enable_feature_1 &= ~GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
> +  else
> +    info->enable_feature_1 &= ((m->l_riscv_feature_1_and
> +                                & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
> +                               | ~GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS);
> +# endif
> +
> +  if (info->enable_feature_1 != 0)
> +    dl_check_legacy_object (m, info);
>   
>     /* Update GL(dl_riscv_feature_1)  */
> -  GL(dl_riscv_feature_1) = *feature_1;
> +  if (info->enable_feature_1 ^ info->feature_1_enabled) {
> +    info->feature_1_enabled = info->enable_feature_1;
> +    GL(dl_riscv_feature_1) = info->enable_feature_1;
> +  }
>   }
>   #endif /* SHARED  */
>   
>   static void
> -dl_cfi_check_dlopen (struct link_map *m)
> +dl_cfi_check_dlopen (struct link_map *m, struct dl_cfi_info *info)
>   {
> +  if (info->enable_feature_1 != 0) {
> +    dl_check_legacy_object(m, info);
> +
> +    if (info->feature_1_legacy == 0)
> +      return;
> +  }
> +
> +  unsigned int disable_feature_1 = 0;
> +  unsigned int legacy_obj = 0;
> +  const char *msg = NULL;
> +
> +#ifdef __riscv_landing_pad
> +  if ((info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) != 0
> +      && (info->feature_1_legacy & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) != 0)
> +    {
> +      if (info->enable_lp_type != cfi_permissive || !SINGLE_THREAD_P)
> +        {
> +          legacy_obj = info->feature_1_legacy_lp;
> +          msg = N_("rebuild shared object with landing pad support");
> +        }
> +      else
> +        disable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
> +    }
> +#endif
> +
> +#ifdef __riscv_shadow_stack
> +  if ((info->feature_1_enabled & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS) != 0
> +      && (info->feature_1_legacy & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS) != 0)
> +    {
> +      if (info->enable_ss_type != cfi_permissive || !SINGLE_THREAD_P)
> +        {
> +          legacy_obj = info->feature_1_legacy_ss;
> +          msg = N_("rebuild shared object with shadow stack support");
> +        }
> +      else
> +        disable_feature_1 |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
> +    }
> +#endif
> +
> +  if (msg != NULL)
> +    _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;
> +      if (res)
> +        {
> +          msg = N_("can't disable CFI feature");
> +          _dl_signal_error (-res, m->l_initfini[legacy_obj]->l_name,
> +                            "dlopen", msg);
> +        }
> +      GL(dl_riscv_feature_1) &= ~disable_feature_1;
> +    }
>   }


The braces in the final if statement seem mismatched - As it is, I would 
have to presume this is unlikely to compile.


>   
>   attribute_hidden void
> @@ -89,20 +239,61 @@ _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;
> +  struct dl_cfi_info info;
>   #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  */
>   
> +  unsigned int supported_exts = 0;
> +  unsigned int always_on_exts = 0;
> +
> +#ifdef __riscv_landing_pad
> +  info.enable_lp_type = GL(dl_riscv_feature_control).lp;
> +  supported_exts += 1;
> +  always_on_exts += (info.enable_lp_type == cfi_always_on);
> +#endif
> +#ifdef __riscv_shadow_stack
> +  info.enable_ss_type = GL(dl_riscv_feature_control).ss;
> +  supported_exts += 1;
> +  always_on_exts += (info.enable_ss_type == cfi_always_on);
> +#endif
> +
> +  info.feature_1_enabled = GL(dl_riscv_feature_1);
> +
> +  /* No legacy check needed if all cfi exts are always on in main  */
> +  if (program && (supported_exts == always_on_exts))
> +    return;
> +
> +  /* No legacy check needed if all cfi exts are off  */
> +  if (info.feature_1_enabled == 0)
> +    return;
> +
> +  info.program = program;
> +
> +  info.enable_feature_1 = 0;
> +#ifdef __riscv_landing_pad
> +  if (info.enable_lp_type != cfi_always_off)
> +    info.enable_feature_1 |= (info.feature_1_enabled
> +            & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED);
> +  info.feature_1_legacy_lp = 0;
> +#endif
> +#ifdef __riscv_shadow_stack
> +  if (info.enable_ss_type != cfi_always_off)
> +    info.enable_feature_1 |= (info.feature_1_enabled
> +            & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS);
> +  info.feature_1_legacy_ss = 0;
> +#endif
> +
> +  info.feature_1_enabled = GL(dl_riscv_feature_1);
> +  info.feature_1_legacy = 0;
> +
>   #ifdef SHARED
>     if (program)
> -    dl_cfi_check_startup (l, &feature_1);
> +    dl_cfi_check_startup (l, &info);
>     else
>   #endif /* SHARED  */
> -    dl_cfi_check_dlopen (l);
> +    dl_cfi_check_dlopen (l, &info);
>   }
>
> [snip]




More information about the Libc-alpha mailing list