diff --git a/INSTALL b/INSTALL index d3200f271f..476861b521 100644 --- a/INSTALL +++ b/INSTALL @@ -155,6 +155,19 @@ passed to 'configure'. For example: NOTE: '--enable-cet' is only supported on x86_64 and x32. +'--enable-cfi' + Enable RISC-V Control Flow Integrity Extensions (Zicfilp/Zicfiss) + support. When the GNU C Library is built with '--enable-cfi', the + resulting library is protected with landing pad and shadow stack. + This feature is currently supported on RV64 with GCC 15 and + binutils 2.45 or later. With '--enable-cfi', it is an error to + dlopen a non CFI enabled shared library in CFI enabled application. + The restriction can be loosen by setting to permissive mode with + the use of the glibc tunables, see glibc tunables section for more + information. + + NOTE: '--enable-cfi' is only supported on RV64. + '--enable-memory-tagging' Enable memory tagging support if the architecture supports it. When the GNU C Library is built with this option then the resulting diff --git a/NEWS b/NEWS index 521fbd5e07..02a39b05e4 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ Major new features: * The ISO C2Y family of unsigned abs functions, i.e. uabs, ulabs, ullabs and uimaxabs, are now supported. +* Added --enable-cfi option to enable the RISC-V CFI extensions + (Zicfilp/Zicfiss) support on RV64 Linux. + Deprecated and removed features, and other changes affecting compatibility: * The glibc.rtld.execstack now supports a compatibility mode to allow diff --git a/configure b/configure index 7cda641fce..5a996c8f25 100755 --- a/configure +++ b/configure @@ -818,6 +818,7 @@ enable_nscd enable_pt_chown enable_mathvec enable_cet +enable_cfi enable_scv enable_fortify_source with_cpu @@ -1499,6 +1500,7 @@ Optional Features: depends on architecture] --enable-cet enable Intel Control-flow Enforcement Technology (CET), x86 only + --enable-cfi enable Control Flow Integrity (CFI), RISC-V only --disable-scv syscalls will not use scv instruction, even if the kernel supports it, powerpc only --enable-fortify-source[=1|2|3] @@ -4853,6 +4855,16 @@ esac fi +# Check whether --enable-cfi was given. +if test ${enable_cfi+y} +then : + enableval=$enable_cfi; enable_cfi=$enableval +else case e in #( + e) enable_cfi=no ;; +esac +fi + + # Check whether --enable-scv was given. if test ${enable_scv+y} then : diff --git a/configure.ac b/configure.ac index 0b0d8875cc..9bda3d1520 100644 --- a/configure.ac +++ b/configure.ac @@ -421,6 +421,12 @@ AC_ARG_ENABLE([cet], [enable_cet=$enableval], [enable_cet=$libc_cv_compiler_default_cet]) +AC_ARG_ENABLE([cfi], + AS_HELP_STRING([--enable-cfi], + [enable Control Flow Integrity (CFI), RISC-V only]), + [enable_cfi=$enableval], + [enable_cfi=no]) + AC_ARG_ENABLE([scv], AS_HELP_STRING([--disable-scv], [syscalls will not use scv instruction, even if the kernel supports it, powerpc only]), diff --git a/elf/elf.h b/elf/elf.h index 1e1a59c14d..575c14f022 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -1426,6 +1426,11 @@ typedef struct SHSTK. */ #define GNU_PROPERTY_X86_FEATURE_1_SHSTK (1U << 1) +/* RISC-V specific GNU PROPERTY. */ +#define GNU_PROPERTY_RISCV_FEATURE_1_AND 0xc0000000 +#define GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED (1u << 0) +#define GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS (1u << 1) + /* Move records. */ typedef struct { diff --git a/manual/install.texi b/manual/install.texi index 7fcdda9146..d3e7153365 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -185,6 +185,18 @@ non CET enabled shared library in CET enabled application. NOTE: @option{--enable-cet} is only supported on x86_64 and x32. +@item --enable-cfi +Enable RISC-V Control Flow Integrity Extensions (Zicfilp/Zicfiss) support. +When @theglibc{} is built with @option{--enable-cfi}, the resulting +library is protected with landing pad and shadow stack@. +This feature is currently supported on RV64 with GCC 15 and binutils 2.45 +or later. With @option{--enable-cfi}, it is an error to dlopen a non CFI +enabled shared library in CFI enabled application. The restriction can be +loosen by setting to permissive mode with the use of the glibc tunables, +see glibc tunables section for more information. + +NOTE: @option{--enable-cfi} is only supported on RV64. + @item --enable-memory-tagging Enable memory tagging support if the architecture supports it. When @theglibc{} is built with this option then the resulting library will diff --git a/manual/tunables.texi b/manual/tunables.texi index d11ca7ed7c..7c5178e6b4 100644 --- a/manual/tunables.texi +++ b/manual/tunables.texi @@ -574,6 +574,28 @@ assume that the CPU is @code{xxx} where xxx may have one of these values: This tunable is specific to aarch64. @end deftp +@deftp Tunable glibc.cpu.riscv_cfi_lp +The @code{glibc.cpu.riscv_cfi_lp=[on|off|permissive]} tunable allows the +user to temporarily turn off the branch control flow protection (a.k.a. +landing pad) or set to permissive mode. The default value is @code{on} for +target compiled with Zicfilp extension. Permissive mode allows the protection +to be turned off on program trying to dynamically load a legacy shared library +without landing pad support. + +This tunable is specific to riscv. +@end deftp + +@deftp Tunable glibc.cpu.riscv_cfi_ss +The @code{glibc.cpu.riscv_cfi_ss=[on|off|permissive]} tunable allows the +user to temporarily turn off the return control flow protection (a.k.a. +shadow stack) or set to permissive mode. The default value is @code{on} for +target compiled with Zicfiss extension. Permissive mode allows the protection +to be turned off on program trying to dynamically load a legacy shared library +without shadow stack support. + +This tunable is specific to riscv. +@end deftp + @deftp Tunable glibc.cpu.x86_data_cache_size The @code{glibc.cpu.x86_data_cache_size} tunable allows the user to set data cache size in bytes for use in memory and string routines. diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile index c08753ae8a..b1f074a3eb 100644 --- a/sysdeps/riscv/Makefile +++ b/sysdeps/riscv/Makefile @@ -1,9 +1,10 @@ ifeq ($(subdir),misc) sysdep_headers += sys/asm.h endif +sysdep-dl-routines += dl-get-cpu-features 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 @@ -15,3 +16,13 @@ ASFLAGS-.os += -Wa,-mno-relax ASFLAGS-.o += -Wa,-mno-relax sysdep-CFLAGS += -mno-relax 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 +CFLAGS-.oS += -fcf-protection=full +asm-CPPFLAGS += -fcf-protection=full -include sysdep.h +endif diff --git a/sysdeps/riscv/__longjmp.S b/sysdeps/riscv/__longjmp.S index 7228b457a6..1aa8a235d7 100644 --- a/sysdeps/riscv/__longjmp.S +++ b/sysdeps/riscv/__longjmp.S @@ -20,6 +20,7 @@ #include ENTRY (__longjmp) + LPAD REG_L ra, 0*SZREG(a0) REG_L s0, 1*SZREG(a0) REG_L s1, 2*SZREG(a0) @@ -50,6 +51,34 @@ ENTRY (__longjmp) FREG_L fs11,14*SZREG+11*SZFREG(a0) #endif +#ifdef __riscv_shadow_stack + /* skip unwinding if ss is not enabled */ + ssrdp t0 + beqz t0, .Lunwind_fin +# ifndef __riscv_float_abi_soft + REG_L t1, 14*SZREG+12*SZFREG(a0) +# else + REG_L t1, 14*SZREG(a0) +# endif +.Lunwind: + bleu t1, t0, .Lunwind_fin + /* Increase ssp by at most a page size to ensure always run into a + guard page before accidentally point to another legal shadow stack + page */ + /* t0 = (t1 - t0 >= 4096) ? t0 + 4096 : t1 */ + lui a0, 1 + add t0, t0, a0 + bleu t0, t1, 1f + mv t0, t1 +1: + csrw ssp, t0 + /* Test if the location pointed by ssp is legal */ + sspush x5 + sspopchk x5 + j .Lunwind +.Lunwind_fin: +#endif + seqz a0, a1 add a0, a0, a1 # a0 = (a1 == 0) ? 1 : a1 ret diff --git a/sysdeps/riscv/bits/setjmp.h b/sysdeps/riscv/bits/setjmp.h index 5ebbec393a..5cbc12fa3d 100644 --- a/sysdeps/riscv/bits/setjmp.h +++ b/sysdeps/riscv/bits/setjmp.h @@ -33,6 +33,10 @@ typedef struct __jmp_buf_internal_tag double __fpregs[12]; #elif !defined __riscv_float_abi_soft # error unsupported FLEN +#endif +#ifdef __riscv_shadow_stack + /* Shadow stack pointer. */ + long int __ssp; #endif } __jmp_buf[1]; diff --git a/sysdeps/riscv/cpu-features.c b/sysdeps/riscv/cpu-features.c new file mode 100644 index 0000000000..beca59fe17 --- /dev/null +++ b/sysdeps/riscv/cpu-features.c @@ -0,0 +1,46 @@ +/* Initialize CPU feature data. + This file is part of the GNU C Library. + Copyright (C) 2025 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 + . */ + +#ifndef _CPU_FEATURES_RISCV_H +#define _CPU_FEATURES_RISCV_H + +# define TUNABLE_NAMESPACE cpu +# include + +# ifdef __riscv_landing_pad +extern void TUNABLE_CALLBACK (set_riscv_cfi_lp) (tunable_val_t *) + attribute_hidden; +# endif +# ifdef __riscv_shadow_stack +extern void TUNABLE_CALLBACK (set_riscv_cfi_ss) (tunable_val_t *) + attribute_hidden; +# endif + +static inline void +init_cpu_features (void) +{ +# ifdef __riscv_landing_pad + TUNABLE_GET (riscv_cfi_lp, tunable_val_t *, + TUNABLE_CALLBACK (set_riscv_cfi_lp)); +# endif +# ifdef __riscv_shadow_stack + TUNABLE_GET (riscv_cfi_ss, tunable_val_t *, + TUNABLE_CALLBACK (set_riscv_cfi_ss)); +# endif +} +#endif /* _CPU_FEATURES_RISCV_H */ diff --git a/sysdeps/riscv/cpu-tunables.c b/sysdeps/riscv/cpu-tunables.c new file mode 100644 index 0000000000..e84dcf2414 --- /dev/null +++ b/sysdeps/riscv/cpu-tunables.c @@ -0,0 +1,50 @@ +/* RISC-V CPU feature tuning. + This file is part of the GNU C Library. + Copyright (C) 2025 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 + . */ + +#define TUNABLE_NAMESPACE cpu +#include +#include +#include + +#ifdef __riscv_landing_pad +attribute_hidden +void +TUNABLE_CALLBACK (set_riscv_cfi_lp) (tunable_val_t *valp) +{ + if (tunable_strcmp_cte (valp, "permissive")) + GL(dl_riscv_feature_control).lp = cfi_permissive; + else if (tunable_strcmp_cte (valp, "off")) + GL(dl_riscv_feature_control).lp = cfi_always_off; + else + GL(dl_riscv_feature_control).lp = cfi_always_on; +} +#endif + +#ifdef __riscv_shadow_stack +attribute_hidden +void +TUNABLE_CALLBACK (set_riscv_cfi_ss) (tunable_val_t *valp) +{ + if (tunable_strcmp_cte (valp, "permissive")) + GL(dl_riscv_feature_control).ss = cfi_permissive; + else if (tunable_strcmp_cte (valp, "off")) + GL(dl_riscv_feature_control).ss = cfi_always_off; + else + GL(dl_riscv_feature_control).ss = cfi_always_on; +} +#endif diff --git a/sysdeps/riscv/crti.S b/sysdeps/riscv/crti.S new file mode 100644 index 0000000000..fb1097c5ca --- /dev/null +++ b/sysdeps/riscv/crti.S @@ -0,0 +1,4 @@ +/* crti.S is empty because .init_array/.fini_array are used exclusively. + Include sysdep.h to define gnu property if necessary. */ + +#include diff --git a/sysdeps/riscv/crtn.S b/sysdeps/riscv/crtn.S new file mode 100644 index 0000000000..b2e7cb692e --- /dev/null +++ b/sysdeps/riscv/crtn.S @@ -0,0 +1,4 @@ +/* crtn.S is empty because .init_array/.fini_array are used exclusively. + Include sysdep.h to define gnu property if necessary. */ + +#include diff --git a/sysdeps/riscv/dl-cfi.c b/sysdeps/riscv/dl-cfi.c new file mode 100644 index 0000000000..d58d42e75d --- /dev/null +++ b/sysdeps/riscv/dl-cfi.c @@ -0,0 +1,317 @@ +/* RISC-V CFI extensions (zicfilp/zicfiss) functions. + Copyright (C) 2025 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 + . */ + +#include "feature-control.h" +#include +#include +#include +#include +#include +#include + +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, struct dl_cfi_info *info) +{ + /* Iterate through the dependencies and record legacy objects */ + 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 (is_rtld_link_map (l) + || is_rtld_link_map (l->l_real) + || (info->program != NULL && l == m)) + continue; +#endif /* SHARED */ + + 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 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) + != (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, struct dl_cfi_info *info) +{ +# 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) */ + 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, 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) + { + int res = dl_cfi_disable_cfi (disable_feature_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; + } +} + +attribute_hidden void +_dl_cfi_setup_features (unsigned int feature_1) +{ + /* 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 (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 */ +#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. */ +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. */ + 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; + } +#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_legacy = 0; + +#ifdef SHARED + if (program) + dl_cfi_check_startup (l, &info); + else +#endif /* SHARED */ + dl_cfi_check_dlopen (l, &info); +} diff --git a/sysdeps/riscv/dl-get-cpu-features.c b/sysdeps/riscv/dl-get-cpu-features.c new file mode 100644 index 0000000000..23da13d52d --- /dev/null +++ b/sysdeps/riscv/dl-get-cpu-features.c @@ -0,0 +1,27 @@ +/* Initialize CPU feature data. + Copyright (C) 2025 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 + . */ + +#include + +#ifdef SHARED +# include +void +_dl_riscv_init_cpu_features (void) +{ + init_cpu_features (); +} +#endif diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h index dcc3e0883b..b2a013e78a 100644 --- a/sysdeps/riscv/dl-machine.h +++ b/sysdeps/riscv/dl-machine.h @@ -28,6 +28,20 @@ #include #include #include +#if defined(__riscv_landing_pad) || defined(__riscv_shadow_stack) +# include +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 +# define SET_LPAD +#else +# define SET_LPAD +#endif +extern void _dl_riscv_init_cpu_features (void); #ifndef _RTLD_PROLOGUE # define _RTLD_PROLOGUE(entry) \ @@ -107,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\ @@ -126,6 +142,7 @@ elf_machine_dynamic (void) # Pass our finalizer function to _start.\n\ lla a0, _dl_fini\n\ # Jump to the user entry point.\n\ + " STRINGXV (SET_LPAD) "\n\ jr s0\n\ " _RTLD_EPILOGUE (ENTRY_POINT) \ _RTLD_EPILOGUE (_dl_start_user) "\ @@ -136,6 +153,24 @@ elf_machine_dynamic (void) #define ARCH_LA_PLTENTER riscv_gnu_pltenter #define ARCH_LA_PLTEXIT riscv_gnu_pltexit +/* We define an initialization function. This is called very early in + _dl_sysdep_start. */ +#define DL_PLATFORM_INIT dl_platform_init () + +static inline void __attribute__ ((unused)) +dl_platform_init (void) +{ + if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0') + /* Avoid an empty string which would disturb us. */ + GLRO(dl_platform) = NULL; + +#ifdef SHARED + /* init_cpu_features which has been called early from __libc_start_main in + static executable. */ + _dl_riscv_init_cpu_features (); +#endif +} + /* Bias .got.plt entry by the offset requested by the PLT header. */ #define elf_machine_plt_value(map, reloc, value) (value) diff --git a/sysdeps/riscv/dl-procruntime.c b/sysdeps/riscv/dl-procruntime.c new file mode 100644 index 0000000000..b1c095cf10 --- /dev/null +++ b/sysdeps/riscv/dl-procruntime.c @@ -0,0 +1,77 @@ +/* Data for processor runtime information. RISC-V version. + Copyright (C) 2025 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 + . */ + +/* This information must be kept in sync with the _DL_HWCAP_COUNT, + HWCAP_PLATFORMS_START and HWCAP_PLATFORMS_COUNT definitions in + dl-hwcap.h. + + If anything should be added here check whether the size of each string + is still ok with the given array size. + + All the #ifdefs in the definitions are quite irritating but + necessary if we want to avoid duplicating the information. There + are three different modes: + + - PROCINFO_DECL is defined. This means we are only interested in + declarations. + + - PROCINFO_DECL is not defined: + + + if SHARED is defined the file is included in an array + initializer. The .element = { ... } syntax is needed. + + + if SHARED is not defined a normal array initialization is + needed. + */ + +#ifndef PROCINFO_CLASS +# define PROCINFO_CLASS +#endif + +#if !IS_IN (ldconfig) +# if !defined PROCINFO_DECL && defined SHARED + ._dl_riscv_feature_1 +# else +PROCINFO_CLASS unsigned int _dl_riscv_feature_1 +# endif +# ifndef PROCINFO_DECL += 0 +# endif +# if !defined SHARED || defined PROCINFO_DECL +; +# else +, +# endif + +# if !defined PROCINFO_DECL && defined SHARED + ._dl_riscv_feature_control +# else +PROCINFO_CLASS struct dl_riscv_feature_control _dl_riscv_feature_control +# endif +# ifndef PROCINFO_DECL += { + .lp = cfi_always_on, + .ss = cfi_always_on, + } +# endif +# if !defined SHARED || defined PROCINFO_DECL +; +# else +, +# endif +#endif diff --git a/sysdeps/riscv/dl-prop.h b/sysdeps/riscv/dl-prop.h new file mode 100644 index 0000000000..1c9fb6b38b --- /dev/null +++ b/sysdeps/riscv/dl-prop.h @@ -0,0 +1,74 @@ +/* Support for GNU properties. RISC-V version. + Copyright (C) 2025 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 + . */ + +#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) +{ +#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)) +_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/dl-trampoline.S b/sysdeps/riscv/dl-trampoline.S index 93d04af326..2b25f64a3b 100644 --- a/sysdeps/riscv/dl-trampoline.S +++ b/sysdeps/riscv/dl-trampoline.S @@ -25,13 +25,27 @@ /* Assembler veneer called from the PLT header code for lazy loading. The PLT header passes its own args in t0-t2. */ -#ifdef __riscv_float_abi_soft -# define FRAME_SIZE (-((-10 * SZREG) & ALMASK)) -#else -# define FRAME_SIZE (-((-10 * SZREG - 8 * SZFREG) & ALMASK)) +#ifdef __riscv_landing_pad + +# ifdef __riscv_float_abi_soft +# define FRAME_SIZE (-((-11 * SZREG) & ALMASK)) +# else +# define FRAME_SIZE (-((-11 * SZREG - 8 * SZFREG) & ALMASK)) +# define FREG_BASE_OFFSET (11*SZREG) +# endif + +# else + +# ifdef __riscv_float_abi_soft +# define FRAME_SIZE (-((-10 * SZREG) & ALMASK)) +# else +# define FRAME_SIZE (-((-10 * SZREG - 8 * SZFREG) & ALMASK)) +# define FREG_BASE_OFFSET (10*SZREG) +# endif #endif ENTRY (_dl_runtime_resolve) + LPAD # Save arguments to stack. addi sp, sp, -FRAME_SIZE REG_S ra, 9*SZREG(sp) @@ -43,16 +57,19 @@ ENTRY (_dl_runtime_resolve) REG_S a5, 6*SZREG(sp) REG_S a6, 7*SZREG(sp) REG_S a7, 8*SZREG(sp) +#ifdef __riscv_landing_pad + REG_S t2, 10*SZREG(sp) +#endif #ifndef __riscv_float_abi_soft - FREG_S fa0, (10*SZREG + 0*SZFREG)(sp) - FREG_S fa1, (10*SZREG + 1*SZFREG)(sp) - FREG_S fa2, (10*SZREG + 2*SZFREG)(sp) - FREG_S fa3, (10*SZREG + 3*SZFREG)(sp) - FREG_S fa4, (10*SZREG + 4*SZFREG)(sp) - FREG_S fa5, (10*SZREG + 5*SZFREG)(sp) - FREG_S fa6, (10*SZREG + 6*SZFREG)(sp) - FREG_S fa7, (10*SZREG + 7*SZFREG)(sp) + FREG_S fa0, (FREG_BASE_OFFSET + 0*SZFREG)(sp) + FREG_S fa1, (FREG_BASE_OFFSET + 1*SZFREG)(sp) + FREG_S fa2, (FREG_BASE_OFFSET + 2*SZFREG)(sp) + FREG_S fa3, (FREG_BASE_OFFSET + 3*SZFREG)(sp) + FREG_S fa4, (FREG_BASE_OFFSET + 4*SZFREG)(sp) + FREG_S fa5, (FREG_BASE_OFFSET + 5*SZFREG)(sp) + FREG_S fa6, (FREG_BASE_OFFSET + 6*SZFREG)(sp) + FREG_S fa7, (FREG_BASE_OFFSET + 7*SZFREG)(sp) #endif # Update .got.plt and obtain runtime address of callee. @@ -60,6 +77,7 @@ ENTRY (_dl_runtime_resolve) mv a0, t0 # link map add a1, a1, t1 # reloc offset (== thrice the .got.plt offset) la a2, _dl_fixup + SET_LPAD jalr a2 mv t1, a0 @@ -73,16 +91,19 @@ ENTRY (_dl_runtime_resolve) REG_L a5, 6*SZREG(sp) REG_L a6, 7*SZREG(sp) REG_L a7, 8*SZREG(sp) +#ifdef __riscv_landing_pad + REG_L t2, 10*SZREG(sp) +#endif #ifndef __riscv_float_abi_soft - FREG_L fa0, (10*SZREG + 0*SZFREG)(sp) - FREG_L fa1, (10*SZREG + 1*SZFREG)(sp) - FREG_L fa2, (10*SZREG + 2*SZFREG)(sp) - FREG_L fa3, (10*SZREG + 3*SZFREG)(sp) - FREG_L fa4, (10*SZREG + 4*SZFREG)(sp) - FREG_L fa5, (10*SZREG + 5*SZFREG)(sp) - FREG_L fa6, (10*SZREG + 6*SZFREG)(sp) - FREG_L fa7, (10*SZREG + 7*SZFREG)(sp) + FREG_L fa0, (FREG_BASE_OFFSET + 0*SZFREG)(sp) + FREG_L fa1, (FREG_BASE_OFFSET + 1*SZFREG)(sp) + FREG_L fa2, (FREG_BASE_OFFSET + 2*SZFREG)(sp) + FREG_L fa3, (FREG_BASE_OFFSET + 3*SZFREG)(sp) + FREG_L fa4, (FREG_BASE_OFFSET + 4*SZFREG)(sp) + FREG_L fa5, (FREG_BASE_OFFSET + 5*SZFREG)(sp) + FREG_L fa6, (FREG_BASE_OFFSET + 6*SZFREG)(sp) + FREG_L fa7, (FREG_BASE_OFFSET + 7*SZFREG)(sp) #endif addi sp, sp, FRAME_SIZE diff --git a/sysdeps/riscv/dl-tunables.list b/sysdeps/riscv/dl-tunables.list new file mode 100644 index 0000000000..9260cb40ba --- /dev/null +++ b/sysdeps/riscv/dl-tunables.list @@ -0,0 +1,27 @@ +# RISC-V specific tunables. +# Copyright (C) 2025 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 +# . + +glibc { + cpu { + riscv_cfi_lp { + type: STRING + } + riscv_cfi_ss { + type: STRING + } +} diff --git a/sysdeps/riscv/feature-control.h b/sysdeps/riscv/feature-control.h new file mode 100644 index 0000000000..a14dfe5519 --- /dev/null +++ b/sysdeps/riscv/feature-control.h @@ -0,0 +1,42 @@ +/* RISC-V feature tuning. + This file is part of the GNU C Library. + Copyright (C) 2025 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 + . */ + +#ifndef _RISCV_FEATURE_CONTROL_H +#define _RISCV_FEATURE_CONTROL_H + +/* For each CFI feature, LP and SS, valid control values. */ +enum dl_riscv_cfi_control +{ + /* Enable CFI features based on ELF property note. */ + cfi_elf_property = 0, + /* Always enable CFI features. */ + cfi_always_on, + /* Always disable CFI features. */ + cfi_always_off, + /* Enable CFI features permissively. */ + cfi_permissive +}; + +struct dl_riscv_feature_control +{ + enum dl_riscv_cfi_control lp : 2; + enum dl_riscv_cfi_control ss : 2; +}; + +#endif /* feature-control.h */ + 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 + +RTLD_GLOBAL_DL_RISCV_FEATURE_1_OFFSET offsetof (struct rtld_global, _dl_riscv_feature_1) diff --git a/sysdeps/riscv/ldsodefs.h b/sysdeps/riscv/ldsodefs.h index 7e22d64102..5aa4acc47d 100644 --- a/sysdeps/riscv/ldsodefs.h +++ b/sysdeps/riscv/ldsodefs.h @@ -20,6 +20,7 @@ #define _RISCV_LDSODEFS_H 1 #include +#include struct La_riscv_regs; struct La_riscv_retval; diff --git a/sysdeps/riscv/libc-start.c b/sysdeps/riscv/libc-start.c new file mode 100644 index 0000000000..0f7ce35c85 --- /dev/null +++ b/sysdeps/riscv/libc-start.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2025 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 + . */ + +#ifndef SHARED + +/* Mark symbols hidden in static PIE for early self relocation to work. */ +# if BUILD_PIE_DEFAULT +# pragma GCC visibility push(hidden) +# endif +# include +# include + +# define ARCH_INIT_CPU_FEATURES() init_cpu_features () + +#endif /* !SHARED */ +#include + diff --git a/sysdeps/riscv/libc-start.h b/sysdeps/riscv/libc-start.h new file mode 100644 index 0000000000..5bb3ecb84d --- /dev/null +++ b/sysdeps/riscv/libc-start.h @@ -0,0 +1,95 @@ +/* RISC-V libc main startup. + Copyright (C) 2025 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 + . */ + +#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 +# include +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 */ + +#ifdef __riscv_landing_pad + if (GL(dl_riscv_feature_control).lp != cfi_always_off) + cfi_feature |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED; +#endif +#ifdef __riscv_shadow_stack + if (GL(dl_riscv_feature_control).ss != cfi_always_off) + cfi_feature |= GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS; +#endif + 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. */ + 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/riscv/link_map.h b/sysdeps/riscv/link_map.h new file mode 100644 index 0000000000..b8e757adf4 --- /dev/null +++ b/sysdeps/riscv/link_map.h @@ -0,0 +1,22 @@ +/* Additional fields in struct link_map. Linux/RISC-V version. + Copyright (C) 2025 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 + . */ + +/* GNU_PROPERTY_RISCV_FEATURE_1_AND of this object. */ +unsigned int l_riscv_feature_1_and; + +#include diff --git a/sysdeps/riscv/multiarch/memcpy_noalignment.S b/sysdeps/riscv/multiarch/memcpy_noalignment.S index dd135f4a4d..e97748e82b 100644 --- a/sysdeps/riscv/multiarch/memcpy_noalignment.S +++ b/sysdeps/riscv/multiarch/memcpy_noalignment.S @@ -34,7 +34,11 @@ #define BLOCK_SIZE (16 * SZREG) .attribute unaligned_access, 1 +#ifdef __riscv_landing_pad + .align 2 +#endif ENTRY (__memcpy_noalignment) + LPAD beq a2, zero, L(ret) /* if LEN < SZREG jump to tail handling. */ diff --git a/sysdeps/riscv/nptl/Makefile b/sysdeps/riscv/nptl/Makefile new file mode 100644 index 0000000000..2b7bf43403 --- /dev/null +++ b/sysdeps/riscv/nptl/Makefile @@ -0,0 +1 @@ +gen-as-const-headers += tcb-offsets.sym diff --git a/sysdeps/riscv/nptl/tcb-offsets.sym b/sysdeps/riscv/nptl/tcb-offsets.sym new file mode 100644 index 0000000000..04fee5e20e --- /dev/null +++ b/sysdeps/riscv/nptl/tcb-offsets.sym @@ -0,0 +1,5 @@ +#include +#include +#include + +SSP_BASE_OFFSET offsetof (tcbhead_t, ssp_base) diff --git a/sysdeps/riscv/nptl/tls.h b/sysdeps/riscv/nptl/tls.h index 11a1b08972..1e7c26790b 100644 --- a/sysdeps/riscv/nptl/tls.h +++ b/sysdeps/riscv/nptl/tls.h @@ -44,6 +44,8 @@ typedef struct { dtv_t *dtv; void *private; + /* The marker for the current shadow stack. */ + unsigned long long int ssp_base; } tcbhead_t; /* This is the size of the initial TCB. Because our TCB is before the thread diff --git a/sysdeps/riscv/preconfigure b/sysdeps/riscv/preconfigure index a5de5ccb7d..439b526452 100644 --- a/sysdeps/riscv/preconfigure +++ b/sysdeps/riscv/preconfigure @@ -62,6 +62,8 @@ riscv*) printf "%s\n" "#define RISCV_ABI_FLEN $abi_flen" >>confdefs.h + config_vars="$config_vars +riscv-enable-cfi = $enable_cfi" ;; esac diff --git a/sysdeps/riscv/preconfigure.ac b/sysdeps/riscv/preconfigure.ac index a5c30e0dbf..fb75ae427a 100644 --- a/sysdeps/riscv/preconfigure.ac +++ b/sysdeps/riscv/preconfigure.ac @@ -60,5 +60,6 @@ riscv*) AC_DEFINE_UNQUOTED([RISCV_ABI_XLEN], [$xlen]) AC_DEFINE_UNQUOTED([RISCV_ABI_FLEN], [$abi_flen]) + LIBC_CONFIG_VAR([riscv-enable-cfi], [$enable_cfi]) ;; esac diff --git a/sysdeps/riscv/setjmp.S b/sysdeps/riscv/setjmp.S index 600ea84db6..f547bc0238 100644 --- a/sysdeps/riscv/setjmp.S +++ b/sysdeps/riscv/setjmp.S @@ -20,14 +20,17 @@ #include ENTRY (_setjmp) + LPAD li a1, 0 j HIDDEN_JUMPTARGET (__sigsetjmp) END (_setjmp) ENTRY (setjmp) + LPAD li a1, 1 /* Fallthrough */ END (setjmp) ENTRY (__sigsetjmp) + LPAD REG_S ra, 0*SZREG(a0) REG_S s0, 1*SZREG(a0) REG_S s1, 2*SZREG(a0) @@ -58,6 +61,16 @@ ENTRY (__sigsetjmp) FREG_S fs11,14*SZREG+11*SZFREG(a0) #endif +#ifdef __riscv_shadow_stack + /* read ssp into t0 */ + ssrdp t0 +# ifndef __riscv_float_abi_soft + REG_S t0, 14*SZREG+12*SZFREG(a0) +# else + REG_S t0, 14*SZREG(a0) +# endif +#endif + #if !IS_IN (libc) && IS_IN (rtld) /* In ld.so we never save the signal mask. */ li a0, 0 diff --git a/sysdeps/riscv/start.S b/sysdeps/riscv/start.S index 2db79c0ae6..ff083b13a4 100644 --- a/sysdeps/riscv/start.S +++ b/sysdeps/riscv/start.S @@ -47,12 +47,14 @@ ENTRY (ENTRY_POINT) .cfi_label to force starting the FDE. */ .cfi_label .Ldummy cfi_undefined (ra) + LPAD call load_gp mv a5, a0 /* rtld_fini. */ /* main may be in a shared library. */ #if defined PIC && !defined SHARED /* Avoid relocation in static PIE since _start is called before it is relocated. */ + SET_LPAD lla a0, __wrap_main #else la a0, main @@ -69,7 +71,11 @@ ENTRY (ENTRY_POINT) END (ENTRY_POINT) #if defined PIC && !defined SHARED +#ifdef __riscv_landing_pad + .align 2 +#endif /* __riscv_landing_pad */ __wrap_main: + LPAD tail main@plt #endif @@ -79,9 +85,13 @@ __wrap_main: needs to be initialized before calling __libc_start_main in that case. So we redundantly initialize it at the beginning of _start. */ +#ifdef __riscv_landing_pad + .align 2 +#endif /* __riscv_landing_pad */ load_gp: .option push .option norelax + LPAD lla gp, __global_pointer$ .option pop ret diff --git a/sysdeps/unix/sysv/linux/riscv/Makefile b/sysdeps/unix/sysv/linux/riscv/Makefile index 04abf226ad..7e81cc078a 100644 --- a/sysdeps/unix/sysv/linux/riscv/Makefile +++ b/sysdeps/unix/sysv/linux/riscv/Makefile @@ -7,6 +7,7 @@ sysdep_headers += \ sysdep_routines += \ flush-icache \ hwprobe \ + allocate-shadow-stack \ # sysdep_routines endif diff --git a/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.c b/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.c new file mode 100644 index 0000000000..7e060e1fe3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.c @@ -0,0 +1,55 @@ +/* Helper function to allocate shadow stack. + Copyright (C) 2023-2025 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 + . */ + +#include +#include +#include +#include +#include +#include + +/* NB: This can be treated as a syscall by caller. */ + +long int +__allocate_shadow_stack (size_t stack_size, + shadow_stack_size_t *child_stack) +{ +#ifdef __NR_map_shadow_stack + size_t shadow_stack_size + = stack_size >> STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT; + /* Align shadow stack to 8 bytes. */ + shadow_stack_size = ALIGN_UP (shadow_stack_size, 8); + /* Since sigaltstack shares shadow stack with the current context in + the thread, add extra 20 stack frames in shadow stack for signal + handlers. */ + shadow_stack_size += 20 * 8; + void *shadow_stack = (void *)INLINE_SYSCALL_CALL + (map_shadow_stack, NULL, shadow_stack_size, SHADOW_STACK_SET_TOKEN); + /* Report the map_shadow_stack error. */ + if (shadow_stack == MAP_FAILED) + return -errno; + + /* Save the shadow stack base and size on child stack. */ + child_stack[0] = (uintptr_t) shadow_stack; + child_stack[1] = shadow_stack_size; + + return 0; +#else + return -ENOSYS; +#endif +} diff --git a/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.h b/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.h new file mode 100644 index 0000000000..af5f0f1dda --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/allocate-shadow-stack.h @@ -0,0 +1,24 @@ +/* Helper function to allocate shadow stack. + Copyright (C) 2023-2025 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 + . */ + +#include + +typedef __typeof (((ucontext_t *) 0)->uc_ssp) shadow_stack_size_t; + +extern long int __allocate_shadow_stack (size_t, shadow_stack_size_t *) + attribute_hidden; diff --git a/sysdeps/unix/sysv/linux/riscv/bits/mman.h b/sysdeps/unix/sysv/linux/riscv/bits/mman.h new file mode 100644 index 0000000000..683d45b72e --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/bits/mman.h @@ -0,0 +1,30 @@ +/* Definitions for POSIX memory map interface. Linux/risc-v version. + Copyright (C) 1997-2025 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 + . */ + +#ifndef _SYS_MMAN_H +# error "Never use directly; include instead." +#endif + +#ifdef __riscv_shadow_stack +# define SHADOW_STACK_SET_TOKEN 0x1 +#endif + +#include + +/* Include generic Linux declarations. */ +#include diff --git a/sysdeps/unix/sysv/linux/riscv/bits/types/__sigset_t.h b/sysdeps/unix/sysv/linux/riscv/bits/types/__sigset_t.h new file mode 100644 index 0000000000..308ae99a10 --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/bits/types/__sigset_t.h @@ -0,0 +1,13 @@ +/* Architecture-specific __sigset_t definition. ARC version. */ +#ifndef ____sigset_t_defined +#define ____sigset_t_defined + +/* We shrink it a bit to make space for storing shadow stack pointer. */ + +#define _SIGSET_NWORDS (896 / (8 * sizeof (unsigned long int))) +typedef struct +{ + unsigned long int __val[_SIGSET_NWORDS]; +} __sigset_t; + +#endif diff --git a/sysdeps/unix/sysv/linux/riscv/clone.S b/sysdeps/unix/sysv/linux/riscv/clone.S index 1362dd9c22..d6a0996ec8 100644 --- a/sysdeps/unix/sysv/linux/riscv/clone.S +++ b/sysdeps/unix/sysv/linux/riscv/clone.S @@ -31,6 +31,7 @@ .text LEAF (__clone) + LPAD /* Align stack to a 128-bit boundary as per RISC-V ABI. */ andi a1,a1,ALMASK @@ -82,6 +83,7 @@ L (thread_start): REG_L a0,SZREG(sp) /* Argument pointer. */ /* Call the user's function. */ + SET_LPAD jalr a1 /* Call exit with the function's return value. */ 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..8bbb379fea --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/dl-cfi.h @@ -0,0 +1,115 @@ +/* Linux/RISC-V CFI initializers function. + Copyright (C) 2025 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 + . */ + +/* FIXME: Should be remove after they are included in the kernel header */ +#include +#include +#include + +#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\ +" + +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 */ diff --git a/sysdeps/unix/sysv/linux/riscv/getcontext.S b/sysdeps/unix/sysv/linux/riscv/getcontext.S index 86e7a8ff91..e529ad98ef 100644 --- a/sysdeps/unix/sysv/linux/riscv/getcontext.S +++ b/sysdeps/unix/sysv/linux/riscv/getcontext.S @@ -17,11 +17,13 @@ . */ #include "ucontext-macros.h" +#include "tcb-offsets.h" /* int getcontext (ucontext_t *ucp) */ .text LEAF (__getcontext) + LPAD SAVE_INT_REG (ra, 0, a0) SAVE_INT_REG (ra, 1, a0) SAVE_INT_REG (sp, 2, a0) @@ -58,6 +60,24 @@ LEAF (__getcontext) sw a1, MCONTEXT_FSR(a0) #endif /* __riscv_float_abi_soft */ +#ifdef __riscv_shadow_stack + ssrdp t0 + beqz t0, .Lskip_ss + /* Read ssp_base from TLS */ + ld t1, SSP_BASE_OFFSET(tp) + + bnez t1, .Lbase_saved + /* if not found, use current ssp as the marker */ + mv t1, t0 + sd t1, SSP_BASE_OFFSET(tp) + +.Lbase_saved: + /* Save caller's ssp and base marker to ucontext */ + REG_S t1, UCONTEXT_SSP_BASE(a0) + REG_S t0, UCONTEXT_SSP(a0) +.Lskip_ss: +#endif + /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */ li a3, _NSIG8 add a2, a0, UCONTEXT_SIGMASK diff --git a/sysdeps/unix/sysv/linux/riscv/include/asm/prctl.h b/sysdeps/unix/sysv/linux/riscv/include/asm/prctl.h new file mode 100644 index 0000000000..091a21b70d --- /dev/null +++ b/sysdeps/unix/sysv/linux/riscv/include/asm/prctl.h @@ -0,0 +1,48 @@ +/* + * Get the current shadow stack configuration for the current thread, + * this will be the value configured via PR_SET_SHADOW_STACK_STATUS. + */ +#define PR_GET_SHADOW_STACK_STATUS 74 + +/* + * Set the current shadow stack configuration. Enabling the shadow + * stack will cause a shadow stack to be allocated for the thread. + */ +#define PR_SET_SHADOW_STACK_STATUS 75 +# define PR_SHADOW_STACK_ENABLE (1UL << 0) +# define PR_SHADOW_STACK_WRITE (1UL << 1) +# define PR_SHADOW_STACK_PUSH (1UL << 2) + +/* + * Prevent further changes to the specified shadow stack + * configuration. All bits may be locked via this call, including + * undefined bits. + */ +#define PR_LOCK_SHADOW_STACK_STATUS 76 + +/* + * Get the current indirect branch tracking configuration for the current + * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. + */ +#define PR_GET_INDIR_BR_LP_STATUS 79 + +/* + * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will + * enable cpu feature for user thread, to track all indirect branches and ensure + * they land on arch defined landing pad instruction. + * x86 - If enabled, an indirect branch must land on `ENDBRANCH` instruction. + * arch64 - If enabled, an indirect branch must land on `BTI` instruction. + * riscv - If enabled, an indirect branch must land on `lpad` instruction. + * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect + * branches will no more be tracked by cpu to land on arch defined landing pad + * instruction. + */ +#define PR_SET_INDIR_BR_LP_STATUS 80 +# define PR_INDIR_BR_LP_ENABLE (1UL << 0) + +/* + * Prevent further changes to the specified indirect branch tracking + * configuration. All bits may be locked via this call, including + * undefined bits. + */ +#define PR_LOCK_INDIR_BR_LP_STATUS 81 diff --git a/sysdeps/unix/sysv/linux/riscv/makecontext.c b/sysdeps/unix/sysv/linux/riscv/makecontext.c index 3da27dd5df..1f9bef6887 100644 --- a/sysdeps/unix/sysv/linux/riscv/makecontext.c +++ b/sysdeps/unix/sysv/linux/riscv/makecontext.c @@ -21,6 +21,9 @@ #include #include #include +#ifdef __riscv_shadow_stack +#include +#endif void __makecontext (ucontext_t *ucp, void (*func) (void), int argc, @@ -73,6 +76,21 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc, va_end (vl); } +#ifdef __riscv_shadow_stack + /* Allocate shadow stack for the new context */ + + /* shstk_size[0]: shadow stack base + shstk_size[1]: shadow stack size */ + shadow_stack_size_t shstk_size[2]; + int ret = __allocate_shadow_stack(ucp->uc_stack.ss_size, shstk_size); + if (ret != 0) + { + abort(); + } + + ucp->uc_ssp_base = shstk_size[0]; + ucp->uc_ssp = shstk_size[0] + shstk_size[1] - sizeof (shstk_size[0]); +#endif } weak_alias (__makecontext, makecontext) diff --git a/sysdeps/unix/sysv/linux/riscv/setcontext.S b/sysdeps/unix/sysv/linux/riscv/setcontext.S index a2de57b537..eb7ffc5f3a 100644 --- a/sysdeps/unix/sysv/linux/riscv/setcontext.S +++ b/sysdeps/unix/sysv/linux/riscv/setcontext.S @@ -17,6 +17,7 @@ . */ #include "ucontext-macros.h" +#include "tcb-offsets.h" /* int __setcontext (const ucontext_t *ucp) @@ -29,6 +30,7 @@ .text LEAF (__setcontext) + LPAD mv t0, a0 /* Save ucp into t0. */ @@ -45,6 +47,55 @@ LEAF (__setcontext) cfi_def_cfa (t0, 0) +#ifdef __riscv_shadow_stack + /* Skip if shadow stack is not enabled */ + ssrdp ra + beqz ra, .Lfin + /* We are safe to adjust shadow stack after the sanity check */ + REG_L t1, UCONTEXT_SSP_BASE(t0) + REG_L a1, UCONTEXT_SSP(t0) + REG_L a2, SSP_BASE_OFFSET(tp) + bne t1, a2, .Ldifferent_stack + +.Lunwind: + bleu a1, ra, .Lfin + /* increase ssp by at most a page size to ensure always run into + a guard page before accidentally point to another legal shadow + stack page */ + /* ra = (a1 - ra >= 4096) ? ra + 4096 : a1 */ + lui t2, 1 + add ra, ra, t2 + bleu ra, a1, 1f + mv ra, a1 +1: + csrw ssp, ra + /* Test if the location pointed by ssp is legal */ + sspush ra + sspopchk ra + j .Lunwind + +.Ldifferent_stack: + /* Create restore token */ + sspush ra + mv a4, a1 + +.Lfind_rstor_token: + /* Probe and validate target restore token */ + ssamoswap.d a3, x0, (a4) + addi a2, a4, 8 + beq a3, a2, .Lswitch_stack + /* Restore the shadow stack and try the next slot */ + ssamoswap.d x0, a3, (a4) + addi a4, a4, -8 + j .Lfind_rstor_token + +.Lswitch_stack: + /* Switch stack: update ssp and base */ + csrw ssp, a1 + REG_S t1, SSP_BASE_OFFSET(tp) +.Lfin: +#endif + #ifndef __riscv_float_abi_soft lw t1, MCONTEXT_FSR(t0) @@ -66,7 +117,11 @@ LEAF (__setcontext) /* Note the contents of argument registers will be random unless makecontext() has been called. */ +#ifdef __riscv_landing_pad + RESTORE_INT_REG (t2, 0, t0) +#else RESTORE_INT_REG (t1, 0, t0) +#endif RESTORE_INT_REG_CFI (ra, 1, t0) RESTORE_INT_REG (sp, 2, t0) RESTORE_INT_REG_CFI (s0, 8, t0) @@ -90,7 +145,12 @@ LEAF (__setcontext) RESTORE_INT_REG_CFI (s10, 26, t0) RESTORE_INT_REG_CFI (s11, 27, t0) +#ifdef __riscv_landing_pad + /* We need to use software-guared jump */ + jr t2 +#else jr t1 +#endif 99: tail __syscall_error @@ -99,12 +159,19 @@ libc_hidden_def (__setcontext) weak_alias (__setcontext, setcontext) LEAF (__start_context) + LPAD /* Terminate call stack by noting ra == 0. Happily, s0 == 0 here. */ cfi_register (ra, s0) /* Call the function passed to makecontext. */ +#ifdef __riscv_landing_pad + /* We need to use software-guared jump */ + mv t2, s1 + jalr t2 +#else jalr s1 +#endif /* Invoke subsequent context if present, else exit(0). */ mv a0, s2 diff --git a/sysdeps/unix/sysv/linux/riscv/swapcontext.S b/sysdeps/unix/sysv/linux/riscv/swapcontext.S index bf5754c8b5..cb76eca9b1 100644 --- a/sysdeps/unix/sysv/linux/riscv/swapcontext.S +++ b/sysdeps/unix/sysv/linux/riscv/swapcontext.S @@ -17,10 +17,12 @@ . */ #include "ucontext-macros.h" +#include "tcb-offsets.h" /* int swapcontext (ucontext_t *oucp, const ucontext_t *ucp) */ LEAF (__swapcontext) + LPAD mv t0, a1 /* Save ucp into t0. */ SAVE_INT_REG (ra, 0, a0) @@ -59,6 +61,25 @@ LEAF (__swapcontext) sw a1, MCONTEXT_FSR(a0) #endif /* __riscv_float_abi_soft */ +#ifdef __riscv_shadow_stack + /* Skip if shadow stack is not enabled */ + ssrdp ra + beqz ra, .Lfin + + /* Read ssp_base from TLS */ + ld t2, SSP_BASE_OFFSET(tp) + bnez t2, .Lbase_saved + + /* if not found, use current ssp as the marker */ + mv t2, ra + sd t2, SSP_BASE_OFFSET(tp) + +.Lbase_saved: + /* Save caller's ssp and base marker to oucp */ + REG_S t2, UCONTEXT_SSP_BASE(a0) + REG_S ra, UCONTEXT_SSP(a0) +#endif + /* rt_sigprocmask (SIG_SETMASK, &ucp->uc_sigmask, &oucp->uc_sigmask, _NSIG8) */ li a3, _NSIG8 add a2, a0, UCONTEXT_SIGMASK @@ -70,6 +91,52 @@ LEAF (__swapcontext) bltz a0, 99f +#ifdef __riscv_shadow_stack + /* Load ss information from ucp */ + REG_L a0, UCONTEXT_SSP_BASE(t0) + REG_L a1, UCONTEXT_SSP(t0) + REG_L a2, SSP_BASE_OFFSET(tp) + bne a0, a2, .Ldifferent_stack + +.Lunwind: + bleu a1, ra, .Lfin + /* increase ssp by at most a page size to ensure always run into + a guard page before accidentally point to another legal shadow + stack page */ + /* ra = (a1 - ra >= 4096) ? ra + 4096 : a1 */ + lui t2, 1 + add ra, ra, t2 + bleu ra, a1, 1f + mv ra, a1 +1: + csrw ssp, ra + /* Test if the location pointed by ssp is legal */ + sspush ra + sspopchk ra + j .Lunwind + +.Ldifferent_stack: + /* Create restore token */ + sspush ra + mv a4, a1 + +.Lfind_rstor_token: + /* Probe and validate target restore token */ + ssamoswap.d a3, x0, (a4) + addi a2, a4, 8 + beq a3, a2, .Lswitch_stack + /* Restore the shadow stack and try the next slot */ + ssamoswap.d x0, a3, (a4) + addi a4, a4, -8 + j .Lfind_rstor_token + +.Lswitch_stack: + /* Switch stack: update ssp and base */ + csrw ssp, a1 + REG_S a0, SSP_BASE_OFFSET(tp) +.Lfin: +#endif + #ifndef __riscv_float_abi_soft lw t1, MCONTEXT_FSR(t0) @@ -91,7 +158,11 @@ LEAF (__swapcontext) /* Note the contents of argument registers will be random unless makecontext() has been called. */ +#ifdef __riscv_landing_pad + RESTORE_INT_REG (t2, 0, t0) +#else RESTORE_INT_REG (t1, 0, t0) +#endif RESTORE_INT_REG (ra, 1, t0) RESTORE_INT_REG (sp, 2, t0) RESTORE_INT_REG (s0, 8, t0) @@ -115,8 +186,12 @@ LEAF (__swapcontext) RESTORE_INT_REG (s10, 26, t0) RESTORE_INT_REG (s11, 27, t0) +#ifdef __riscv_landing_pad + /* We need to use software-guared jump */ + jr t2 +#else jr t1 - +#endif 99: tail __syscall_error diff --git a/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h b/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h index a572ec62ee..9c157fa0d8 100644 --- a/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h +++ b/sysdeps/unix/sysv/linux/riscv/sys/ucontext.h @@ -95,7 +95,12 @@ typedef struct ucontext_t future. Though this is unlikely, other architectures put uc_sigmask at the end of this structure and explicitly state it can be expanded, so we didn't want to box ourselves in here. */ - char __glibc_reserved[1024 / 8 - sizeof (sigset_t)]; + /* We've taken 16 bytes from uc_sigmask by shrinking sigset_t to store the + shadow stack information. */ + unsigned long long int uc_ssp; + unsigned long long int uc_ssp_base; + char __glibc_reserved[1024 / 8 - sizeof (sigset_t) - \ + 2 * sizeof (unsigned long long int)]; /* We can't put uc_sigmask at the end of this structure because we need to be able to expand sigcontext in the future. For example, the vector ISA extension will almost certainly add ISA state. We want diff --git a/sysdeps/unix/sysv/linux/riscv/sysdep.S b/sysdeps/unix/sysv/linux/riscv/sysdep.S index b50671b9b7..e7ea424e45 100644 --- a/sysdeps/unix/sysv/linux/riscv/sysdep.S +++ b/sysdeps/unix/sysv/linux/riscv/sysdep.S @@ -23,6 +23,7 @@ #endif ENTRY (__syscall_error) + LPAD mv t0, ra /* Fall through to __syscall_set_errno. */ END (__syscall_error) diff --git a/sysdeps/unix/sysv/linux/riscv/sysdep.h b/sysdeps/unix/sysv/linux/riscv/sysdep.h index ee015dfeb6..da28831655 100644 --- a/sysdeps/unix/sysv/linux/riscv/sysdep.h +++ b/sysdeps/unix/sysv/linux/riscv/sysdep.h @@ -53,6 +53,75 @@ # include +/* GNU_PROPERTY_RISCV_* macros from elf.h for use in asm code. */ +#define FEATURE_1_AND 0xc0000000 + +/* Add a NT_GNU_PROPERTY_TYPE_0 note. */ +#if __riscv_xlen == 32 +# define GNU_PROPERTY(type, value) \ + .section .note.gnu.property, "a"; \ + .p2align 2; \ + .word 4; \ + .word 12; \ + .word 5; \ + .asciz "GNU"; \ + .word type; \ + .word 4; \ + .word value; \ + .text +#else +# define GNU_PROPERTY(type, value) \ + .section .note.gnu.property, "a"; \ + .p2align 3; \ + .word 4; \ + .word 16; \ + .word 5; \ + .asciz "GNU"; \ + .word type; \ + .word 4; \ + .word value; \ + .word 0; \ + .text +#endif + +/* Add GNU property note with the supported features to all asm code + where sysdep.h is included. */ +#undef __VALUE_FOR_FEATURE_1_AND +#if defined (__riscv_landing_pad) || defined (__riscv_shadow_stack) +# if defined (__riscv_landing_pad_unlabeled) +# if defined (__riscv_shadow_stack) +# define __VALUE_FOR_FEATURE_1_AND 0x3 +# else +# define __VALUE_FOR_FEATURE_1_AND 0x1 +# endif +# elif defined (__riscv_landing_pad_func_sig) +# if defined (__riscv_shadow_stack) +# define __VALUE_FOR_FEATURE_1_AND 0x6 +# else +# define __VALUE_FOR_FEATURE_1_AND 0x4 +# endif +# else +# if defined (__riscv_shadow_stack) +# define __VALUE_FOR_FEATURE_1_AND 0x2 +# else +# error "What?" +# endif +# endif +#endif + +#if defined (__VALUE_FOR_FEATURE_1_AND) +GNU_PROPERTY (FEATURE_1_AND, __VALUE_FOR_FEATURE_1_AND) +#endif +#undef __VALUE_FOR_FEATURE_1_AND + +#ifdef __riscv_landing_pad_unlabeled +# define SET_LPAD +# define LPAD lpad 0 +#else +# define SET_LPAD +# define LPAD +#endif + # define ENTRY(name) LEAF(name) # define L(label) .L ## label @@ -64,6 +133,7 @@ .text; \ .align 2; \ ENTRY (name); \ + LPAD; \ li a7, SYS_ify (syscall_name); \ scall; \ li a7, -4096; \ @@ -111,6 +181,7 @@ # define PSEUDO_NOERRNO(name, syscall_name, args) \ .align 2; \ ENTRY (name); \ + LPAD; \ li a7, SYS_ify (syscall_name); \ scall; @@ -136,6 +207,8 @@ #else /* !__ASSEMBLER__ */ +# define STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT 5 + # if __WORDSIZE == 64 # define VDSO_NAME "LINUX_4.15" # define VDSO_HASH 182943605 diff --git a/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym b/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym index be55b26310..d9f7957983 100644 --- a/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym +++ b/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym @@ -20,6 +20,8 @@ UCONTEXT_LINK ucontext (uc_link) UCONTEXT_STACK ucontext (uc_stack) UCONTEXT_MCONTEXT ucontext (uc_mcontext) UCONTEXT_SIGMASK ucontext (uc_sigmask) +UCONTEXT_SSP ucontext (uc_ssp) +UCONTEXT_SSP_BASE ucontext (uc_ssp_base) STACK_SP stack (ss_sp) STACK_SIZE stack (ss_size) diff --git a/sysdeps/unix/sysv/linux/riscv/vfork.S b/sysdeps/unix/sysv/linux/riscv/vfork.S index 1e39b7a057..4cf7b5a9d8 100644 --- a/sysdeps/unix/sysv/linux/riscv/vfork.S +++ b/sysdeps/unix/sysv/linux/riscv/vfork.S @@ -29,6 +29,7 @@ .text LEAF (__libc_vfork) + LPAD li a0, (CLONE_VFORK | CLONE_VM | SIGCHLD) mv a1, sp