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
@@ -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
@@ -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);
+}
@@ -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\
@@ -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))
new file mode 100644
@@ -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)
@@ -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\
+"