From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
v25: Removal of `riscv_nousercfi` from `cpufeature.c` and instead placing
it as extern in `usercfi.h` was leading to build error whene cfi config
is not selected. Placed `riscv_nousercfi` outside cfi config ifdef block
in `usercfi.h`
v24:
Took PaulW suggestion for fixing commit desc and checkpatch fixes
in patch #21.
"riscv: kernel command line option to opt out of user cfi"
Collected "Tested-by" tags from Andreas and Valentin. Thanks a lot
for testing it.
v23:
fixed some of the "CHECK:" reported on checkpatch --strict.
Accepted Joel's suggestion for kselftest's Makefile.
CONFIG_RISCV_USER_CFI is enabled when zicfiss, zicfilp and fcf-protection
are all present in toolchain
v22: fixing build error due to -march=zicfiss being picked in gcc-13 and above
but not actually doing any codegen or recognizing instruction for zicfiss.
Change in v22 makes dependence on `-fcf-protection=full` compiler flag to
ensure that toolchain has support and then only CONFIG_RISCV_USER_CFI will be
visible in menuconfig.
v21: fixed build errors.
Basics and overview
===================
Software with larger attack surfaces (e.g. network facing apps like databases,
browsers or apps relying on browser runtimes) suffer from memory corruption
issues which can be utilized by attackers to bend control flow of the program
to eventually gain control (by making their payload executable). Attackers are
able to perform such attacks by leveraging call-sites which rely on indirect
calls or return sites which rely on obtaining return address from stack memory.
To mitigate such attacks, risc-v extension zicfilp enforces that all indirect
calls must land on a landing pad instruction `lpad` else cpu will raise software
check exception (a new cpu exception cause code on riscv).
Similarly for return flow, risc-v extension zicfiss extends architecture with
- `sspush` instruction to push return address on a shadow stack
- `sspopchk` instruction to pop return address from shadow stack
and compare with input operand (i.e. return address on stack)
- `sspopchk` to raise software check exception if comparision above
was a mismatch
- Protection mechanism using which shadow stack is not writeable via
regular store instructions
More information an details can be found at extensions github repo [1].
Equivalent to landing pad (zicfilp) on x86 is `ENDBRANCH` instruction in Intel
CET [3] and branch target identification (BTI) [4] on arm.
Similarly x86's Intel CET has shadow stack [5] and arm64 has guarded control
stack (GCS) [6] which are very similar to risc-v's zicfiss shadow stack.
x86 and arm64 support for user mode shadow stack is already in mainline.
Kernel awareness for user control flow integrity
================================================
This series picks up Samuel Holland's envcfg changes [2] as well. So if those are
being applied independently, they should be removed from this series.
Enabling:
In order to maintain compatibility and not break anything in user mode, kernel
doesn't enable control flow integrity cpu extensions on binary by default.
Instead exposes a prctl interface to enable, disable and lock the shadow stack
or landing pad feature for a task. This allows userspace (loader) to enumerate
if all objects in its address space are compiled with shadow stack and landing
pad support and accordingly enable the feature. Additionally if a subsequent
`dlopen` happens on a library, user mode can take a decision again to disable
the feature (if incoming library is not compiled with support) OR terminate the
task (if user mode policy is strict to have all objects in address space to be
compiled with control flow integirty cpu feature). prctl to enable shadow stack
results in allocating shadow stack from virtual memory and activating for user
address space. x86 and arm64 are also following same direction due to similar
reason(s).
clone/fork:
On clone and fork, cfi state for task is inherited by child. Shadow stack is
part of virtual memory and is a writeable memory from kernel perspective
(writeable via a restricted set of instructions aka shadow stack instructions)
Thus kernel changes ensure that this memory is converted into read-only when
fork/clone happens and COWed when fault is taken due to sspush, sspopchk or
ssamoswap. In case `CLONE_VM` is specified and shadow stack is to be enabled,
kernel will automatically allocate a shadow stack for that clone call.
map_shadow_stack:
x86 introduced `map_shadow_stack` system call to allow user space to explicitly
map shadow stack memory in its address space. It is useful to allocate shadow
for different contexts managed by a single thread (green threads or contexts)
risc-v implements this system call as well.
signal management:
If shadow stack is enabled for a task, kernel performs an asynchronous control
flow diversion to deliver the signal and eventually expects userspace to issue
sigreturn so that original execution can be resumed. Even though resume context
is prepared by kernel, it is in user space memory and is subject to memory
corruption and corruption bugs can be utilized by attacker in this race window
to perform arbitrary sigreturn and eventually bypass cfi mechanism.
Another issue is how to ensure that cfi related state on sigcontext area is not
trampled by legacy apps or apps compiled with old kernel headers.
In order to mitigate control-flow hijacting, kernel prepares a token and place
it on shadow stack before signal delivery and places address of token in
sigcontext structure. During sigreturn, kernel obtains address of token from
sigcontext struture, reads token from shadow stack and validates it and only
then allow sigreturn to succeed. Compatiblity issue is solved by adopting
dynamic sigcontext management introduced for vector extension. This series
re-factor the code little bit to allow future sigcontext management easy (as
proposed by Andy Chiu from SiFive)
config and compilation:
Introduce a new risc-v config option `CONFIG_RISCV_USER_CFI`. Selecting this
config option picks the kernel support for user control flow integrity. This
optin is presented only if toolchain has shadow stack and landing pad support.
And is on purpose guarded by toolchain support. Reason being that eventually
vDSO also needs to be compiled in with shadow stack and landing pad support.
vDSO compile patches are not included as of now because landing pad labeling
scheme is yet to settle for usermode runtime.
To get more information on kernel interactions with respect to
zicfilp and zicfiss, patch series adds documentation for
`zicfilp` and `zicfiss` in following:
Documentation/arch/riscv/zicfiss.rst
Documentation/arch/riscv/zicfilp.rst
How to test this series
=======================
Toolchain
---------
$ git clone git@github.com:sifive/riscv-gnu-toolchain.git -b cfi-dev
$ riscv-gnu-toolchain/configure --prefix=<path-to-where-to-build> --with-arch=rv64gc_zicfilp_zicfiss --enable-linux --disable-gdb --with-extra-multilib-test="rv64gc_zicfilp_zicfiss-lp64d:-static"
$ make -j$(nproc)
Qemu
----
Get the lastest qemu
$ cd qemu
$ mkdir build
$ cd build
$ ../configure --target-list=riscv64-softmmu
$ make -j$(nproc)
Opensbi
-------
$ git clone git@github.com:deepak0414/opensbi.git -b v6_cfi_spec_split_opensbi
$ make CROSS_COMPILE=<your riscv toolchain> -j$(nproc) PLATFORM=generic
Linux
-----
Running defconfig is fine. CFI is enabled by default if the toolchain
supports it.
$ make ARCH=riscv CROSS_COMPILE=<path-to-cfi-riscv-gnu-toolchain>/build/bin/riscv64-unknown-linux-gnu- -j$(nproc) defconfig
$ make ARCH=riscv CROSS_COMPILE=<path-to-cfi-riscv-gnu-toolchain>/build/bin/riscv64-unknown-linux-gnu- -j$(nproc)
Running
-------
Modify your qemu command to have:
-bios <path-to-cfi-opensbi>/build/platform/generic/firmware/fw_dynamic.bin
-cpu rv64,zicfilp=true,zicfiss=true,zimop=true,zcmop=true
References
==========
[1] - https://github.com/riscv/riscv-cfi
[2] - https://lore.kernel.org/all/20240814081126.956287-1-samuel.holland@sifive.com/
[3] - https://lwn.net/Articles/889475/
[4] - https://developer.arm.com/documentation/109576/0100/Branch-Target-Identification
[5] - https://www.intel.com/content/dam/develop/external/us/en/documents/catc17-introduction-intel-cet-844137.pdf
[6] - https://lwn.net/Articles/940403/
To: Thomas Gleixner <redacted>
To: Ingo Molnar <mingo@redhat.com>
To: Borislav Petkov <bp@alien8.de>
To: Dave Hansen <dave.hansen@linux.intel.com>
To: x86@kernel.org
To: H. Peter Anvin <hpa@zytor.com>
To: Andrew Morton <akpm@linux-foundation.org>
To: Liam R. Howlett <redacted>
To: Vlastimil Babka <redacted>
To: Lorenzo Stoakes <redacted>
To: Paul Walmsley <redacted>
To: Palmer Dabbelt <palmer@dabbelt.com>
To: Albert Ou <aou@eecs.berkeley.edu>
To: Conor Dooley <conor@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Arnd Bergmann <arnd@arndb.de>
To: Christian Brauner <brauner@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>
To: Oleg Nesterov <oleg@redhat.com>
To: Eric Biederman <redacted>
To: Kees Cook <kees@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <shuah@kernel.org>
To: Jann Horn <jannh@google.com>
To: Conor Dooley <conor+dt@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
To: Alex Gaynor <redacted>
To: Boqun Feng <redacted>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <redacted>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
Cc: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-riscv@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org
Cc: alistair.francis@wdc.com
Cc: richard.henderson@linaro.org
Cc: jim.shu@sifive.com
Cc: andybnac@gmail.com
Cc: kito.cheng@sifive.com
Cc: charlie@rivosinc.com
Cc: atishp@rivosinc.com
Cc: evan@rivosinc.com
Cc: cleger@rivosinc.com
Cc: alexghiti@rivosinc.com
Cc: samitolvanen@google.com
Cc: broonie@kernel.org
Cc: rick.p.edgecombe@intel.com
Cc: rust-for-linux@vger.kernel.org
changelog
---------
v25:
- fixed build error when cfi config is not selected due to missing symbol
error on `riscv_nousercfi`.
v24:
- Checkpatch fixes in patch # 21.
- Collected "Tested-by" tags.
v23:
- fixed some of the "CHECK:" reported on checkpatch --strict.
- Accepted Joel's suggestion for kselftest's Makefile.
- CONFIG_RISCV_USER_CFI is enabled when zicfiss, zicfilp and fcf-protection
are all present in toolchain
v22:
- CONFIG_RISCV_USER_CFI was by default "n". With dual vdso support it is
default "y" (if toolchain supports it). Fixing build error due to
"-march=zicfiss" being picked in gcc-13 partially. gcc-13 only recognizes the
flag but not actually doing any codegen or recognizing instruction for zicfiss.
Change in v22 makes dependence on `-fcf-protection=full` compiler flag to
ensure that toolchain has support and then only CONFIG_RISCV_USER_CFI will be
visible in menuconfig.
- picked up tags and some cosmetic changes in commit message for dual vdso
patch.
v21:
- Fixing build errors due to changes in arch/riscv/include/asm/vdso.h
Using #ifdef instead of IS_ENABLED in arch/riscv/include/asm/vdso.h
vdso-cfi-offsets.h should be included only when CONFIG_RISCV_USER_CFI
is selected.
v20:
- rebased on v6.18-rc1.
- Added two vDSO support. If `CONFIG_RISCV_USER_CFI` is selected
two vDSOs are compiled (one for hardware prior to RVA23 and one
for RVA23 onwards). Kernel exposes RVA23 vDSO if hardware/cpu
implements zimop else exposes existing vDSO to userspace.
- default selection for `CONFIG_RISCV_USER_CFI` is "Yes".
- replaced "__ASSEMBLY__" with "__ASSEMBLER__"
v19:
- riscv_nousercfi was `int`. changed it to unsigned long.
Thanks to Alex Ghiti for reporting it. It was a bug.
- ELP is cleared on trap entry only when CONFIG_64BIT.
- restore ssp back on return to usermode was being done
before `riscv_v_context_nesting_end` on trap exit path.
If kernel shadow stack were enabled this would result in
kernel operating on user shadow stack and panic (as I found
in my testing of kcfi patch series). So fixed that.
v18:
- rebased on 6.16-rc1
- uprobe handling clears ELP in sstatus image in pt_regs
- vdso was missing shadow stack elf note for object files.
added that. Additional asm file for vdso needed the elf marker
flag. toolchain should complain if `-fcf-protection=full` and
marker is missing for object generated from asm file. Asked
toolchain folks to fix this. Although no reason to gate the merge
on that.
- Split up compile options for march and fcf-protection in vdso
Makefile
- CONFIG_RISCV_USER_CFI option is moved under "Kernel features" menu
Added `arch/riscv/configs/hardening.config` fragment which selects
CONFIG_RISCV_USER_CFI
v17:
- fixed warnings due to empty macros in usercfi.h (reported by alexg)
- fixed prefixes in commit titles reported by alexg
- took below uprobe with fcfi v2 patch from Zong Li and squashed it with
"riscv/traps: Introduce software check exception and uprobe handling"
https://lore.kernel.org/all/20250604093403.10916-1-zong.li@sifive.com/
v16:
- If FWFT is not implemented or returns error for shadow stack activation, then
no_usercfi is set to disable shadow stack. Although this should be picked up
by extension validation and activation. Fixed this bug for zicfilp and zicfiss
both. Thanks to Charlie Jenkins for reporting this.
- If toolchain doesn't support cfi, cfi kselftest shouldn't build. Suggested by
Charlie Jenkins.
- Default for CONFIG_RISCV_USER_CFI is set to no. Charlie/Atish suggested to
keep it off till we have more hardware availibility with RVA23 profile and
zimop/zcmop implemented. Else this will start breaking people's workflow
- Includes the fix if "!RV64 and !SBI" then definitions for FWFT in
asm-offsets.c error.
v15:
- Toolchain has been updated to include `-fcf-protection` flag. This
exists for x86 as well. Updated kernel patches to compile vDSO and
selftest to compile with `fcf-protection=full` flag.
- selecting CONFIG_RISCV_USERCFI selects CONFIG_RISCV_SBI.
- Patch to enable shadow stack for kernel wasn't hidden behind
CONFIG_RISCV_USERCFI and CONFIG_RISCV_SBI. fixed that.
v14:
- rebased on top of palmer/sbi-v3. Thus dropped clement's FWFT patches
Updated RISCV_ISA_EXT_XXXX in hwcap and hwprobe constants.
- Took Radim's suggestions on bitfields.
- Placed cfi_state at the end of thread_info block so that current situation
is not disturbed with respect to member fields of thread_info in single
cacheline.
v13:
- cpu_supports_shadow_stack/cpu_supports_indirect_br_lp_instr uses
riscv_has_extension_unlikely()
- uses nops(count) to create nop slide
- RISCV_ACQUIRE_BARRIER is not needed in `amo_user_shstk`. Removed it
- changed ternaries to simply use implicit casting to convert to bool.
- kernel command line allows to disable zicfilp and zicfiss independently.
updated kernel-parameters.txt.
- ptrace user abi for cfi uses bitmasks instead of bitfields. Added ptrace
kselftest.
- cosmetic and grammatical changes to documentation.
v12:
- It seems like I had accidently squashed arch agnostic indirect branch
tracking prctl and riscv implementation of those prctls. Split them again.
- set_shstk_status/set_indir_lp_status perform CSR writes only when CPU
support is available. As suggested by Zong Li.
- Some minor clean up in kselftests as suggested by Zong Li.
v11:
- patch "arch/riscv: compile vdso with landing pad" was unconditionally
selecting `_zicfilp` for vDSO compile. fixed that. Changed `lpad 1` to
to `lpad 0`.
v10:
- dropped "mm: helper `is_shadow_stack_vma` to check shadow stack vma". This patch
is not that interesting to this patch series for risc-v. There are instances in
arch directories where VM_SHADOW_STACK flag is anyways used. Dropping this patch
to expedite merging in riscv tree.
- Took suggestions from `Clement` on "riscv: zicfiss / zicfilp enumeration" to
validate presence of cfi based on config.
- Added a patch for vDSO to have `lpad 0`. I had omitted this earlier to make sure
we add single vdso object with cfi enabled. But a vdso object with scheme of
zero labeled landing pad is least common denominator and should work with all
objects of zero labeled as well as function-signature labeled objects.
v9:
- rebased on master (39a803b754d5 fix braino in "9p: fix ->rename_sem exclusion")
- dropped "mm: Introduce ARCH_HAS_USER_SHADOW_STACK" (master has it from arm64/gcs)
- dropped "prctl: arch-agnostic prctl for shadow stack" (master has it from arm64/gcs)
v8:
- rebased on palmer/for-next
- dropped samuel holland's `envcfg` context switch patches.
they are in parlmer/for-next
v7:
- Removed "riscv/Kconfig: enable HAVE_EXIT_THREAD for riscv"
Instead using `deactivate_mm` flow to clean up.
see here for more context
https://lore.kernel.org/all/20230908203655.543765-1-rick.p.edgecombe@intel.com/#t
- Changed the header include in `kselftest`. Hopefully this fixes compile
issue faced by Zong Li at SiFive.
- Cleaned up an orphaned change to `mm/mmap.c` in below patch
"riscv/mm : ensure PROT_WRITE leads to VM_READ | VM_WRITE"
- Lock interfaces for shadow stack and indirect branch tracking expect arg == 0
Any future evolution of this interface should accordingly define how arg should
be setup.
- `mm/map.c` has an instance of using `VM_SHADOW_STACK`. Fixed it to use helper
`is_shadow_stack_vma`.
- Link to v6: https://lore.kernel.org/r/20241008-v5_user_cfi_series-v6-0-60d9fe073f37@rivosinc.com
v6:
- Picked up Samuel Holland's changes as is with `envcfg` placed in
`thread` instead of `thread_info`
- fixed unaligned newline escapes in kselftest
- cleaned up messages in kselftest and included test output in commit message
- fixed a bug in clone path reported by Zong Li
- fixed a build issue if CONFIG_RISCV_ISA_V is not selected
(this was introduced due to re-factoring signal context
management code)
v5:
- rebased on v6.12-rc1
- Fixed schema related issues in device tree file
- Fixed some of the documentation related issues in zicfilp/ss.rst
(style issues and added index)
- added `SHADOW_STACK_SET_MARKER` so that implementation can define base
of shadow stack.
- Fixed warnings on definitions added in usercfi.h when
CONFIG_RISCV_USER_CFI is not selected.
- Adopted context header based signal handling as proposed by Andy Chiu
- Added support for enabling kernel mode access to shadow stack using
FWFT
(https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/src/ext-firmware-features.adoc)
- Link to v5: https://lore.kernel.org/r/20241001-v5_user_cfi_series-v1-0-3ba65b6e550f@rivosinc.com
(Note: I had an issue in my workflow due to which version number wasn't
picked up correctly while sending out patches)
v4:
- rebased on 6.11-rc6
- envcfg: Converged with Samuel Holland's patches for envcfg management on per-
thread basis.
- vma_is_shadow_stack is renamed to is_vma_shadow_stack
- picked up Mark Brown's `ARCH_HAS_USER_SHADOW_STACK` patch
- signal context: using extended context management to maintain compatibility.
- fixed `-Wmissing-prototypes` compiler warnings for prctl functions
- Documentation fixes and amending typos.
- Link to v4: https://lore.kernel.org/all/20240912231650.3740732-1-debug@rivosinc.com/
v3:
- envcfg
logic to pick up base envcfg had a bug where `ENVCFG_CBZE` could have been
picked on per task basis, even though CPU didn't implement it. Fixed in
this series.
- dt-bindings
As suggested, split into separate commit. fixed the messaging that spec is
in public review
- arch_is_shadow_stack change
arch_is_shadow_stack changed to vma_is_shadow_stack
- hwprobe
zicfiss / zicfilp if present will get enumerated in hwprobe
- selftests
As suggested, added object and binary filenames to .gitignore
Selftest binary anyways need to be compiled with cfi enabled compiler which
will make sure that landing pad and shadow stack are enabled. Thus removed
separate enable/disable tests. Cleaned up tests a bit.
- Link to v3: https://lore.kernel.org/lkml/20240403234054.2020347-1-debug@rivosinc.com/
v2:
- Using config `CONFIG_RISCV_USER_CFI`, kernel support for riscv control flow
integrity for user mode programs can be compiled in the kernel.
- Enabling of control flow integrity for user programs is left to user runtime
- This patch series introduces arch agnostic `prctls` to enable shadow stack
and indirect branch tracking. And implements them on riscv.
---
Changes in v25:
- Link to v24: https://lore.kernel.org/r/20251204-v5_user_cfi_series-v24-0-ada7a3ba14dc@rivosinc.com
Changes in v24:
- Link to v23: https://lore.kernel.org/r/20251112-v5_user_cfi_series-v23-0-b55691eacf4f@rivosinc.com
Changes in v23:
- Link to v22: https://lore.kernel.org/r/20251023-v5_user_cfi_series-v22-0-1935270f7636@rivosinc.com
Changes in v22:
- Link to v21: https://lore.kernel.org/r/20251015-v5_user_cfi_series-v21-0-6a07856e90e7@rivosinc.com
Changes in v21:
- Link to v20: https://lore.kernel.org/r/20251013-v5_user_cfi_series-v20-0-b9de4be9912e@rivosinc.com
Changes in v20:
- Link to v19: https://lore.kernel.org/r/20250731-v5_user_cfi_series-v19-0-09b468d7beab@rivosinc.com
Changes in v19:
- Link to v18: https://lore.kernel.org/r/20250711-v5_user_cfi_series-v18-0-a8ee62f9f38e@rivosinc.com
Changes in v18:
- Link to v17: https://lore.kernel.org/r/20250604-v5_user_cfi_series-v17-0-4565c2cf869f@rivosinc.com
Changes in v17:
- Link to v16: https://lore.kernel.org/r/20250522-v5_user_cfi_series-v16-0-64f61a35eee7@rivosinc.com
Changes in v16:
- Link to v15: https://lore.kernel.org/r/20250502-v5_user_cfi_series-v15-0-914966471885@rivosinc.com
Changes in v15:
- changelog posted just below cover letter
- Link to v14: https://lore.kernel.org/r/20250429-v5_user_cfi_series-v14-0-5239410d012a@rivosinc.com
Changes in v14:
- changelog posted just below cover letter
- Link to v13: https://lore.kernel.org/r/20250424-v5_user_cfi_series-v13-0-971437de586a@rivosinc.com
Changes in v13:
- changelog posted just below cover letter
- Link to v12: https://lore.kernel.org/r/20250314-v5_user_cfi_series-v12-0-e51202b53138@rivosinc.com
Changes in v12:
- changelog posted just below cover letter
- Link to v11: https://lore.kernel.org/r/20250310-v5_user_cfi_series-v11-0-86b36cbfb910@rivosinc.com
Changes in v11:
- changelog posted just below cover letter
- Link to v10: https://lore.kernel.org/r/20250210-v5_user_cfi_series-v10-0-163dcfa31c60@rivosinc.com
---
Andy Chiu (1):
riscv: signal: abstract header saving for setup_sigcontext
Deepak Gupta (26):
mm: VM_SHADOW_STACK definition for riscv
dt-bindings: riscv: zicfilp and zicfiss in dt-bindings (extensions.yaml)
riscv: zicfiss / zicfilp enumeration
riscv: zicfiss / zicfilp extension csr and bit definitions
riscv: usercfi state for task and save/restore of CSR_SSP on trap entry/exit
riscv/mm : ensure PROT_WRITE leads to VM_READ | VM_WRITE
riscv/mm: manufacture shadow stack pte
riscv/mm: teach pte_mkwrite to manufacture shadow stack PTEs
riscv/mm: write protect and shadow stack
riscv/mm: Implement map_shadow_stack() syscall
riscv/shstk: If needed allocate a new shadow stack on clone
riscv: Implements arch agnostic shadow stack prctls
prctl: arch-agnostic prctl for indirect branch tracking
riscv: Implements arch agnostic indirect branch tracking prctls
riscv/traps: Introduce software check exception and uprobe handling
riscv/signal: save and restore of shadow stack for signal
riscv/kernel: update __show_regs to print shadow stack register
riscv/ptrace: riscv cfi status and state via ptrace and in core files
riscv/hwprobe: zicfilp / zicfiss enumeration in hwprobe
riscv: kernel command line option to opt out of user cfi
riscv: enable kernel access to shadow stack memory via FWFT sbi call
arch/riscv: dual vdso creation logic and select vdso based on hw
riscv: create a config for shadow stack and landing pad instr support
riscv: Documentation for landing pad / indirect branch tracking
riscv: Documentation for shadow stack on riscv
kselftest/riscv: kselftest for user mode cfi
Jim Shu (1):
arch/riscv: compile vdso with landing pad and shadow stack note
Documentation/admin-guide/kernel-parameters.txt | 8 +
Documentation/arch/riscv/index.rst | 2 +
Documentation/arch/riscv/zicfilp.rst | 115 +++++
Documentation/arch/riscv/zicfiss.rst | 179 +++++++
.../devicetree/bindings/riscv/extensions.yaml | 14 +
arch/riscv/Kconfig | 22 +
arch/riscv/Makefile | 8 +-
arch/riscv/configs/hardening.config | 4 +
arch/riscv/include/asm/asm-prototypes.h | 1 +
arch/riscv/include/asm/assembler.h | 44 ++
arch/riscv/include/asm/cpufeature.h | 12 +
arch/riscv/include/asm/csr.h | 16 +
arch/riscv/include/asm/entry-common.h | 2 +
arch/riscv/include/asm/hwcap.h | 2 +
arch/riscv/include/asm/mman.h | 26 +
arch/riscv/include/asm/mmu_context.h | 7 +
arch/riscv/include/asm/pgtable.h | 30 +-
arch/riscv/include/asm/processor.h | 1 +
arch/riscv/include/asm/thread_info.h | 3 +
arch/riscv/include/asm/usercfi.h | 97 ++++
arch/riscv/include/asm/vdso.h | 13 +-
arch/riscv/include/asm/vector.h | 3 +
arch/riscv/include/uapi/asm/hwprobe.h | 2 +
arch/riscv/include/uapi/asm/ptrace.h | 34 ++
arch/riscv/include/uapi/asm/sigcontext.h | 1 +
arch/riscv/kernel/Makefile | 2 +
arch/riscv/kernel/asm-offsets.c | 10 +
arch/riscv/kernel/cpufeature.c | 25 +
arch/riscv/kernel/entry.S | 38 ++
arch/riscv/kernel/head.S | 27 +
arch/riscv/kernel/process.c | 27 +-
arch/riscv/kernel/ptrace.c | 95 ++++
arch/riscv/kernel/signal.c | 148 +++++-
arch/riscv/kernel/sys_hwprobe.c | 2 +
arch/riscv/kernel/sys_riscv.c | 10 +
arch/riscv/kernel/traps.c | 54 ++
arch/riscv/kernel/usercfi.c | 545 +++++++++++++++++++++
arch/riscv/kernel/vdso.c | 7 +
arch/riscv/kernel/vdso/Makefile | 40 +-
arch/riscv/kernel/vdso/flush_icache.S | 4 +
arch/riscv/kernel/vdso/gen_vdso_offsets.sh | 4 +-
arch/riscv/kernel/vdso/getcpu.S | 4 +
arch/riscv/kernel/vdso/note.S | 3 +
arch/riscv/kernel/vdso/rt_sigreturn.S | 4 +
arch/riscv/kernel/vdso/sys_hwprobe.S | 4 +
arch/riscv/kernel/vdso/vgetrandom-chacha.S | 5 +-
arch/riscv/kernel/vdso_cfi/Makefile | 25 +
arch/riscv/kernel/vdso_cfi/vdso-cfi.S | 11 +
arch/riscv/mm/init.c | 2 +-
arch/riscv/mm/pgtable.c | 16 +
include/linux/cpu.h | 4 +
include/linux/mm.h | 7 +
include/uapi/linux/elf.h | 2 +
include/uapi/linux/prctl.h | 27 +
kernel/sys.c | 30 ++
tools/testing/selftests/riscv/Makefile | 2 +-
tools/testing/selftests/riscv/cfi/.gitignore | 2 +
tools/testing/selftests/riscv/cfi/Makefile | 23 +
tools/testing/selftests/riscv/cfi/cfi_rv_test.h | 82 ++++
tools/testing/selftests/riscv/cfi/cfitests.c | 173 +++++++
tools/testing/selftests/riscv/cfi/shadowstack.c | 385 +++++++++++++++
tools/testing/selftests/riscv/cfi/shadowstack.h | 27 +
62 files changed, 2481 insertions(+), 41 deletions(-)
---
base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
change-id: 20240930-v5_user_cfi_series-3dc332f8f5b2
--
- debug
@@ -444,6 +444,20 @@ properties:The standard Zicboz extension for cache-block zeroing as ratifiedin commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.+-const:zicfilp+description:|+The standard Zicfilp extension for enforcing forward edge+control-flow integrity as ratified in commit 3f8e450 ("merge+pull request#227 from ved-rivos/0709") of riscv-cfi+github repo.++-const:zicfiss+description:|+The standard Zicfiss extension for enforcing backward edge+control-flow integrity as ratified in commit 3f8e450 ("merge+pull request#227 from ved-rivos/0709") of riscv-cfi+github repo.+-const:zicntrdescription:The standard Zicntr extension for base counters and timers, as
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
From: Deepak Gupta <redacted>
This patch adds support for detecting zicfiss and zicfilp. zicfiss and
zicfilp stands for unprivleged integer spec extension for shadow stack
and branch tracking on indirect branches, respectively.
This patch looks for zicfiss and zicfilp in device tree and accordinlgy
lights up bit in cpu feature bitmap. Furthermore this patch adds detection
utility functions to return whether shadow stack or landing pads are
supported by cpu.
Reviewed-by: Zong Li <redacted>
Reviewed-by: Alexandre Ghiti <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/cpufeature.h | 12 ++++++++++++
arch/riscv/include/asm/hwcap.h | 2 ++
arch/riscv/kernel/cpufeature.c | 22 ++++++++++++++++++++++
3 files changed, 36 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
From: Deepak Gupta <redacted>
zicfiss and zicfilp extension gets enabled via b3 and b2 in *envcfg CSR.
menvcfg controls enabling for S/HS mode. henvcfg control enabling for VS
while senvcfg controls enabling for U/VU mode.
zicfilp extension extends *status CSR to hold `expected landing pad` bit.
A trap or interrupt can occur between an indirect jmp/call and target
instr. `expected landing pad` bit from CPU is recorded into xstatus CSR so
that when supervisor performs xret, `expected landing pad` state of CPU can
be restored.
zicfiss adds one new CSR
- CSR_SSP: CSR_SSP contains current shadow stack pointer.
Reviewed-by: Charlie Jenkins <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/csr.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
From: Deepak Gupta <redacted>
pte_mkwrite creates PTEs with WRITE encodings for underlying arch.
Underlying arch can have two types of writeable mappings. One that can be
written using regular store instructions. Another one that can only be
written using specialized store instructions (like shadow stack stores).
pte_mkwrite can select write PTE encoding based on VMA range (i.e.
VM_SHADOW_STACK)
Reviewed-by: Alexandre Ghiti <redacted>
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/pgtable.h | 7 +++++++
arch/riscv/mm/pgtable.c | 16 ++++++++++++++++
2 files changed, 23 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
From: Deepak Gupta <redacted>
`arch_calc_vm_prot_bits` is implemented on risc-v to return VM_READ |
VM_WRITE if PROT_WRITE is specified. Similarly `riscv_sys_mmap` is
updated to convert all incoming PROT_WRITE to (PROT_WRITE | PROT_READ).
This is to make sure that any existing apps using PROT_WRITE still work.
Earlier `protection_map[VM_WRITE]` used to pick read-write PTE encodings.
Now `protection_map[VM_WRITE]` will always pick PAGE_SHADOWSTACK PTE
encodings for shadow stack. Above changes ensure that existing apps
continue to work because underneath kernel will be picking
`protection_map[VM_WRITE|VM_READ]` PTE encodings.
Reviewed-by: Zong Li <redacted>
Reviewed-by: Alexandre Ghiti <redacted>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/mman.h | 26 ++++++++++++++++++++++++++
arch/riscv/include/asm/pgtable.h | 1 +
arch/riscv/kernel/sys_riscv.c | 10 ++++++++++
arch/riscv/mm/init.c | 2 +-
4 files changed, 38 insertions(+), 1 deletion(-)
@@ -16,6 +17,15 @@ static long riscv_sys_mmap(unsigned long addr, unsigned long len,if(unlikely(offset&(~PAGE_MASK>>page_shift_offset)))return-EINVAL;+/*+*IfPROT_WRITEisspecifiedthenextendthattoPROT_READ+*protection_map[VM_WRITE]isnowgoingtoselectshadowstackencodings.+*SospecifyingPROT_WRITEactuallyshouldselectprotection_map[VM_WRITE|VM_READ]+*Ifuserwantstocreateshadowstackthentheyshoulduse`map_shadow_stack`syscall.+*/+if(unlikely((prot&PROT_WRITE)&&!(prot&PROT_READ)))+prot|=PROT_READ;+returnksys_mmap_pgoff(addr,len,prot,flags,fd,offset>>(PAGE_SHIFT-page_shift_offset));}
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:50
From: Deepak Gupta <redacted>
Carves out space in arch specific thread struct for cfi status and shadow
stack in usermode on riscv.
This patch does following
- defines a new structure cfi_status with status bit for cfi feature
- defines shadow stack pointer, base and size in cfi_status structure
- defines offsets to new member fields in thread in asm-offsets.c
- Saves and restore shadow stack pointer on trap entry (U --> S) and exit
(S --> U)
Shadow stack save/restore is gated on feature availiblity and implemented
using alternative. CSR can be context switched in `switch_to` as well but
soon as kernel shadow stack support gets rolled in, shadow stack pointer
will need to be switched at trap entry/exit point (much like `sp`). It can
be argued that kernel using shadow stack deployment scenario may not be as
prevalant as user mode using this feature. But even if there is some
minimal deployment of kernel shadow stack, that means that it needs to be
supported. And thus save/restore of shadow stack pointer in entry.S instead
of in `switch_to.h`.
Reviewed-by: Charlie Jenkins <redacted>
Reviewed-by: Zong Li <redacted>
Reviewed-by: Alexandre Ghiti <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/processor.h | 1 +
arch/riscv/include/asm/thread_info.h | 3 +++
arch/riscv/include/asm/usercfi.h | 23 +++++++++++++++++++++++
arch/riscv/kernel/asm-offsets.c | 4 ++++
arch/riscv/kernel/entry.S | 31 +++++++++++++++++++++++++++++++
5 files changed, 62 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
`fork` implements copy on write (COW) by making pages readonly in child
and parent both.
ptep_set_wrprotect and pte_wrprotect clears _PAGE_WRITE in PTE.
Assumption is that page is readable and on fault copy on write happens.
To implement COW on shadow stack pages, clearing up W bit makes them XWR =
000. This will result in wrong PTE setting which says no perms but V=1 and
PFN field pointing to final page. Instead desired behavior is to turn it
into a readable page, take an access (load/store) fault on sspush/sspop
(shadow stack) and then perform COW on such pages. This way regular reads
would still be allowed and not lead to COW maintaining current behavior
of COW on non-shadow stack but writeable memory.
On the other hand it doesn't interfere with existing COW for read-write
memory. Assumption is always that _PAGE_READ must have been set and thus
setting _PAGE_READ is harmless.
Reviewed-by: Alexandre Ghiti <redacted>
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/pgtable.h | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
Implement architecture agnostic prctls() interface for setting and getting
shadow stack status.
prctls implemented are PR_GET_SHADOW_STACK_STATUS,
PR_SET_SHADOW_STACK_STATUS and PR_LOCK_SHADOW_STACK_STATUS.
As part of PR_SET_SHADOW_STACK_STATUS/PR_GET_SHADOW_STACK_STATUS, only
PR_SHADOW_STACK_ENABLE is implemented because RISCV allows each mode to
write to their own shadow stack using `sspush` or `ssamoswap`.
PR_LOCK_SHADOW_STACK_STATUS locks current configuration of shadow stack
enabling.
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/usercfi.h | 16 ++++++
arch/riscv/kernel/process.c | 8 +++
arch/riscv/kernel/usercfi.c | 110 +++++++++++++++++++++++++++++++++++++++
3 files changed, 134 insertions(+)
@@ -14,6 +15,7 @@ struct kernel_clone_args;#ifdef CONFIG_RISCV_USER_CFIstructcfi_state{unsignedlongubcfi_en:1;/* Enable for backward cfi. */+unsignedlongubcfi_locked:1;unsignedlonguser_shdw_stk;/* Current user shadow stack pointer */unsignedlongshdw_stk_base;/* Base address of shadow stack */unsignedlongshdw_stk_size;/* size of shadow stack */
@@ -260,3 +290,83 @@ void shstk_release(struct task_struct *tsk)vm_munmap(base,size);set_shstk_base(tsk,0,0);}++intarch_get_shadow_stack_status(structtask_struct*t,unsignedlong__user*status)+{+unsignedlongbcfi_status=0;++if(!cpu_supports_shadow_stack())+return-EINVAL;++/* this means shadow stack is enabled on the task */+bcfi_status|=(is_shstk_enabled(t)?PR_SHADOW_STACK_ENABLE:0);++returncopy_to_user(status,&bcfi_status,sizeof(bcfi_status))?-EFAULT:0;+}++intarch_set_shadow_stack_status(structtask_struct*t,unsignedlongstatus)+{+unsignedlongsize=0,addr=0;+boolenable_shstk=false;++if(!cpu_supports_shadow_stack())+return-EINVAL;++/* Reject unknown flags */+if(status&~PR_SHADOW_STACK_SUPPORTED_STATUS_MASK)+return-EINVAL;++/* bcfi status is locked and further can't be modified by user */+if(is_shstk_locked(t))+return-EINVAL;++enable_shstk=status&PR_SHADOW_STACK_ENABLE;+/* Request is to enable shadow stack and shadow stack is not enabled already */+if(enable_shstk&&!is_shstk_enabled(t)){+/* shadow stack was allocated and enable request again+*noneedtosupportsuchusecaseandreturnEINVAL.+*/+if(is_shstk_allocated(t))+return-EINVAL;++size=calc_shstk_size(0);+addr=allocate_shadow_stack(0,size,0,false);+if(IS_ERR_VALUE(addr))+return-ENOMEM;+set_shstk_base(t,addr,size);+set_active_shstk(t,addr+size);+}++/*+*Ifarequesttodisableshadowstackhappens,let'sgoaheadandreleaseit+*Although,ifCLONE_VFORKedchilddidthis,theninthatcasewewillendup+*notreleasingtheshadowstack(becauseitmightbeneededinparent).Although+*wewilldisableitforVFORKedchild.AndifVFORKedchildtriestoenableagain+*theninthatcase,it'llgetentirelynewshadowstackbecausefollowingcondition+*aretrue+*-shadowstackwasnotenabledforvforkedchild+*-shadowstackbasewasanywayspointingto0+*Thisshouldn'tbeabigissuebecausewewantparenttohaveavailabilityofshadow+*stackwheneverVFORKedchildreleasesresourcesviaexitorexecbutatthesame+*timewewantVFORKedchildtobreakawayandestablishnewshadowstackifitdesires+*+*/+if(!enable_shstk)+shstk_release(t);++set_shstk_status(t,enable_shstk);+return0;+}++intarch_lock_shadow_stack_status(structtask_struct*task,+unsignedlongarg)+{+/* If shtstk not supported or not enabled on task, nothing to lock here */+if(!cpu_supports_shadow_stack()||+!is_shstk_enabled(task)||arg!=0)+return-EINVAL;++set_shstk_lock(task);++return0;+}
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
As discussed extensively in the changelog for the addition of this
syscall on x86 ("x86/shstk: Introduce map_shadow_stack syscall") the
existing mmap() and madvise() syscalls do not map entirely well onto the
security requirements for shadow stack memory since they lead to windows
where memory is allocated but not yet protected or stacks which are not
properly and safely initialised. Instead a new syscall map_shadow_stack()
has been defined which allocates and initialises a shadow stack page.
This patch implements this syscall for riscv. riscv doesn't require token
to be setup by kernel because user mode can do that by itself. However to
provide compatibility and portability with other architectues, user mode
can specify token set flag.
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/usercfi.c | 142 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 143 insertions(+)
@@ -0,0 +1,142 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright(C)2024Rivos,Inc.+*DeepakGupta<debug@rivosinc.com>+*/++#include<linux/sched.h>+#include<linux/bitops.h>+#include<linux/types.h>+#include<linux/mm.h>+#include<linux/mman.h>+#include<linux/uaccess.h>+#include<linux/sizes.h>+#include<linux/user.h>+#include<linux/syscalls.h>+#include<linux/prctl.h>+#include<asm/csr.h>+#include<asm/usercfi.h>++#define SHSTK_ENTRY_SIZE sizeof(void *)++/*+*Writesonshadowstackcaneitherbe`sspush`or`ssamoswap`.`sspush`canhappen+*implicitlyoncurrentshadowstackpointedtobyCSR_SSP.`ssamoswap`takespointerto+*shadowstack.Tokeepitsimple,weplantouse`ssamoswap`toperformwritesonshadow+*stack.+*/+staticnoinlineunsignedlongamo_user_shstk(unsignedlong*addr,unsignedlongval)+{+/*+*Neverexpect-1onshadowstack.Expectreturnaddressesandzero+*/+unsignedlongswap=-1;++__enable_user_access();+asmgoto(".option push\n"+".option arch, +zicfiss\n"+"1: ssamoswap.d %[swap], %[val], %[addr]\n"+_ASM_EXTABLE(1b,%l[fault])+".option pop\n"+:[swap]"=r"(swap),[addr]"+A"(*addr)+:[val]"r"(val)+:"memory"+:fault+);+__disable_user_access();+returnswap;+fault:+__disable_user_access();+return-1;+}++/*+*Createarestoretokenontheshadowstack.AtokenisalwaysXLENwide+*andalignedtoXLEN.+*/+staticintcreate_rstor_token(unsignedlongssp,unsignedlong*token_addr)+{+unsignedlongaddr;++/* Token must be aligned */+if(!IS_ALIGNED(ssp,SHSTK_ENTRY_SIZE))+return-EINVAL;++/* On RISC-V we're constructing token to be function of address itself */+addr=ssp-SHSTK_ENTRY_SIZE;++if(amo_user_shstk((unsignedlong__user*)addr,(unsignedlong)ssp)==-1)+return-EFAULT;++if(token_addr)+*token_addr=addr;++return0;+}++staticunsignedlongallocate_shadow_stack(unsignedlongaddr,unsignedlongsize,+unsignedlongtoken_offset,boolset_tok)+{+intflags=MAP_ANONYMOUS|MAP_PRIVATE;+structmm_struct*mm=current->mm;+unsignedlongpopulate,tok_loc=0;++if(addr)+flags|=MAP_FIXED_NOREPLACE;++mmap_write_lock(mm);+addr=do_mmap(NULL,addr,size,PROT_READ,flags,+VM_SHADOW_STACK|VM_WRITE,0,&populate,NULL);+mmap_write_unlock(mm);++if(!set_tok||IS_ERR_VALUE(addr))+gotoout;++if(create_rstor_token(addr+token_offset,&tok_loc)){+vm_munmap(addr,size);+return-EINVAL;+}++addr=tok_loc;++out:+returnaddr;+}++SYSCALL_DEFINE3(map_shadow_stack,unsignedlong,addr,unsignedlong,size,unsignedint,flags)+{+boolset_tok=flags&SHADOW_STACK_SET_TOKEN;+unsignedlongaligned_size=0;++if(!cpu_supports_shadow_stack())+return-EOPNOTSUPP;++/* Anything other than set token should result in invalid param */+if(flags&~SHADOW_STACK_SET_TOKEN)+return-EINVAL;++/*+*Unlikeotherarchitectures,onRISC-V,SSPpointerisheldinCSR_SSPandisavailable+*CSRinallmodes.CSRaccessesareperformedusing12bitindexprogrammedininstruction+*itself.ThisprovidesstaticpropertyonregisterprogrammingandwritestoCSRcan't+*beunintentionalfromprogrammer'sperspective.Aslongasprogrammerhasguardedareas+*whichperformwritestoCSR_SSPproperly,shadowstackpivotingisnotpossible.Since+*CSR_SSPiswriteablebyusermode,ititselfcansetupashadowstacktokensubsequent+*toallocation.Althoughinordertoprovideportablitywithotherarchitecture(because+*`map_shadow_stack`isarchagnosticsyscall),RISC-Vwillfollowexpectationofatoken+*flaginflagsandifprovidedinflags,setupatokenatthebase.+*/++/* If there isn't space for a token */+if(set_tok&&size<SHSTK_ENTRY_SIZE)+return-ENOSPC;++if(addr&&(addr&(PAGE_SIZE-1)))+return-EINVAL;++aligned_size=PAGE_ALIGN(size);+if(aligned_size<size)+return-EOVERFLOW;++returnallocate_shadow_stack(addr,aligned_size,size,set_tok);+}
@@ -16,6 +16,8 @@ struct kernel_clone_args;structcfi_state{unsignedlongubcfi_en:1;/* Enable for backward cfi. */unsignedlongubcfi_locked:1;+unsignedlongufcfi_en:1;/* Enable for forward cfi. Note that ELP goes in sstatus */+unsignedlongufcfi_locked:1;unsignedlonguser_shdw_stk;/* Current user shadow stack pointer */unsignedlongshdw_stk_base;/* Base address of shadow stack */unsignedlongshdw_stk_size;/* size of shadow stack */
@@ -370,3 +399,53 @@ int arch_lock_shadow_stack_status(struct task_struct *task,return0;}++intarch_get_indir_br_lp_status(structtask_struct*t,unsignedlong__user*status)+{+unsignedlongfcfi_status=0;++if(!cpu_supports_indirect_br_lp_instr())+return-EINVAL;++/* indirect branch tracking is enabled on the task or not */+fcfi_status|=(is_indir_lp_enabled(t)?PR_INDIR_BR_LP_ENABLE:0);++returncopy_to_user(status,&fcfi_status,sizeof(fcfi_status))?-EFAULT:0;+}++intarch_set_indir_br_lp_status(structtask_struct*t,unsignedlongstatus)+{+boolenable_indir_lp=false;++if(!cpu_supports_indirect_br_lp_instr())+return-EINVAL;++/* indirect branch tracking is locked and further can't be modified by user */+if(is_indir_lp_locked(t))+return-EINVAL;++/* Reject unknown flags */+if(status&~PR_INDIR_BR_LP_ENABLE)+return-EINVAL;++enable_indir_lp=(status&PR_INDIR_BR_LP_ENABLE);+set_indir_lp_status(t,enable_indir_lp);++return0;+}++intarch_lock_indir_br_lp_status(structtask_struct*task,+unsignedlongarg)+{+/*+*Ifindirectbranchtrackingisnotsupportedornotenabledontask,+*nothingtolockhere+*/+if(!cpu_supports_indirect_br_lp_instr()||+!is_indir_lp_enabled(task)||arg!=0)+return-EINVAL;++set_indir_lp_lock(task);++return0;+}
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
Userspace specifies CLONE_VM to share address space and spawn new thread.
`clone` allow userspace to specify a new stack for new thread. However
there is no way to specify new shadow stack base address without changing
API. This patch allocates a new shadow stack whenever CLONE_VM is given.
In case of CLONE_VFORK, parent is suspended until child finishes and thus
can child use parent shadow stack. In case of !CLONE_VM, COW kicks in
because entire address space is copied from parent to child.
`clone3` is extensible and can provide mechanisms using which shadow stack
as an input parameter can be provided. This is not settled yet and being
extensively discussed on mailing list. Once that's settled, this commit
will adapt to that.
Reviewed-by: Zong Li <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/mmu_context.h | 7 ++
arch/riscv/include/asm/usercfi.h | 25 ++++++++
arch/riscv/kernel/process.c | 10 +++
arch/riscv/kernel/usercfi.c | 120 +++++++++++++++++++++++++++++++++++
4 files changed, 162 insertions(+)
@@ -226,6 +227,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)u64clone_flags=args->flags;unsignedlongusp=args->stack;unsignedlongtls=args->tls;+unsignedlongssp=0;structpt_regs*childregs=task_pt_regs(p);/* Ensure all threads in this mm have the same pointer masking mode. */
@@ -245,11 +247,19 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)p->thread.s[1]=(unsignedlong)args->fn_arg;p->thread.ra=(unsignedlong)ret_from_fork_kernel_asm;}else{+/* allocate new shadow stack if needed. In case of CLONE_VM we have to */+ssp=shstk_alloc_thread_stack(p,args);+if(IS_ERR_VALUE(ssp))+returnPTR_ERR((void*)ssp);+*childregs=*(current_pt_regs());/* Turn off status.VS */riscv_v_vstate_off(childregs);if(usp)/* User fork */childregs->sp=usp;+/* if needed, set new ssp */+if(ssp)+set_active_shstk(p,ssp);if(clone_flags&CLONE_SETTLS)childregs->tp=tls;childregs->a0=0;/* Return value of fork() */
@@ -140,3 +175,88 @@ SYSCALL_DEFINE3(map_shadow_stack, unsigned long, addr, unsigned long, size, unsireturnallocate_shadow_stack(addr,aligned_size,size,set_tok);}++/*+*Thisgetscalledduringclone/clone3/fork.Andisneededtoallocateashadowstackfor+*caseswhereCLONE_VMisspecifiedandthusadifferentstackisspecifiedbyuser.We+*thusneedaseparateshadowstacktoo.Howdoesseparateshadowstackisspecifiedby+*userisstillbeingdebated.Oncethat'ssettled,removethispartofthecomment.+*Thisfunctionsimplyreturns0ifshadowstackarenotsupportedorifseparateshadow+*stackallocationisnotneeded(likeincaseof!CLONE_VM)+*/+unsignedlongshstk_alloc_thread_stack(structtask_struct*tsk,+conststructkernel_clone_args*args)+{+unsignedlongaddr,size;++/* If shadow stack is not supported, return 0 */+if(!cpu_supports_shadow_stack())+return0;++/*+*Ifshadowstackisnotenabledonthenewthread,skipany+*switchtoanewshadowstack.+*/+if(!is_shstk_enabled(tsk))+return0;++/*+*ForCLONE_VFORKthechildwillsharetheparentsshadowstack.+*Setbase=0andsize=0,thisisspecialmeanstotrackthisstate+*sothefreeinglogicrunforchildknowstoleaveitalone.+*/+if(args->flags&CLONE_VFORK){+set_shstk_base(tsk,0,0);+return0;+}++/*+*For!CLONE_VMthechildwilluseacopyoftheparentsshadow+*stack.+*/+if(!(args->flags&CLONE_VM))+return0;++/*+*reachingheremeans,CLONE_VMwasspecifiedandthusaseparateshadow+*stackisneededfornewclonedthread.Note:belowallocationishappening+*usingcurrentmm.+*/+size=calc_shstk_size(args->stack_size);+addr=allocate_shadow_stack(0,size,0,false);+if(IS_ERR_VALUE(addr))+returnaddr;++set_shstk_base(tsk,addr,size);++returnaddr+size;+}++voidshstk_release(structtask_struct*tsk)+{+unsignedlongbase=0,size=0;+/* If shadow stack is not supported or not enabled, nothing to release */+if(!cpu_supports_shadow_stack()||!is_shstk_enabled(tsk))+return;++/*+*Whenfork()withCLONE_VMfails,thechild(tsk)alreadyhasa+*shadowstackallocated,andexit_thread()callsthisfunctionto+*freeit.Inthiscasetheparent(current)andthechildshare+*thesamemmstruct.Moveforwardonlywhenthey'resame.+*/+if(!tsk->mm||tsk->mm!=current->mm)+return;++/*+*WeknowshadowstackisenabledbutifbaseisNULL,then+*thistaskisnotmanagingitsownshadowstack(CLONE_VFORK).So+*skipfreeingit.+*/+base=get_shstk_base(tsk,&size);+if(!base)+return;++vm_munmap(base,size);+set_shstk_base(tsk,0,0);+}
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
Three architectures (x86, aarch64, riscv) have support for indirect branch
tracking feature in a very similar fashion. On a very high level, indirect
branch tracking is a CPU feature where CPU tracks branches which uses
memory operand to perform control transfer in program. As part of this
tracking on indirect branches, CPU goes in a state where it expects a
landing pad instr on target and if not found then CPU raises some fault
(architecture dependent)
x86 landing pad instr - `ENDBRANCH`
arch64 landing pad instr - `BTI`
riscv landing instr - `lpad`
Given that three major arches have support for indirect branch tracking,
This patch makes `prctl` for indirect branch tracking arch agnostic.
To allow userspace to enable this feature for itself, following prtcls are
defined:
- PR_GET_INDIR_BR_LP_STATUS: Gets current configured status for indirect
branch tracking.
- PR_SET_INDIR_BR_LP_STATUS: Sets a configuration for indirect branch
tracking.
Following status options are allowed
- PR_INDIR_BR_LP_ENABLE: Enables indirect branch tracking on user
thread.
- PR_INDIR_BR_LP_DISABLE; Disables indirect branch tracking on user
thread.
- PR_LOCK_INDIR_BR_LP_STATUS: Locks configured status for indirect branch
tracking for user thread.
Reviewed-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
include/linux/cpu.h | 4 ++++
include/uapi/linux/prctl.h | 27 +++++++++++++++++++++++++++
kernel/sys.c | 30 ++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
zicfiss / zicfilp introduces a new exception to priv isa `software check
exception` with cause code = 18. This patch implements software check
exception.
Additionally it implements a cfi violation handler which checks for code
in xtval. If xtval=2, it means that sw check exception happened because of
an indirect branch not landing on 4 byte aligned PC or not landing on
`lpad` instruction or label value embedded in `lpad` not matching label
value setup in `x7`. If xtval=3, it means that sw check exception happened
because of mismatch between link register (x1 or x5) and top of shadow
stack (on execution of `sspopchk`).
In case of cfi violation, SIGSEGV is raised with code=SEGV_CPERR.
SEGV_CPERR was introduced by x86 shadow stack patches.
To keep uprobes working, handle the uprobe event first before reporting
the CFI violation in software-check exception handler. Because when the
landing pad is activated, if the uprobe point is set at the lpad
instruction at the beginning of a function, the system triggers a software
-check exception instead of an ebreak exception due to the exception
priority, then uprobe can't work successfully.
Co-developed-by: Zong Li <redacted>
Reviewed-by: Zong Li <redacted>
Signed-off-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/asm-prototypes.h | 1 +
arch/riscv/include/asm/entry-common.h | 2 ++
arch/riscv/kernel/entry.S | 3 ++
arch/riscv/kernel/traps.c | 54 +++++++++++++++++++++++++++++++++
4 files changed, 60 insertions(+)
@@ -366,6 +366,60 @@ void do_trap_ecall_u(struct pt_regs *regs)}+#define CFI_TVAL_FCFI_CODE 2+#define CFI_TVAL_BCFI_CODE 3+/* handle cfi violations */+boolhandle_user_cfi_violation(structpt_regs*regs)+{+unsignedlongtval=csr_read(CSR_TVAL);+boolis_fcfi=(tval==CFI_TVAL_FCFI_CODE&&cpu_supports_indirect_br_lp_instr());+boolis_bcfi=(tval==CFI_TVAL_BCFI_CODE&&cpu_supports_shadow_stack());++/*+*Handleuprobeeventfirst.Theprobepointcanbeavalidtarget+*ofindirectjumpsorcalls,inthiscase,forwardcfiviolation+*willbetriggeredinsteadofbreakpointexception.ClearELPflag+*onsstatusimageaswelltoavoidrecurringfault.+*/+if(is_fcfi&&probe_breakpoint_handler(regs)){+regs->status&=~SR_ELP;+returntrue;+}++if(is_fcfi||is_bcfi){+do_trap_error(regs,SIGSEGV,SEGV_CPERR,regs->epc,+"Oops - control flow violation");+returntrue;+}++returnfalse;+}++/*+*softwarecheckexceptionisdefinedwithrisc-vcfispec.Softwarecheck+*exceptionisraisedwhen:-+*a)Anindirectbranchdoesn'tlandon4bytealignedPCor`lpad`+*instructionor`label`valueprogrammedin`lpad`instrdoesn't+*matchwithvaluesetupin`x7`.reportedcodein`xtval`is2.+*b)`sspopchk`instructionfindsamismatchbetweentopofshadowstack(ssp)+*andx1/x5.reportedcodein`xtval`is3.+*/+asmlinkage__visible__trap_sectionvoiddo_trap_software_check(structpt_regs*regs)+{+if(user_mode(regs)){+irqentry_enter_from_user_mode(regs);++/* not a cfi violation, then merge into flow of unknown trap handler */+if(!handle_user_cfi_violation(regs))+do_trap_unknown(regs);++irqentry_exit_to_user_mode(regs);+}else{+/* sw check exception coming from kernel is a bug in kernel */+die(regs,"Kernel BUG");+}+}+#ifdef CONFIG_MMUasmlinkage__visiblenoinstrvoiddo_page_fault(structpt_regs*regs){
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Andy Chiu <redacted>
The function save_v_state() served two purposes. First, it saved
extension context into the signal stack. Then, it constructed the
extension header if there was no fault. The second part is independent
of the extension itself. As a result, we can pull that part out, so
future extensions may reuse it. This patch adds arch_ext_list and makes
setup_sigcontext() go through all possible extensions' save() callback.
The callback returns a positive value indicating the size of the
successfully saved extension. Then the kernel proceeds to construct the
header for that extension. The kernel skips an extension if it does
not exist, or if the saving fails for some reasons. The error code is
propagated out on the later case.
This patch does not introduce any functional changes.
Signed-off-by: Andy Chiu <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
---
arch/riscv/include/asm/vector.h | 3 ++
arch/riscv/kernel/signal.c | 62 +++++++++++++++++++++++++++--------------
2 files changed, 44 insertions(+), 21 deletions(-)
@@ -423,6 +423,9 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }#define riscv_v_thread_free(tsk) do {} while (0)#define riscv_v_setup_ctx_cache() do {} while (0)#define riscv_v_thread_alloc(tsk) do {} while (0)+#define get_cpu_vector_context() do {} while (0)+#define put_cpu_vector_context() do {} while (0)+#define riscv_v_vstate_set_restore(task, regs) do {} while (0)#endif /* CONFIG_RISCV_ISA_V */
@@ -68,18 +68,19 @@ static long save_fp_state(struct pt_regs *regs,#define restore_fp_state(task, regs) (0)#endif-#ifdef CONFIG_RISCV_ISA_V--staticlongsave_v_state(structpt_regs*regs,void__user**sc_vec)+staticlongsave_v_state(structpt_regs*regs,void__user*sc_vec){-struct__riscv_ctx_hdr__user*hdr;struct__sc_riscv_v_state__user*state;void__user*datap;longerr;-hdr=*sc_vec;-/* Place state to the user's signal context space after the hdr */-state=(struct__sc_riscv_v_state__user*)(hdr+1);+if(!IS_ENABLED(CONFIG_RISCV_ISA_V)||+!((has_vector()||has_xtheadvector())&&+riscv_v_vstate_query(regs)))+return0;++/* Place state to the user's signal context spac */+state=(struct__sc_riscv_v_state__user*)sc_vec;/* Point datap right after the end of __sc_riscv_v_state */datap=state+1;
@@ -97,15 +98,11 @@ static long save_v_state(struct pt_regs *regs, void __user **sc_vec)err|=__put_user((__forcevoid*)datap,&state->v_state.datap);/* Copy the whole vector content to user space datap. */err|=__copy_to_user(datap,current->thread.vstate.datap,riscv_v_vsize);-/* Copy magic to the user space after saving all vector conetext */-err|=__put_user(RISCV_V_MAGIC,&hdr->magic);-err|=__put_user(riscv_v_sc_size,&hdr->size);if(unlikely(err))-returnerr;+return-EFAULT;-/* Only progress the sv_vec if everything has done successfully */-*sc_vec+=riscv_v_sc_size;-return0;+/* Only return the size if everything has done successfully */+returnriscv_v_sc_size;}/*
@@ -270,7 +277,8 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,{structsigcontext__user*sc=&frame->uc.uc_mcontext;struct__riscv_ctx_hdr__user*sc_ext_ptr=&sc->sc_extdesc.hdr;-longerr;+structarch_ext_priv*arch_ext;+longerr,i,ext_size;/* sc_regs is structured the same as the start of pt_regs */err=__copy_to_user(&sc->sc_regs,regs,sizeof(sc->sc_regs));
@@ -278,8 +286,20 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,if(has_fpu())err|=save_fp_state(regs,&sc->sc_fpregs);/* Save the vector state. */-if((has_vector()||has_xtheadvector())&&riscv_v_vstate_query(regs))-err|=save_v_state(regs,(void__user**)&sc_ext_ptr);+for(i=0;i<nr_arch_exts;i++){+arch_ext=&arch_ext_list[i];+if(!arch_ext->save)+continue;++ext_size=arch_ext->save(regs,sc_ext_ptr+1);+if(ext_size<=0){+err|=ext_size;+}else{+err|=__put_user(arch_ext->magic,&sc_ext_ptr->magic);+err|=__put_user(ext_size,&sc_ext_ptr->size);+sc_ext_ptr=(void*)sc_ext_ptr+ext_size;+}+}/* Write zero to fp-reserved space and check it on restore_sigcontext */err|=__put_user(0,&sc->sc_extdesc.reserved);/* And put END __riscv_ctx_hdr at the end. */
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
Expose a new register type NT_RISCV_USER_CFI for risc-v cfi status and
state. Intentionally both landing pad and shadow stack status and state
are rolled into cfi state. Creating two different NT_RISCV_USER_XXX would
not be useful and wastage of a note type. Enabling, disabling and locking
of feature is not allowed via ptrace set interface. However setting `elp`
state or setting shadow stack pointer are allowed via ptrace set interface
. It is expected `gdb` might have use to fixup `elp` state or `shadow
stack` pointer.
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/uapi/asm/ptrace.h | 30 ++++++++++++
arch/riscv/kernel/ptrace.c | 95 ++++++++++++++++++++++++++++++++++++
include/uapi/linux/elf.h | 2 +
3 files changed, 127 insertions(+)
@@ -184,6 +188,87 @@ static int tagged_addr_ctrl_set(struct task_struct *target,}#endif+#ifdef CONFIG_RISCV_USER_CFI+staticintriscv_cfi_get(structtask_struct*target,+conststructuser_regset*regset,+structmembufto)+{+structuser_cfi_stateuser_cfi;+structpt_regs*regs;++memset(&user_cfi,0,sizeof(user_cfi));+regs=task_pt_regs(target);++if(is_indir_lp_enabled(target)){+user_cfi.cfi_status.cfi_state|=PTRACE_CFI_LP_EN_STATE;+user_cfi.cfi_status.cfi_state|=is_indir_lp_locked(target)?+PTRACE_CFI_LP_LOCK_STATE:0;+user_cfi.cfi_status.cfi_state|=(regs->status&SR_ELP)?+PTRACE_CFI_ELP_STATE:0;+}++if(is_shstk_enabled(target)){+user_cfi.cfi_status.cfi_state|=(PTRACE_CFI_SS_EN_STATE|+PTRACE_CFI_SS_PTR_STATE);+user_cfi.cfi_status.cfi_state|=is_shstk_locked(target)?+PTRACE_CFI_SS_LOCK_STATE:0;+user_cfi.shstk_ptr=get_active_shstk(target);+}++returnmembuf_write(&to,&user_cfi,sizeof(user_cfi));+}++/*+*Doesitmakesensetoallowingenable/disableofcfiviaptrace?+*Notallowingenable/disable/lockingcontrolviaptracefornow.+*Settingshadowstackpointerisallowed.GDBmightuseittounwindor+*someotherfixup.Similarlygdbmightwanttosuppresselpandmaywant+*toresetelpstate.+*/+staticintriscv_cfi_set(structtask_struct*target,+conststructuser_regset*regset,+unsignedintpos,unsignedintcount,+constvoid*kbuf,constvoid__user*ubuf)+{+intret;+structuser_cfi_stateuser_cfi;+structpt_regs*regs;++regs=task_pt_regs(target);++ret=user_regset_copyin(&pos,&count,&kbuf,&ubuf,&user_cfi,0,-1);+if(ret)+returnret;++/*+*Notallowingenablingorlockingshadowstackorlandingpad+*Thereisnodisablingofshadowstackorlandingpadviaptrace+*rsvdfieldshouldbesettozerosothatifthosefieldsareneededinfuture+*/+if((user_cfi.cfi_status.cfi_state&+(PTRACE_CFI_LP_EN_STATE|PTRACE_CFI_LP_LOCK_STATE|+PTRACE_CFI_SS_EN_STATE|PTRACE_CFI_SS_LOCK_STATE))||+(user_cfi.cfi_status.cfi_state&PRACE_CFI_STATE_INVALID_MASK))+return-EINVAL;++/* If lpad is enabled on target and ptrace requests to set / clear elp, do that */+if(is_indir_lp_enabled(target)){+if(user_cfi.cfi_status.cfi_state&+PTRACE_CFI_ELP_STATE)/* set elp state */+regs->status|=SR_ELP;+else+regs->status&=~SR_ELP;/* clear elp state */+}++/* If shadow stack enabled on target, set new shadow stack pointer */+if(is_shstk_enabled(target)&&+(user_cfi.cfi_status.cfi_state&PTRACE_CFI_SS_PTR_STATE))+set_active_shstk(target,user_cfi.shstk_ptr);++return0;+}+#endif+staticconststructuser_regsetriscv_user_regset[]={[REGSET_X]={USER_REGSET_NOTE_TYPE(PRSTATUS),
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:51
From: Deepak Gupta <redacted>
Save shadow stack pointer in sigcontext structure while delivering signal.
Restore shadow stack pointer from sigcontext on sigreturn.
As part of save operation, kernel uses `ssamoswap` to save snapshot of
current shadow stack on shadow stack itself (can be called as a save
token). During restore on sigreturn, kernel retrieves token from top of
shadow stack and validates it. This allows that user mode can't arbitrary
pivot to any shadow stack address without having a token and thus provide
strong security assurance between signaly delivery and sigreturn window.
Use ABI compatible way of saving/restoring shadow stack pointer into
signal stack. This follows what Vector extension, where extra registers
are placed in a form of extension header + extension body in the stack.
The extension header indicates the size of the extra architectural
states plus the size of header itself, and a magic identifier of the
extension. Then, the extensions body contains the new architectural
states in the form defined by uapi.
Signed-off-by: Andy Chiu <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/include/asm/usercfi.h | 10 ++++
arch/riscv/include/uapi/asm/ptrace.h | 4 ++
arch/riscv/include/uapi/asm/sigcontext.h | 1 +
arch/riscv/kernel/signal.c | 86 ++++++++++++++++++++++++++++++++
arch/riscv/kernel/usercfi.c | 57 +++++++++++++++++++++
5 files changed, 158 insertions(+)
@@ -10,6 +10,7 @@/* The Magic number for signal context frame header. */#define RISCV_V_MAGIC 0x53465457+#define RISCV_ZICFISS_MAGIC 0x9487#define END_MAGIC 0x0/* The size of END signal context header. */
@@ -359,6 +437,11 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,#ifdef CONFIG_MMUregs->ra=(unsignedlong)VDSO_SYMBOL(current->mm->context.vdso,rt_sigreturn);++/* if bcfi is enabled x1 (ra) and x5 (t0) must match. not sure if we need this? */+if(is_shstk_enabled(current))+regs->t0=regs->ra;+#else/**Forthenommucasewedon'thaveaVDSO.Insteadwepushtwo
@@ -168,6 +173,58 @@ static int create_rstor_token(unsigned long ssp, unsigned long *token_addr)return0;}+/*+*Saveusershadowstackpointeronshadowstackitselfandreturnpointertosavedlocation+*returns-EFAULTifoperationwasunsuccessful+*/+intsave_user_shstk(structtask_struct*tsk,unsignedlong*saved_shstk_ptr)+{+unsignedlongss_ptr=0;+unsignedlongtoken_loc=0;+intret=0;++if(!saved_shstk_ptr)+return-EINVAL;++ss_ptr=get_active_shstk(tsk);+ret=create_rstor_token(ss_ptr,&token_loc);++if(!ret){+*saved_shstk_ptr=token_loc;+set_active_shstk(tsk,token_loc);+}++returnret;+}++/*+*Restoresusershadowstackpointerfromtokenonshadowstackfortask`tsk`+*returns-EFAULTifoperationwasunsuccessful+*/+intrestore_user_shstk(structtask_struct*tsk,unsignedlongshstk_ptr)+{+unsignedlongtoken=0;++token=amo_user_shstk((unsignedlong__user*)shstk_ptr,0);++if(token==-1)+return-EFAULT;++/* invalid token, return EINVAL */+if((token-shstk_ptr)!=SHSTK_ENTRY_SIZE){+pr_info_ratelimited("%s[%d]: bad restore token in %s: pc=%p sp=%p, token=%p, "+"shstk_ptr=%p\n",tsk->comm,task_pid_nr(tsk),__func__,+(void*)(task_pt_regs(tsk)->epc),+(void*)(task_pt_regs(tsk)->sp),+(void*)token,(void*)shstk_ptr);+return-EINVAL;+}++/* all checks passed, set active shstk and return success */+set_active_shstk(tsk,token);+return0;+}+staticunsignedlongallocate_shadow_stack(unsignedlongaddr,unsignedlongsize,unsignedlongtoken_offset,boolset_tok){
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
This commit adds a kernel command line option to disable part or all of
user cfi. User backward cfi and forward cfi can be controlled
independently. Kernel command line parameter "riscv_nousercfi" can take
the following values:
- "all" : Disable forward and backward cfi both.
- "bcfi" : Disable backward cfi.
- "fcfi" : Disable forward cfi
Signed-off-by: Paul Walmsley <pjw@kernel.org>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
Documentation/admin-guide/kernel-parameters.txt | 8 ++++
arch/riscv/include/asm/usercfi.h | 9 ++++
arch/riscv/kernel/cpufeature.c | 7 ++-
arch/riscv/kernel/usercfi.c | 59 ++++++++++++++++++++-----
4 files changed, 70 insertions(+), 13 deletions(-)
@@ -6453,6 +6453,14 @@ replacement properties are not found. See the Kconfig entry for RISCV_ISA_FALLBACK.+ riscv_nousercfi=+ all Disable user cfi ABI to userspace even if cpu extensions+ are available.+ bcfi Disable user backward cfi ABI to userspace even if+ shadow stack extension is available.+ fcfi Disable user forward cfi ABI to userspace even if landing+ pad extension is available.+ ro [KNL] Mount root device read-only on boot rodata= [KNL,EARLY]
@@ -259,7 +261,7 @@ SYSCALL_DEFINE3(map_shadow_stack, unsigned long, addr, unsigned long, size, unsiboolset_tok=flags&SHADOW_STACK_SET_TOKEN;unsignedlongaligned_size=0;-if(!cpu_supports_shadow_stack())+if(!is_user_shstk_enabled())return-EOPNOTSUPP;/* Anything other than set token should result in invalid param */
@@ -306,7 +308,7 @@ unsigned long shstk_alloc_thread_stack(struct task_struct *tsk,unsignedlongaddr,size;/* If shadow stack is not supported, return 0 */-if(!cpu_supports_shadow_stack())+if(!is_user_shstk_enabled())return0;/*
@@ -352,7 +354,7 @@ void shstk_release(struct task_struct *tsk){unsignedlongbase=0,size=0;/* If shadow stack is not supported or not enabled, nothing to release */-if(!cpu_supports_shadow_stack()||!is_shstk_enabled(tsk))+if(!is_user_shstk_enabled()||!is_shstk_enabled(tsk))return;/*
@@ -381,7 +383,7 @@ int arch_get_shadow_stack_status(struct task_struct *t, unsigned long __user *st{unsignedlongbcfi_status=0;-if(!cpu_supports_shadow_stack())+if(!is_user_shstk_enabled())return-EINVAL;/* this means shadow stack is enabled on the task */
@@ -395,7 +397,7 @@ int arch_set_shadow_stack_status(struct task_struct *t, unsigned long status)unsignedlongsize=0,addr=0;boolenable_shstk=false;-if(!cpu_supports_shadow_stack())+if(!is_user_shstk_enabled())return-EINVAL;/* Reject unknown flags */
@@ -448,7 +450,7 @@ int arch_lock_shadow_stack_status(struct task_struct *task,unsignedlongarg){/* If shtstk not supported or not enabled on task, nothing to lock here */-if(!cpu_supports_shadow_stack()||+if(!is_user_shstk_enabled()||!is_shstk_enabled(task)||arg!=0)return-EINVAL;
@@ -461,7 +463,7 @@ int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *sta{unsignedlongfcfi_status=0;-if(!cpu_supports_indirect_br_lp_instr())+if(!is_user_lpad_enabled())return-EINVAL;/* indirect branch tracking is enabled on the task or not */
@@ -474,7 +476,7 @@ int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status){boolenable_indir_lp=false;-if(!cpu_supports_indirect_br_lp_instr())+if(!is_user_lpad_enabled())return-EINVAL;/* indirect branch tracking is locked and further can't be modified by user */
@@ -498,7 +500,7 @@ int arch_lock_indir_br_lp_status(struct task_struct *task,*Ifindirectbranchtrackingisnotsupportedornotenabledontask,*nothingtolockhere*/-if(!cpu_supports_indirect_br_lp_instr()||+if(!is_user_lpad_enabled()||!is_indir_lp_enabled(task)||arg!=0)return-EINVAL;
@@ -506,3 +508,38 @@ int arch_lock_indir_br_lp_status(struct task_struct *task,return0;}++boolis_user_shstk_enabled(void)+{+return(cpu_supports_shadow_stack()&&+!(riscv_nousercfi&CMDLINE_DISABLE_RISCV_USERCFI_BCFI));+}++boolis_user_lpad_enabled(void)+{+return(cpu_supports_indirect_br_lp_instr()&&+!(riscv_nousercfi&CMDLINE_DISABLE_RISCV_USERCFI_FCFI));+}++staticint__initsetup_global_riscv_enable(char*str)+{+if(strcmp(str,"all")==0)+riscv_nousercfi=CMDLINE_DISABLE_RISCV_USERCFI;++if(strcmp(str,"fcfi")==0)+riscv_nousercfi|=CMDLINE_DISABLE_RISCV_USERCFI_FCFI;++if(strcmp(str,"bcfi")==0)+riscv_nousercfi|=CMDLINE_DISABLE_RISCV_USERCFI_BCFI;++if(riscv_nousercfi)+pr_info("riscv user cfi disabled via cmdline "+"shadow stack status : %s, landing pad status : %s\n",+(riscv_nousercfi&CMDLINE_DISABLE_RISCV_USERCFI_BCFI)?"disabled":+"enabled",(riscv_nousercfi&CMDLINE_DISABLE_RISCV_USERCFI_FCFI)?+"disabled":"enabled");++return1;+}++__setup("riscv_nousercfi=",setup_global_riscv_enable);
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
Kernel will have to perform shadow stack operations on user shadow stack.
Like during signal delivery and sigreturn, shadow stack token must be
created and validated respectively. Thus shadow stack access for kernel
must be enabled.
In future when kernel shadow stacks are enabled for linux kernel, it must
be enabled as early as possible for better coverage and prevent imbalance
between regular stack and shadow stack. After `relocate_enable_mmu` has
been done, this is as early as possible it can enabled.
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/kernel/asm-offsets.c | 6 ++++++
arch/riscv/kernel/head.S | 27 +++++++++++++++++++++++++++
2 files changed, 33 insertions(+)
@@ -0,0 +1,115 @@+.. SPDX-License-Identifier: GPL-2.0++:Author: Deepak Gupta <debug@rivosinc.com>+:Date: 12 January 2024++====================================================+Tracking indirect control transfers on RISC-V Linux+====================================================++This document briefly describes the interface provided to userspace by Linux+to enable indirect branch tracking for user mode applications on RISC-V++1. Feature Overview+--------------------++Memory corruption issues usually result into crashes, however when in hands of+an adversary and if used creatively can result into a variety security issues.++One of those security issues can be code re-use attacks on program where adversary+can use corrupt function pointers and chain them together to perform jump oriented+programming (JOP) or call oriented programming (COP) and thus compromising control+flow integrity (CFI) of the program.++Function pointers live in read-write memory and thus are susceptible to corruption+and allows an adversary to reach any program counter (PC) in address space. On+RISC-V zicfilp extension enforces a restriction on such indirect control+transfers:++- indirect control transfers must land on a landing pad instruction ``lpad``.+ There are two exception to this rule:++- rs1 = x1 or rs1 = x5, i.e. a return from a function and returns are+ protected using shadow stack (see zicfiss.rst)++- rs1 = x7. On RISC-V compiler usually does below to reach function+ which is beyond the offset possible J-type instruction::++ auipc x7, <imm>+ jalr (x7)++ Such form of indirect control transfer are still immutable and don't rely+ on memory and thus rs1=x7 is exempted from tracking and considered software+ guarded jumps.++``lpad`` instruction is pseudo of ``auipc rd, <imm_20bit>`` with ``rd=x0`` and+is a HINT nop. ``lpad`` instruction must be aligned on 4 byte boundary and+compares 20 bit immediate with x7. If ``imm_20bit`` == 0, CPU doesn't perform+any comparision with ``x7``. If ``imm_20bit`` != 0, then ``imm_20bit`` must+match ``x7`` else CPU will raise ``software check exception`` (``cause=18``)+with ``*tval = 2``.++Compiler can generate a hash over function signatures and setup them (truncated+to 20bit) in x7 at callsites and function prologues can have ``lpad`` with same+function hash. This further reduces number of program counters a call site can+reach.++2. ELF and psABI+-----------------++Toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_FCFI` for property+:c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in notes section of the object file.++3. Linux enabling+------------------++User space programs can have multiple shared objects loaded in its address space+and it's a difficult task to make sure all the dependencies have been compiled+with support of indirect branch. Thus it's left to dynamic loader to enable+indirect branch tracking for the program.++4. prctl() enabling+--------------------++:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` /+:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect+branch tracking. prctls are arch agnostic and returns -EINVAL on other arches.++* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg)++If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports ``zicfilp``+then kernel will enable indirect branch tracking for the task. Dynamic loader+can issue this :c:macro:`prctl` once it has determined that all the objects+loaded in address space support indirect branch tracking. Additionally if there+is a `dlopen` to an object which wasn't compiled with ``zicfilp``, dynamic+loader can issue this prctl with arg1 set to 0 (i.e.+:c:macro:`PR_INDIR_BR_LP_ENABLE` being clear)++* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg)++Returns current status of indirect branch tracking. If enabled it'll return+:c:macro:`PR_INDIR_BR_LP_ENABLE`++* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)++Locks current status of indirect branch tracking on the task. User space may+want to run with strict security posture and wouldn't want loading of objects+without ``zicfilp`` support in it and thus would want to disallow disabling of+indirect branch tracking. In that case user space can use this prctl to lock+current settings.++5. violations related to indirect branch tracking+--------------------------------------------------++Pertaining to indirect branch tracking, CPU raises software check exception in+following conditions:++- missing ``lpad`` after indirect call / jmp+-``lpad`` not on 4 byte boundary+-``imm_20bit`` embedded in ``lpad`` instruction doesn't match with ``x7``++In all 3 cases, ``*tval = 2`` is captured and software check exception is+raised (``cause=18``)++Linux kernel will treat this as :c:macro:`SIGSEGV` with code =+:c:macro:`SEGV_CPERR` and follow normal course of signal delivery.
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
This patch creates a config for shadow stack support and landing pad instr
support. Shadow stack support and landing instr support can be enabled by
selecting `CONFIG_RISCV_USER_CFI`. Selecting `CONFIG_RISCV_USER_CFI` wires
up path to enumerate CPU support and if cpu support exists, kernel will
support cpu assisted user mode cfi.
If CONFIG_RISCV_USER_CFI is selected, select `ARCH_USES_HIGH_VMA_FLAGS`,
`ARCH_HAS_USER_SHADOW_STACK` and DYNAMIC_SIGFRAME for riscv.
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/Kconfig | 22 ++++++++++++++++++++++
arch/riscv/configs/hardening.config | 4 ++++
2 files changed, 26 insertions(+)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
Adding documentation on shadow stack for user mode on riscv and kernel
interfaces exposed so that user tasks can enable it.
Reviewed-by: Zong Li <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
Documentation/arch/riscv/index.rst | 1 +
Documentation/arch/riscv/zicfiss.rst | 179 +++++++++++++++++++++++++++++++++++
2 files changed, 180 insertions(+)
@@ -0,0 +1,179 @@+.. SPDX-License-Identifier: GPL-2.0++:Author: Deepak Gupta <debug@rivosinc.com>+:Date: 12 January 2024++=========================================================+Shadow stack to protect function returns on RISC-V Linux+=========================================================++This document briefly describes the interface provided to userspace by Linux+to enable shadow stack for user mode applications on RISC-V++1. Feature Overview+--------------------++Memory corruption issues usually result into crashes, however when in hands of+an adversary and if used creatively can result into a variety security issues.++One of those security issues can be code re-use attacks on program where+adversary can use corrupt return addresses present on stack and chain them+together to perform return oriented programming (ROP) and thus compromising+control flow integrity (CFI) of the program.++Return addresses live on stack and thus in read-write memory and thus are+susceptible to corruption and which allows an adversary to reach any program+counter (PC) in address space. On RISC-V ``zicfiss`` extension provides an+alternate stack termed as shadow stack on which return addresses can be safely+placed in prolog of the function and retrieved in epilog. ``zicfiss`` extension+makes following changes:++- PTE encodings for shadow stack virtual memory+ An earlier reserved encoding in first stage translation i.e.+ PTE.R=0, PTE.W=1, PTE.X=0 becomes PTE encoding for shadow stack pages.++-``sspush x1/x5`` instruction pushes (stores) ``x1/x5`` to shadow stack.++-``sspopchk x1/x5`` instruction pops (loads) from shadow stack and compares+ with ``x1/x5`` and if un-equal, CPU raises ``software check exception`` with+``*tval = 3``++Compiler toolchain makes sure that function prologue have ``sspush x1/x5`` to+save return address on shadow stack in addition to regular stack. Similarly+function epilogs have ``ld x5, offset(x2)`` followed by ``sspopchk x5`` to+ensure that popped value from regular stack matches with popped value from+shadow stack.++2. Shadow stack protections and linux memory manager+-----------------------------------------------------++As mentioned earlier, shadow stacks get new page table encodings and thus have+some special properties assigned to them and instructions that operate on them+as below:++- Regular stores to shadow stack memory raises access store faults. This way+ shadow stack memory is protected from stray inadvertent writes.++- Regular loads to shadow stack memory are allowed. This allows stack trace+ utilities or backtrace functions to read true callstack (not tampered).++- Only shadow stack instructions can generate shadow stack load or shadow stack+ store.++- Shadow stack load / shadow stack store on read-only memory raises AMO/store+ page fault. Thus both ``sspush x1/x5`` and ``sspopchk x1/x5`` will raise AMO/+ store page fault. This simplies COW handling in kernel during fork, kernel+ can convert shadow stack pages into read-only memory (as it does for regular+ read-write memory) and as soon as subsequent ``sspush`` or ``sspopchk`` in+ userspace is encountered, then kernel can perform COW.++- Shadow stack load / shadow stack store on read-write, read-write-execute+ memory raises an access fault. This is a fatal condition because shadow stack+ should never be operating on read-write, read-write-execute memory.++3. ELF and psABI+-----------------++Toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_BCFI` for property+:c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in notes section of the object file.++4. Linux enabling+------------------++User space programs can have multiple shared objects loaded in its address space+and it's a difficult task to make sure all the dependencies have been compiled+with support of shadow stack. Thus it's left to dynamic loader to enable+shadow stack for the program.++5. prctl() enabling+--------------------++:c:macro:`PR_SET_SHADOW_STACK_STATUS` / :c:macro:`PR_GET_SHADOW_STACK_STATUS` /+:c:macro:`PR_LOCK_SHADOW_STACK_STATUS` are three prctls added to manage shadow+stack enabling for tasks. prctls are arch agnostic and returns -EINVAL on other+arches.++* prctl(PR_SET_SHADOW_STACK_STATUS, unsigned long arg)++If arg1 :c:macro:`PR_SHADOW_STACK_ENABLE` and if CPU supports ``zicfiss`` then+kernel will enable shadow stack for the task. Dynamic loader can issue this+:c:macro:`prctl` once it has determined that all the objects loaded in address+space have support for shadow stack. Additionally if there is a+:c:macro:`dlopen` to an object which wasn't compiled with ``zicfiss``, dynamic+loader can issue this prctl with arg1 set to 0 (i.e.+:c:macro:`PR_SHADOW_STACK_ENABLE` being clear)++* prctl(PR_GET_SHADOW_STACK_STATUS, unsigned long * arg)++Returns current status of indirect branch tracking. If enabled it'll return+:c:macro:`PR_SHADOW_STACK_ENABLE`.++* prctl(PR_LOCK_SHADOW_STACK_STATUS, unsigned long arg)++Locks current status of shadow stack enabling on the task. User space may want+to run with strict security posture and wouldn't want loading of objects+without ``zicfiss`` support in it and thus would want to disallow disabling of+shadow stack on current task. In that case user space can use this prctl to+lock current settings.++5. violations related to returns with shadow stack enabled+-----------------------------------------------------------++Pertaining to shadow stack, CPU raises software check exception in following+condition:++- On execution of ``sspopchk x1/x5``, ``x1/x5`` didn't match top of shadow+ stack. If mismatch happens then cpu does ``*tval = 3`` and raise software+ check exception.++Linux kernel will treat this as :c:macro:`SIGSEGV` with code =+:c:macro:`SEGV_CPERR` and follow normal course of signal delivery.++6. Shadow stack tokens+-----------------------+Regular stores on shadow stacks are not allowed and thus can't be tampered+with via arbitrary stray writes due to bugs. However method of pivoting /+switching to shadow stack is simply writing to csr ``CSR_SSP`` and that will+change active shadow stack for the program. Instances of writes to ``CSR_SSP``+in the address space of the program should be mostly limited to context+switching, stack unwind, longjmp or similar mechanisms (like context switching+of green threads) in languages like go, rust. This can be problematic because+an attacker can use memory corruption bugs and eventually use such context+switching routines to pivot to any shadow stack. Shadow stack tokens can help+mitigate this problem by making sure that:++- When software is switching away from a shadow stack, shadow stack pointer+ should be saved on shadow stack itself and call it ``shadow stack token``++- When software is switching to a shadow stack, it should read the+``shadow stack token`` from shadow stack pointer and verify that+``shadow stack token`` itself is pointer to shadow stack itself.++- Once the token verification is done, software can perform the write to+``CSR_SSP`` to switch shadow stack.++Here software can be user mode task runtime itself which is managing various+contexts as part of single thread. Software can be kernel as well when kernel+has to deliver a signal to user task and must save shadow stack pointer. Kernel+can perform similar procedure by saving a token on user shadow stack itself.+This way whenever :c:macro:`sigreturn` happens, kernel can read the token and+verify the token and then switch to shadow stack. Using this mechanism, kernel+helps user task so that any corruption issue in user task is not exploited by+adversary by arbitrarily using :c:macro:`sigreturn`. Adversary will have to+make sure that there is a ``shadow stack token`` in addition to invoking+:c:macro:`sigreturn`++7. Signal shadow stack+-----------------------+Following structure has been added to sigcontext for RISC-V::++ struct __sc_riscv_cfi_state {+ unsigned long ss_ptr;+ };++As part of signal delivery, shadow stack token is saved on current shadow stack+itself and updated pointer is saved away in :c:macro:`ss_ptr` field in+:c:macro:`__sc_riscv_cfi_state` under :c:macro:`sigcontext`. Existing shadow+stack allocation is used for signal delivery. During :c:macro:`sigreturn`,+kernel will obtain :c:macro:`ss_ptr` from :c:macro:`sigcontext` and verify the+saved token on shadow stack itself and switch shadow stack.
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Jim Shu <redacted>
user mode tasks compiled with zicfilp may call indirectly into vdso (like
hwprobe indirect calls). Add landing pad compile support in vdso. vdso
with landing pad in it will be nop for tasks which have not enabled
landing pad. Furthermore, adding support for C sources of vdso to be
compiled with shadow stack and landing pad enabled as well.
Landing pad and shadow stack instructions are emitted only when VDSO_CFI
cflags option is defined during compile.
Signed-off-by: Jim Shu <redacted>
Reviewed-by: Zong Li <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/Makefile | 5 +++-
arch/riscv/include/asm/assembler.h | 44 ++++++++++++++++++++++++++++++
arch/riscv/kernel/vdso/Makefile | 11 +++++++-
arch/riscv/kernel/vdso/flush_icache.S | 4 +++
arch/riscv/kernel/vdso/getcpu.S | 4 +++
arch/riscv/kernel/vdso/note.S | 3 ++
arch/riscv/kernel/vdso/rt_sigreturn.S | 4 +++
arch/riscv/kernel/vdso/sys_hwprobe.S | 4 +++
arch/riscv/kernel/vdso/vgetrandom-chacha.S | 5 +++-
9 files changed, 81 insertions(+), 3 deletions(-)
@@ -81,9 +81,12 @@ riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZACAS) := $(riscv-march-y)_zacas# Check if the toolchain supports Zabhariscv-march-$(CONFIG_TOOLCHAIN_HAS_ZABHA):=$(riscv-march-y)_zabha+KBUILD_BASE_ISA=-march=$(shellecho$(riscv-march-y)|sed-E's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')+exportKBUILD_BASE_ISA+# Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by# matching non-v and non-multi-letter extensions out with the filter ([^v_]*)-KBUILD_CFLAGS+=-march=$(shellecho$(riscv-march-y)|sed-E's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')+KBUILD_CFLAGS+=$(KBUILD_BASE_ISA)KBUILD_AFLAGS+=-march=$(riscv-march-y)
@@ -17,6 +17,11 @@ ifdef CONFIG_VDSO_GETRANDOMvdso-syms+=getrandomendif+ifdef VDSO_CFI_BUILD+CFI_MARCH=_zicfilp_zicfiss+CFI_FULL=-fcf-protection=full+endif+# Files to link into the vdsoobj-vdso=$(patsubst%,%.o,$(vdso-syms))note.o
@@ -79,7 +88,7 @@ include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE# The DSO images are built using a special linker script# Make sure only to export the intended __vdso_xxx symbol offsets.quiet_cmd_vdsold_and_check=VDSOLD$@-cmd_vdsold_and_check=$(LD)$(ld_flags)-T$(filter-outFORCE,$^)-o$@.tmp&&\+cmd_vdsold_and_check=$(LD)$(CFI_FULL)$(ld_flags)-T$(filter-outFORCE,$^)-o$@.tmp&&\$(OBJCOPY)$(patsubst%,-G__vdso_%,$(vdso-syms))$@.tmp$@&&\rm$@.tmp&&\$(cmd_vdso_check)
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
Shadow stack instructions are taken from zimop (mandated on RVA23).
Any hardware prior to RVA23 profile will fault on shadow stack instruction.
Any userspace with shadow stack instruction in it will fault on such
hardware. Thus such userspace can't be brought onto such a hardware.
It's not known how userspace will respond to such binary fragmentation.
However in order to keep kernel portable across such different hardware,
`arch/riscv/kernel/vdso_cfi` is created which has logic (Makefile) to
compile `arch/riscv/kernel/vdso` sources with cfi flags and then changes
in `arch/riscv/kernel/vdso.c` for selecting appropriate vdso depending
on whether underlying hardware(cpu) implements zimop extension. Offset
of vdso symbols will change due to having two different vdso binaries,
there is added logic to include new generated vdso offset header and
dynamically select offset (like for rt_sigreturn).
Acked-by: Charles Mirabile <redacted>
Tested-by: Andreas Korb <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
arch/riscv/Makefile | 3 +++
arch/riscv/include/asm/vdso.h | 13 ++++++++++++-
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/vdso.c | 7 +++++++
arch/riscv/kernel/vdso/Makefile | 29 ++++++++++++++++++++---------
arch/riscv/kernel/vdso/gen_vdso_offsets.sh | 4 +++-
arch/riscv/kernel/vdso_cfi/Makefile | 25 +++++++++++++++++++++++++
arch/riscv/kernel/vdso_cfi/vdso-cfi.S | 11 +++++++++++
8 files changed, 82 insertions(+), 11 deletions(-)
@@ -20,6 +20,10 @@ endififdef VDSO_CFI_BUILDCFI_MARCH=_zicfilp_zicfissCFI_FULL=-fcf-protection=full+CFI_SUFFIX=-cfi+OFFSET_SUFFIX=_cfi+ccflags-y+=-DVDSO_CFI=1+asflags-y+=-DVDSO_CFI=1endif# Files to link into the vdso
@@ -48,13 +52,20 @@ endifCFLAGS_hwprobe.o+=-fPIC# Build rules-targets:=$(obj-vdso)vdso.sovdso.so.dbgvdso.lds+vdso_offsets:=vdso$(if$(VDSO_CFI_BUILD),$(CFI_SUFFIX),)-offsets.h+vdso_o:=vdso$(if$(VDSO_CFI_BUILD),$(CFI_SUFFIX),).o+vdso_so:=vdso$(if$(VDSO_CFI_BUILD),$(CFI_SUFFIX),).so+vdso_so_dbg:=vdso$(if$(VDSO_CFI_BUILD),$(CFI_SUFFIX),).so.dbg+vdso_lds:=vdso.lds++targets:=$(obj-vdso)$(vdso_so)$(vdso_so_dbg)$(vdso_lds)+obj-vdso:=$(addprefix$(obj)/,$(obj-vdso))-obj-y+=vdso.o-CPPFLAGS_vdso.lds+=-P-C-U$(ARCH)+obj-y+=vdso$(if$(VDSO_CFI_BUILD),$(CFI_SUFFIX),).o+CPPFLAGS_$(vdso_lds)+=-P-C-U$(ARCH)ifneq ($(filter vgettimeofday, $(vdso-syms)),)-CPPFLAGS_vdso.lds+=-DHAS_VGETTIMEOFDAY+CPPFLAGS_$(vdso_lds)+=-DHAS_VGETTIMEOFDAYendif# Disable -pg to prevent insert call site
@@ -63,12 +74,12 @@ CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS)CFLAGS_REMOVE_hwprobe.o=$(CC_FLAGS_FTRACE)$(CC_FLAGS_SCS)# Force dependency-$(obj)/vdso.o:$(obj)/vdso.so+$(obj)/$(vdso_o):$(obj)/$(vdso_so)# link rule for the .so file, .lds has to be first-$(obj)/vdso.so.dbg:$(obj)/vdso.lds$(obj-vdso)FORCE+$(obj)/$(vdso_so_dbg):$(obj)/$(vdso_lds)$(obj-vdso)FORCE$(callif_changed,vdsold_and_check)-LDFLAGS_vdso.so.dbg=-shared-soname=linux-vdso.so.1\+LDFLAGS_$(vdso_so_dbg)=-shared-soname=linux-vdso.so.1\--build-id=sha1--eh-frame-hdr# strip rule for the .so file
@@ -79,9 +90,9 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE# Generate VDSO offsets using helper scriptgen-vdsosym:=$(src)/gen_vdso_offsets.shquiet_cmd_vdsosym=VDSOSYM$@-cmd_vdsosym=$(NM)$<|$(gen-vdsosym)|LC_ALL=Csort>$@+cmd_vdsosym=$(NM)$<|$(gen-vdsosym)$(OFFSET_SUFFIX)|LC_ALL=Csort>$@-include/generated/vdso-offsets.h:$(obj)/vdso.so.dbgFORCE+include/generated/$(vdso_offsets):$(obj)/$(vdso_so_dbg)FORCE$(callif_changed,vdsosym)# actual build commands
@@ -0,0 +1,25 @@+# SPDX-License-Identifier: GPL-2.0-only+# RISC-V VDSO CFI Makefile+# This Makefile builds the VDSO with CFI support when CONFIG_RISCV_USER_CFI is enabled++# setting VDSO_CFI_BUILD triggers build for vdso differently+VDSO_CFI_BUILD:=1++# Set the source directory to the main vdso directory+src:=$(srctree)/arch/riscv/kernel/vdso++# Copy all .S and .c files from vdso directory to vdso_cfi object build directory+vdso_c_sources:=$(wildcard$(src)/*.c)+vdso_S_sources:=$(wildcard$(src)/*.S)+vdso_c_objects:=$(addprefix$(obj)/,$(notdir$(vdso_c_sources)))+vdso_S_objects:=$(addprefix$(obj)/,$(notdir$(vdso_S_sources)))++$(vdso_S_objects):$(obj)/%.S: $(src)/%.S+$(Q)cp$<$@++$(vdso_c_objects):$(obj)/%.c: $(src)/%.c+$(Q)cp$<$@++# Include the main VDSO Makefile which contains all the build rules and sources+# The VDSO_CFI_BUILD variable will be passed to it to enable CFI compilation+include $(src)/Makefile
From: Deepak Gupta via B4 Relay <devnull+debug.rivosinc.com@kernel.org> Date: 2025-12-05 18:41:52
From: Deepak Gupta <redacted>
Adds kselftest for RISC-V control flow integrity implementation for user
mode. There is not a lot going on in kernel for enabling landing pad for
user mode. cfi selftest are intended to be compiled with zicfilp and
zicfiss enabled compiler. Thus kselftest simply checks if landing pad /
shadow stack for the process are enabled or not and executes ptrace
selftests on cfi. selftest then register a signal handler for SIGSEGV.
Any control flow violation are reported as SIGSEGV with si_code =
SEGV_CPERR. Test will fail on receiving any SEGV_CPERR. Shadow stack part
has more changes in kernel and thus there are separate tests for that
- Exercise `map_shadow_stack` syscall
- `fork` test to make sure COW works for shadow stack pages
- gup tests
Kernel uses FOLL_FORCE when access happens to memory via
/proc/<pid>/mem. Not breaking that for shadow stack.
- signal test. Make sure signal delivery results in token creation on
shadow stack and consumes (and verifies) token on sigreturn
- shadow stack protection test. attempts to write using regular store
instruction on shadow stack memory must result in access faults
- ptrace test: adds landing pad violation, clears ELP and continues
In case toolchain doesn't support cfi extension, cfi kselftest wont
get built.
Test outut
==========
"""
TAP version 13
1..5
This is to ensure shadow stack is indeed enabled and working
This is to ensure shadow stack is indeed enabled and working
ok 1 shstk fork test
ok 2 map shadow stack syscall
ok 3 shadow stack gup tests
ok 4 shadow stack signal tests
ok 5 memory protections of shadow stack memory
"""
Suggested-by: Charlie Jenkins <redacted>
Signed-off-by: Charlie Jenkins <redacted>
Tested-by: Valentin Haudiquet <redacted>
Signed-off-by: Deepak Gupta <redacted>
---
tools/testing/selftests/riscv/Makefile | 2 +-
tools/testing/selftests/riscv/cfi/.gitignore | 2 +
tools/testing/selftests/riscv/cfi/Makefile | 23 ++
tools/testing/selftests/riscv/cfi/cfi_rv_test.h | 82 +++++
tools/testing/selftests/riscv/cfi/cfitests.c | 173 +++++++++++
tools/testing/selftests/riscv/cfi/shadowstack.c | 385 ++++++++++++++++++++++++
tools/testing/selftests/riscv/cfi/shadowstack.h | 27 ++
7 files changed, 693 insertions(+), 1 deletion(-)
@@ -0,0 +1,173 @@+// SPDX-License-Identifier: GPL-2.0-only++#include"../../kselftest.h"+#include<sys/signal.h>+#include<asm/ucontext.h>+#include<linux/prctl.h>+#include<errno.h>+#include<linux/ptrace.h>+#include<sys/wait.h>+#include<linux/elf.h>+#include<sys/uio.h>+#include<asm-generic/unistd.h>++#include"cfi_rv_test.h"++/* do not optimize cfi related test functions */+#pragma GCC push_options+#pragma GCC optimize("O0")++voidsigsegv_handler(intsignum,siginfo_t*si,void*uc)+{+structucontext*ctx=(structucontext*)uc;++if(si->si_code==SEGV_CPERR){+ksft_print_msg("Control flow violation happened somewhere\n");+ksft_print_msg("PC where violation happened %lx\n",ctx->uc_mcontext.gregs[0]);+exit(-1);+}++/* all other cases are expected to be of shadow stack write case */+exit(CHILD_EXIT_CODE_SSWRITE);+}++boolregister_signal_handler(void)+{+structsigactionsa={};++sa.sa_sigaction=sigsegv_handler;+sa.sa_flags=SA_SIGINFO;+if(sigaction(SIGSEGV,&sa,NULL)){+ksft_print_msg("Registering signal handler for landing pad violation failed\n");+returnfalse;+}++returntrue;+}++longptrace(intrequest,pid_tpid,void*addr,void*data);++boolcfi_ptrace_test(void)+{+pid_tpid;+intstatus,ret=0;+unsignedlongptrace_test_num=0,total_ptrace_tests=2;++structuser_cfi_statecfi_reg;+structioveciov;++pid=fork();++if(pid==-1){+ksft_exit_fail_msg("%s: fork failed\n",__func__);+exit(1);+}++if(pid==0){+/* allow to be traced */+ptrace(PTRACE_TRACEME,0,NULL,NULL);+raise(SIGSTOP);+asmvolatile("la a5, 1f\n"+"jalr a5\n"+"nop\n"+"nop\n"+"1: nop\n"+:::"a5");+exit(11);+/* child shouldn't go beyond here */+}++/* parent's code goes here */+iov.iov_base=&cfi_reg;+iov.iov_len=sizeof(cfi_reg);++while(ptrace_test_num<total_ptrace_tests){+memset(&cfi_reg,0,sizeof(cfi_reg));+waitpid(pid,&status,0);+if(WIFSTOPPED(status)){+errno=0;+ret=ptrace(PTRACE_GETREGSET,pid,(void*)NT_RISCV_USER_CFI,&iov);+if(ret==-1&&errno)+ksft_exit_fail_msg("%s: PTRACE_GETREGSET failed\n",__func__);+}else{+ksft_exit_fail_msg("%s: child didn't stop, failed\n",__func__);+}++switch(ptrace_test_num){+#define CFI_ENABLE_MASK (PTRACE_CFI_LP_EN_STATE | \+PTRACE_CFI_SS_EN_STATE|\+PTRACE_CFI_SS_PTR_STATE)+case0:+if((cfi_reg.cfi_status.cfi_state&CFI_ENABLE_MASK)!=CFI_ENABLE_MASK)+ksft_exit_fail_msg("%s: ptrace_getregset failed, %llu\n",__func__,+cfi_reg.cfi_status.cfi_state);+if(!cfi_reg.shstk_ptr)+ksft_exit_fail_msg("%s: NULL shadow stack pointer, test failed\n",+__func__);+break;+case1:+if(!(cfi_reg.cfi_status.cfi_state&PTRACE_CFI_ELP_STATE))+ksft_exit_fail_msg("%s: elp must have been set\n",__func__);+/* clear elp state. not interested in anything else */+cfi_reg.cfi_status.cfi_state=0;++ret=ptrace(PTRACE_SETREGSET,pid,(void*)NT_RISCV_USER_CFI,&iov);+if(ret==-1&&errno)+ksft_exit_fail_msg("%s: PTRACE_GETREGSET failed\n",__func__);+break;+default:+ksft_exit_fail_msg("%s: unreachable switch case\n",__func__);+break;+}+ptrace(PTRACE_CONT,pid,NULL,NULL);+ptrace_test_num++;+}++waitpid(pid,&status,0);+if(WEXITSTATUS(status)!=11)+ksft_print_msg("%s, bad return code from child\n",__func__);++ksft_print_msg("%s, ptrace test succeeded\n",__func__);+returntrue;+}++intmain(intargc,char*argv[])+{+intret=0;+unsignedlonglpad_status=0,ss_status=0;++ksft_print_header();++ksft_print_msg("Starting risc-v tests\n");++/*+*Landingpadtest.Notalotofkernelchangestosupportlanding+*padforusermodeexceptlightingupabitinsenvcfgviaaprctl+*Enablelandingpadthroughouttheexecutionoftestbinary+*/+ret=my_syscall5(__NR_prctl,PR_GET_INDIR_BR_LP_STATUS,&lpad_status,0,0,0);+if(ret)+ksft_exit_fail_msg("Get landing pad status failed with %d\n",ret);++if(!(lpad_status&PR_INDIR_BR_LP_ENABLE))+ksft_exit_fail_msg("Landing pad is not enabled, should be enabled via glibc\n");++ret=my_syscall5(__NR_prctl,PR_GET_SHADOW_STACK_STATUS,&ss_status,0,0,0);+if(ret)+ksft_exit_fail_msg("Get shadow stack failed with %d\n",ret);++if(!(ss_status&PR_SHADOW_STACK_ENABLE))+ksft_exit_fail_msg("Shadow stack is not enabled, should be enabled via glibc\n");++if(!register_signal_handler())+ksft_exit_fail_msg("Registering signal handler for SIGSEGV failed\n");++ksft_print_msg("Landing pad and shadow stack are enabled for binary\n");+cfi_ptrace_test();++execute_shadow_stack_tests();++return0;+}++#pragma GCC pop_options
@@ -0,0 +1,385 @@+// SPDX-License-Identifier: GPL-2.0-only++#include"../../kselftest.h"+#include<sys/wait.h>+#include<signal.h>+#include<fcntl.h>+#include<asm-generic/unistd.h>+#include<sys/mman.h>+#include"shadowstack.h"+#include"cfi_rv_test.h"++staticstructshadow_stack_testsshstk_tests[]={+{"shstk fork test\n",shadow_stack_fork_test},+{"map shadow stack syscall\n",shadow_stack_map_test},+{"shadow stack gup tests\n",shadow_stack_gup_tests},+{"shadow stack signal tests\n",shadow_stack_signal_test},+{"memory protections of shadow stack memory\n",shadow_stack_protection_test}+};++#define RISCV_SHADOW_STACK_TESTS ARRAY_SIZE(shstk_tests)++/* do not optimize shadow stack related test functions */+#pragma GCC push_options+#pragma GCC optimize("O0")++voidzar(void)+{+unsignedlongssp=0;++ssp=csr_read(CSR_SSP);+ksft_print_msg("Spewing out shadow stack ptr: %lx\n"+" This is to ensure shadow stack is indeed enabled and working\n",+ssp);+}++voidbar(void)+{+zar();+}++voidfoo(void)+{+bar();+}++voidzar_child(void)+{+unsignedlongssp=0;++ssp=csr_read(CSR_SSP);+ksft_print_msg("Spewing out shadow stack ptr: %lx\n"+" This is to ensure shadow stack is indeed enabled and working\n",+ssp);+}++voidbar_child(void)+{+zar_child();+}++voidfoo_child(void)+{+bar_child();+}++typedefvoid(call_func_ptr)(void);+/*+*callcoupleoffunctionstotestpushpop.+*/+intshadow_stack_call_tests(call_func_ptrfn_ptr,boolparent)+{+ksft_print_msg("dummy calls for sspush and sspopchk in context of %s\n",+parent?"parent":"child");++(fn_ptr)();++return0;+}++/* forks a thread, and ensure shadow stacks fork out */+boolshadow_stack_fork_test(unsignedlongtest_num,void*ctx)+{+intpid=0,child_status=0,parent_pid=0,ret=0;+unsignedlongss_status=0;++ksft_print_msg("Exercising shadow stack fork test\n");++ret=my_syscall5(__NR_prctl,PR_GET_SHADOW_STACK_STATUS,&ss_status,0,0,0);+if(ret){+ksft_exit_skip("Shadow stack get status prctl failed with errorcode %d\n",ret);+returnfalse;+}++if(!(ss_status&PR_SHADOW_STACK_ENABLE))+ksft_exit_skip("Shadow stack is not enabled, should be enabled via glibc\n");++parent_pid=getpid();+pid=fork();++if(pid){+ksft_print_msg("Parent pid %d and child pid %d\n",parent_pid,pid);+shadow_stack_call_tests(&foo,true);+}else{+shadow_stack_call_tests(&foo_child,false);+}++if(pid){+ksft_print_msg("Waiting on child to finish\n");+wait(&child_status);+}else{+/* exit child gracefully */+exit(0);+}++if(pid&&WIFSIGNALED(child_status)){+ksft_print_msg("Child faulted, fork test failed\n");+returnfalse;+}++returntrue;+}++/* exercise `map_shadow_stack`, pivot to it and call some functions to ensure it works */+#define SHADOW_STACK_ALLOC_SIZE 4096+boolshadow_stack_map_test(unsignedlongtest_num,void*ctx)+{+unsignedlongshdw_addr;+intret=0;++ksft_print_msg("Exercising shadow stack map test\n");++shdw_addr=my_syscall3(__NR_map_shadow_stack,NULL,SHADOW_STACK_ALLOC_SIZE,0);++if(((long)shdw_addr)<=0){+ksft_print_msg("map_shadow_stack failed with error code %d\n",+(int)shdw_addr);+returnfalse;+}++ret=munmap((void*)shdw_addr,SHADOW_STACK_ALLOC_SIZE);++if(ret){+ksft_print_msg("munmap failed with error code %d\n",ret);+returnfalse;+}++returntrue;+}++/*+*shadowstackprotectiontests.mapashadowstackand+*validateallmemoryprotectionsworkonit+*/+boolshadow_stack_protection_test(unsignedlongtest_num,void*ctx)+{+unsignedlongshdw_addr;+unsignedlong*write_addr=NULL;+intret=0,pid=0,child_status=0;++ksft_print_msg("Exercising shadow stack protection test (WPT)\n");++shdw_addr=my_syscall3(__NR_map_shadow_stack,NULL,SHADOW_STACK_ALLOC_SIZE,0);++if(((long)shdw_addr)<=0){+ksft_print_msg("map_shadow_stack failed with error code %d\n",+(int)shdw_addr);+returnfalse;+}++write_addr=(unsignedlong*)shdw_addr;+pid=fork();++/* no child was created, return false */+if(pid==-1)+returnfalse;++/*+*trytoperformastorefromchildonshadowstackmemory+*itshouldresultinSIGSEGV+*/+if(!pid){+/* below write must lead to SIGSEGV */+*write_addr=0xdeadbeef;+}else{+wait(&child_status);+}++/* test fail, if 0xdeadbeef present on shadow stack address */+if(*write_addr==0xdeadbeef){+ksft_print_msg("Shadow stack WPT failed\n");+returnfalse;+}++/* if child reached here, then fail */+if(!pid){+ksft_print_msg("Shadow stack WPT failed: child reached unreachable state\n");+returnfalse;+}++/* if child exited via signal handler but not for write on ss */+if(WIFEXITED(child_status)&&+WEXITSTATUS(child_status)!=CHILD_EXIT_CODE_SSWRITE){+ksft_print_msg("Shadow stack WPT failed: child wasn't signaled for write\n");+returnfalse;+}++ret=munmap(write_addr,SHADOW_STACK_ALLOC_SIZE);+if(ret){+ksft_print_msg("Shadow stack WPT failed: munmap failed, error code %d\n",+ret);+returnfalse;+}++returntrue;+}++#define SS_MAGIC_WRITE_VAL 0xbeefdead++intgup_tests(intmem_fd,unsignedlong*shdw_addr)+{+unsignedlongval=0;++lseek(mem_fd,(unsignedlong)shdw_addr,SEEK_SET);+if(read(mem_fd,&val,sizeof(val))<0){+ksft_print_msg("Reading shadow stack mem via gup failed\n");+return1;+}++val=SS_MAGIC_WRITE_VAL;+lseek(mem_fd,(unsignedlong)shdw_addr,SEEK_SET);+if(write(mem_fd,&val,sizeof(val))<0){+ksft_print_msg("Writing shadow stack mem via gup failed\n");+return1;+}++if(*shdw_addr!=SS_MAGIC_WRITE_VAL){+ksft_print_msg("GUP write to shadow stack memory failed\n");+return1;+}++return0;+}++boolshadow_stack_gup_tests(unsignedlongtest_num,void*ctx)+{+unsignedlongshdw_addr=0;+unsignedlong*write_addr=NULL;+intfd=0;+boolret=false;++ksft_print_msg("Exercising shadow stack gup tests\n");+shdw_addr=my_syscall3(__NR_map_shadow_stack,NULL,SHADOW_STACK_ALLOC_SIZE,0);++if(((long)shdw_addr)<=0){+ksft_print_msg("map_shadow_stack failed with error code %d\n",(int)shdw_addr);+returnfalse;+}++write_addr=(unsignedlong*)shdw_addr;++fd=open("/proc/self/mem",O_RDWR);+if(fd==-1)+returnfalse;++if(gup_tests(fd,write_addr)){+ksft_print_msg("gup tests failed\n");+gotoout;+}++ret=true;+out:+if(shdw_addr&&munmap(write_addr,SHADOW_STACK_ALLOC_SIZE)){+ksft_print_msg("munmap failed with error code %d\n",ret);+ret=false;+}++returnret;+}++volatileboolbreak_loop;++voidsigusr1_handler(intsigno)+{+break_loop=true;+}++boolsigusr1_signal_test(void)+{+structsigactionsa={};++sa.sa_handler=sigusr1_handler;+sa.sa_flags=0;+sigemptyset(&sa.sa_mask);+if(sigaction(SIGUSR1,&sa,NULL)){+ksft_print_msg("Registering signal handler for SIGUSR1 failed\n");+returnfalse;+}++returntrue;+}++/*+*shadowstacksignaltest.shadowstackmustbeenabled.+*registerasignal,forkanotherthreadwhichiswaiting+*onsignal.Sendasignalfromparenttochild,verify+*thatsignalwasreceivedbychild.Ifnottestfails+*/+boolshadow_stack_signal_test(unsignedlongtest_num,void*ctx)+{+intpid=0,child_status=0,ret=0;+unsignedlongss_status=0;++ksft_print_msg("Exercising shadow stack signal test\n");++ret=my_syscall5(__NR_prctl,PR_GET_SHADOW_STACK_STATUS,&ss_status,0,0,0);+if(ret){+ksft_print_msg("Shadow stack get status prctl failed with errorcode %d\n",ret);+returnfalse;+}++if(!(ss_status&PR_SHADOW_STACK_ENABLE))+ksft_print_msg("Shadow stack is not enabled, should be enabled via glibc\n");++/* this should be caught by signal handler and do an exit */+if(!sigusr1_signal_test()){+ksft_print_msg("Registering sigusr1 handler failed\n");+exit(-1);+}++pid=fork();++if(pid==-1){+ksft_print_msg("Signal test: fork failed\n");+gotoout;+}++if(pid==0){+while(!break_loop)+sleep(1);++exit(11);+/* child shouldn't go beyond here */+}++/* send SIGUSR1 to child */+kill(pid,SIGUSR1);+wait(&child_status);++out:++return(WIFEXITED(child_status)&&+WEXITSTATUS(child_status)==11);+}++intexecute_shadow_stack_tests(void)+{+intret=0;+unsignedlongtest_count=0;+unsignedlongshstk_status=0;+booltest_pass=false;++ksft_print_msg("Executing RISC-V shadow stack self tests\n");+ksft_set_plan(RISCV_SHADOW_STACK_TESTS);++ret=my_syscall5(__NR_prctl,PR_GET_SHADOW_STACK_STATUS,&shstk_status,0,0,0);++if(ret!=0)+ksft_exit_fail_msg("Get shadow stack status failed with %d\n",ret);++/*+*Ifweareherethatmeansgetshadowstackstatussucceededand+*thusshadowstacksupportisbakedinthekernel.+*/+while(test_count<RISCV_SHADOW_STACK_TESTS){+test_pass=(*shstk_tests[test_count].t_func)(test_count,NULL);+ksft_test_result(test_pass,shstk_tests[test_count].name);+test_count++;+}++ksft_finished();++return0;+}++#pragma GCC pop_options
From: Krzysztof Kozlowski <krzk@kernel.org> Date: 2025-12-05 19:32:50
On 05/12/2025 19:41, Deepak Gupta via B4 Relay wrote:
v25: Removal of `riscv_nousercfi` from `cpufeature.c` and instead placing
it as extern in `usercfi.h` was leading to build error whene cfi config
is not selected. Placed `riscv_nousercfi` outside cfi config ifdef block
in `usercfi.h`
Please stop. You sent this 28-patch-bomb TWICE to 50 or 60 addresses.
It's actually merge window so it should wait in the first place, but for
sure sending it multiple times does not help. Please observe the Linux
development process.
Best regards,
Krzysztof
On Fri, Dec 05, 2025 at 08:32:32PM +0100, Krzysztof Kozlowski wrote:
On 05/12/2025 19:41, Deepak Gupta via B4 Relay wrote:
quoted
v25: Removal of `riscv_nousercfi` from `cpufeature.c` and instead placing
it as extern in `usercfi.h` was leading to build error whene cfi config
is not selected. Placed `riscv_nousercfi` outside cfi config ifdef block
in `usercfi.h`
Please stop. You sent this 28-patch-bomb TWICE to 50 or 60 addresses.
It's actually merge window so it should wait in the first place, but for
sure sending it multiple times does not help. Please observe the Linux
development process.
You can just delete them. I don't know about you but riscv shadow stack is not
something I even pretend to know so it all goes to /dev/null
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
On Fri, Dec 05, 2025 at 08:32:32PM +0100, Krzysztof Kozlowski wrote:
On 05/12/2025 19:41, Deepak Gupta via B4 Relay wrote:
quoted
v25: Removal of `riscv_nousercfi` from `cpufeature.c` and instead placing
it as extern in `usercfi.h` was leading to build error whene cfi config
is not selected. Placed `riscv_nousercfi` outside cfi config ifdef block
in `usercfi.h`
Please stop. You sent this 28-patch-bomb TWICE to 50 or 60 addresses.
It's actually merge window so it should wait in the first place, but for
sure sending it multiple times does not help. Please observe the Linux
development process.
From: Paul Walmsley <pjw@kernel.org> Date: 2025-12-11 08:47:56
On Fri, 5 Dec 2025, Deepak Gupta via B4 Relay wrote:
From: Deepak Gupta <redacted>
`arch_calc_vm_prot_bits` is implemented on risc-v to return VM_READ |
VM_WRITE if PROT_WRITE is specified. Similarly `riscv_sys_mmap` is
updated to convert all incoming PROT_WRITE to (PROT_WRITE | PROT_READ).
This is to make sure that any existing apps using PROT_WRITE still work.
Earlier `protection_map[VM_WRITE]` used to pick read-write PTE encodings.
Now `protection_map[VM_WRITE]` will always pick PAGE_SHADOWSTACK PTE
encodings for shadow stack. Above changes ensure that existing apps
continue to work because underneath kernel will be picking
`protection_map[VM_WRITE|VM_READ]` PTE encodings.
Reviewed-by: Zong Li <redacted>
Reviewed-by: Alexandre Ghiti <redacted>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This Signed-off-by: doesn't look right. It doesn't look like Arnd
developed this patch, and it doesn't appear that he replied with a
Signed-off-by: to the list regarding a patch that you wrote. Did I miss
it? Did you mean Co-developed-by: or some other tag?
@@ -16,6 +17,15 @@ static long riscv_sys_mmap(unsigned long addr, unsigned long len,if(unlikely(offset&(~PAGE_MASK>>page_shift_offset)))return-EINVAL;+/*+*IfPROT_WRITEisspecifiedthenextendthattoPROT_READ+*protection_map[VM_WRITE]isnowgoingtoselectshadowstackencodings.+*SospecifyingPROT_WRITEactuallyshouldselectprotection_map[VM_WRITE|VM_READ]+*Ifuserwantstocreateshadowstackthentheyshoulduse`map_shadow_stack`syscall.+*/+if(unlikely((prot&PROT_WRITE)&&!(prot&PROT_READ)))+prot|=PROT_READ;+returnksys_mmap_pgoff(addr,len,prot,flags,fd,offset>>(PAGE_SHIFT-page_shift_offset));}
On Thu, Dec 11, 2025 at 12:47 AM Paul Walmsley [off-list ref] wrote:
On Fri, 5 Dec 2025, Deepak Gupta via B4 Relay wrote:
quoted
From: Deepak Gupta <redacted>
`arch_calc_vm_prot_bits` is implemented on risc-v to return VM_READ |
VM_WRITE if PROT_WRITE is specified. Similarly `riscv_sys_mmap` is
updated to convert all incoming PROT_WRITE to (PROT_WRITE | PROT_READ).
This is to make sure that any existing apps using PROT_WRITE still work.
Earlier `protection_map[VM_WRITE]` used to pick read-write PTE encodings.
Now `protection_map[VM_WRITE]` will always pick PAGE_SHADOWSTACK PTE
encodings for shadow stack. Above changes ensure that existing apps
continue to work because underneath kernel will be picking
`protection_map[VM_WRITE|VM_READ]` PTE encodings.
Reviewed-by: Zong Li <redacted>
Reviewed-by: Alexandre Ghiti <redacted>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This Signed-off-by: doesn't look right. It doesn't look like Arnd
developed this patch, and it doesn't appear that he replied with a
Signed-off-by: to the list regarding a patch that you wrote. Did I miss
it? Did you mean Co-developed-by: or some other tag?