Merge tag 'riscv-for-linus-v7.0-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux

Pull RISC-V updates from Paul Walmsley:
 "Before v7.0 is released, fix a few issues with the CFI patchset,
  merged earlier in v7.0-rc, that primarily affect interfaces to
  non-kernel code:

   - Improve the prctl() interface for per-task indirect branch landing
     pad control to expand abbreviations and to resemble the speculation
     control prctl() interface

   - Expand the "LP" and "SS" abbreviations in the ptrace uapi header
     file to "branch landing pad" and "shadow stack", to improve
     readability

   - Fix a typo in a CFI-related macro name in the ptrace uapi header
     file

   - Ensure that the indirect branch tracking state and shadow stack
     state are unlocked immediately after an exec() on the new task so
     that libc subsequently can control it

   - While working in this area, clean up the kernel-internal,
     cross-architecture prctl() function names by expanding the
     abbreviations mentioned above"

* tag 'riscv-for-linus-v7.0-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux:
  prctl: cfi: change the branch landing pad prctl()s to be more descriptive
  riscv: ptrace: cfi: expand "SS" references to "shadow stack" in uapi headers
  prctl: rename branch landing pad implementation functions to be more explicit
  riscv: ptrace: expand "LP" references to "branch landing pads" in uapi headers
  riscv: cfi: clear CFI lock status in start_thread()
  riscv: ptrace: cfi: fix "PRACE" typo in uapi header
This commit is contained in:
Linus Torvalds
2026-04-10 17:27:08 -07:00
11 changed files with 149 additions and 139 deletions

View File

@@ -76,34 +76,49 @@ the program.
4. prctl() enabling 4. prctl() enabling
-------------------- --------------------
:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` / Per-task indirect branch tracking state can be monitored and
:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI`
branch tracking. These prctls are architecture-agnostic and return -EINVAL if ``prctl()` arguments (respectively), by supplying
the underlying functionality is not supported. :c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument. These
are architecture-agnostic, and will return -EINVAL if the underlying
functionality is not supported.
* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg) * prctl(:c:macro:`PR_SET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long arg)
If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports arg is a bitmask.
``zicfilp`` then the kernel will enable indirect branch tracking for the
task. The dynamic loader can issue this :c:macro:`prctl` once it has If :c:macro:`PR_CFI_ENABLE` is set in arg, and the CPU supports
``zicfilp``, then the kernel will enable indirect branch tracking for
the task. The dynamic loader can issue this ``prctl()`` once it has
determined that all the objects loaded in the address space support determined that all the objects loaded in the address space support
indirect branch tracking. Additionally, if there is a `dlopen` to an indirect branch tracking.
object which wasn't compiled with ``zicfilp``, the dynamic loader can
issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE`
cleared).
* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg) Indirect branch tracking state can also be locked once enabled. This
prevents the task from subsequently disabling it. This is done by
setting the bit :c:macro:`PR_CFI_LOCK` in arg. Either indirect branch
tracking must already be enabled for the task, or the bit
:c:macro:`PR_CFI_ENABLE` must also be set in arg. This is intended
for environments that wish to run with a strict security posture that
do not wish to load objects without ``zicfilp`` support.
Returns the current status of indirect branch tracking. If enabled Indirect branch tracking can also be disabled for the task, assuming
it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE` that it has not previously been enabled and locked. If there is a
``dlopen()`` to an object which wasn't compiled with ``zicfilp``, the
dynamic loader can issue this ``prctl()`` with arg set to
:c:macro:`PR_CFI_DISABLE`. Disabling indirect branch tracking for the
task is not possible if it has previously been enabled and locked.
* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)
Locks the current status of indirect branch tracking on the task. User * prctl(:c:macro:`PR_GET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long * arg)
space may want to run with a strict security posture and wouldn't want
loading of objects without ``zicfilp`` support in them, to disallow Returns the current status of indirect branch tracking into a bitmask
disabling of indirect branch tracking. In this case, user space can stored into the memory location pointed to by arg. The bitmask will
use this prctl to lock the current settings. have the :c:macro:`PR_CFI_ENABLE` bit set if indirect branch tracking
is currently enabled for the task, and if it is locked, will
additionally have the :c:macro:`PR_CFI_LOCK` bit set. If indirect
branch tracking is currently disabled for the task, the
:c:macro:`PR_CFI_DISABLE` bit will be set.
5. violations related to indirect branch tracking 5. violations related to indirect branch tracking
-------------------------------------------------- --------------------------------------------------

View File

@@ -39,7 +39,7 @@ void set_active_shstk(struct task_struct *task, unsigned long shstk_addr);
bool is_shstk_enabled(struct task_struct *task); bool is_shstk_enabled(struct task_struct *task);
bool is_shstk_locked(struct task_struct *task); bool is_shstk_locked(struct task_struct *task);
bool is_shstk_allocated(struct task_struct *task); bool is_shstk_allocated(struct task_struct *task);
void set_shstk_lock(struct task_struct *task); void set_shstk_lock(struct task_struct *task, bool lock);
void set_shstk_status(struct task_struct *task, bool enable); void set_shstk_status(struct task_struct *task, bool enable);
unsigned long get_active_shstk(struct task_struct *task); unsigned long get_active_shstk(struct task_struct *task);
int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr); int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr);
@@ -47,7 +47,7 @@ int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr);
bool is_indir_lp_enabled(struct task_struct *task); bool is_indir_lp_enabled(struct task_struct *task);
bool is_indir_lp_locked(struct task_struct *task); bool is_indir_lp_locked(struct task_struct *task);
void set_indir_lp_status(struct task_struct *task, bool enable); void set_indir_lp_status(struct task_struct *task, bool enable);
void set_indir_lp_lock(struct task_struct *task); void set_indir_lp_lock(struct task_struct *task, bool lock);
#define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE) #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE)
@@ -69,7 +69,7 @@ void set_indir_lp_lock(struct task_struct *task);
#define is_shstk_allocated(task) false #define is_shstk_allocated(task) false
#define set_shstk_lock(task) do {} while (0) #define set_shstk_lock(task, lock) do {} while (0)
#define set_shstk_status(task, enable) do {} while (0) #define set_shstk_status(task, enable) do {} while (0)
@@ -79,7 +79,7 @@ void set_indir_lp_lock(struct task_struct *task);
#define set_indir_lp_status(task, enable) do {} while (0) #define set_indir_lp_status(task, enable) do {} while (0)
#define set_indir_lp_lock(task) do {} while (0) #define set_indir_lp_lock(task, lock) do {} while (0)
#define restore_user_shstk(tsk, shstk_ptr) -EINVAL #define restore_user_shstk(tsk, shstk_ptr) -EINVAL

View File

@@ -132,26 +132,28 @@ struct __sc_riscv_cfi_state {
unsigned long ss_ptr; /* shadow stack pointer */ unsigned long ss_ptr; /* shadow stack pointer */
}; };
#define PTRACE_CFI_LP_EN_BIT 0 #define PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT 0
#define PTRACE_CFI_LP_LOCK_BIT 1 #define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT 1
#define PTRACE_CFI_ELP_BIT 2 #define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT 2
#define PTRACE_CFI_SS_EN_BIT 3 #define PTRACE_CFI_SHADOW_STACK_EN_BIT 3
#define PTRACE_CFI_SS_LOCK_BIT 4 #define PTRACE_CFI_SHADOW_STACK_LOCK_BIT 4
#define PTRACE_CFI_SS_PTR_BIT 5 #define PTRACE_CFI_SHADOW_STACK_PTR_BIT 5
#define PTRACE_CFI_LP_EN_STATE _BITUL(PTRACE_CFI_LP_EN_BIT) #define PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT)
#define PTRACE_CFI_LP_LOCK_STATE _BITUL(PTRACE_CFI_LP_LOCK_BIT) #define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE \
#define PTRACE_CFI_ELP_STATE _BITUL(PTRACE_CFI_ELP_BIT) _BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT)
#define PTRACE_CFI_SS_EN_STATE _BITUL(PTRACE_CFI_SS_EN_BIT) #define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE \
#define PTRACE_CFI_SS_LOCK_STATE _BITUL(PTRACE_CFI_SS_LOCK_BIT) _BITUL(PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT)
#define PTRACE_CFI_SS_PTR_STATE _BITUL(PTRACE_CFI_SS_PTR_BIT) #define PTRACE_CFI_SHADOW_STACK_EN_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_EN_BIT)
#define PTRACE_CFI_SHADOW_STACK_LOCK_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_LOCK_BIT)
#define PTRACE_CFI_SHADOW_STACK_PTR_STATE _BITUL(PTRACE_CFI_SHADOW_STACK_PTR_BIT)
#define PRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_LP_EN_STATE | \ #define PTRACE_CFI_STATE_INVALID_MASK ~(PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \
PTRACE_CFI_LP_LOCK_STATE | \ PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | \
PTRACE_CFI_ELP_STATE | \ PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE | \
PTRACE_CFI_SS_EN_STATE | \ PTRACE_CFI_SHADOW_STACK_EN_STATE | \
PTRACE_CFI_SS_LOCK_STATE | \ PTRACE_CFI_SHADOW_STACK_LOCK_STATE | \
PTRACE_CFI_SS_PTR_STATE) PTRACE_CFI_SHADOW_STACK_PTR_STATE)
struct __cfi_status { struct __cfi_status {
__u64 cfi_state; __u64 cfi_state;

View File

@@ -160,6 +160,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
* clear shadow stack state on exec. * clear shadow stack state on exec.
* libc will set it later via prctl. * libc will set it later via prctl.
*/ */
set_shstk_lock(current, false);
set_shstk_status(current, false); set_shstk_status(current, false);
set_shstk_base(current, 0, 0); set_shstk_base(current, 0, 0);
set_active_shstk(current, 0); set_active_shstk(current, 0);
@@ -167,6 +168,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
* disable indirect branch tracking on exec. * disable indirect branch tracking on exec.
* libc will enable it later via prctl. * libc will enable it later via prctl.
*/ */
set_indir_lp_lock(current, false);
set_indir_lp_status(current, false); set_indir_lp_status(current, false);
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT

View File

@@ -303,18 +303,18 @@ static int riscv_cfi_get(struct task_struct *target,
regs = task_pt_regs(target); regs = task_pt_regs(target);
if (is_indir_lp_enabled(target)) { if (is_indir_lp_enabled(target)) {
user_cfi.cfi_status.cfi_state |= PTRACE_CFI_LP_EN_STATE; user_cfi.cfi_status.cfi_state |= PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE;
user_cfi.cfi_status.cfi_state |= is_indir_lp_locked(target) ? user_cfi.cfi_status.cfi_state |= is_indir_lp_locked(target) ?
PTRACE_CFI_LP_LOCK_STATE : 0; PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE : 0;
user_cfi.cfi_status.cfi_state |= (regs->status & SR_ELP) ? user_cfi.cfi_status.cfi_state |= (regs->status & SR_ELP) ?
PTRACE_CFI_ELP_STATE : 0; PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE : 0;
} }
if (is_shstk_enabled(target)) { if (is_shstk_enabled(target)) {
user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SS_EN_STATE | user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SHADOW_STACK_EN_STATE |
PTRACE_CFI_SS_PTR_STATE); PTRACE_CFI_SHADOW_STACK_PTR_STATE);
user_cfi.cfi_status.cfi_state |= is_shstk_locked(target) ? user_cfi.cfi_status.cfi_state |= is_shstk_locked(target) ?
PTRACE_CFI_SS_LOCK_STATE : 0; PTRACE_CFI_SHADOW_STACK_LOCK_STATE : 0;
user_cfi.shstk_ptr = get_active_shstk(target); user_cfi.shstk_ptr = get_active_shstk(target);
} }
@@ -349,15 +349,15 @@ static int riscv_cfi_set(struct task_struct *target,
* rsvd field should be set to zero so that if those fields are needed in future * rsvd field should be set to zero so that if those fields are needed in future
*/ */
if ((user_cfi.cfi_status.cfi_state & if ((user_cfi.cfi_status.cfi_state &
(PTRACE_CFI_LP_EN_STATE | PTRACE_CFI_LP_LOCK_STATE | (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE |
PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) || PTRACE_CFI_SHADOW_STACK_EN_STATE | PTRACE_CFI_SHADOW_STACK_LOCK_STATE)) ||
(user_cfi.cfi_status.cfi_state & PRACE_CFI_STATE_INVALID_MASK)) (user_cfi.cfi_status.cfi_state & PTRACE_CFI_STATE_INVALID_MASK))
return -EINVAL; return -EINVAL;
/* If lpad is enabled on target and ptrace requests to set / clear elp, do that */ /* If lpad is enabled on target and ptrace requests to set / clear elp, do that */
if (is_indir_lp_enabled(target)) { if (is_indir_lp_enabled(target)) {
if (user_cfi.cfi_status.cfi_state & if (user_cfi.cfi_status.cfi_state &
PTRACE_CFI_ELP_STATE) /* set elp state */ PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE) /* set elp state */
regs->status |= SR_ELP; regs->status |= SR_ELP;
else else
regs->status &= ~SR_ELP; /* clear elp state */ regs->status &= ~SR_ELP; /* clear elp state */
@@ -365,7 +365,7 @@ static int riscv_cfi_set(struct task_struct *target,
/* If shadow stack enabled on target, set new shadow stack pointer */ /* If shadow stack enabled on target, set new shadow stack pointer */
if (is_shstk_enabled(target) && if (is_shstk_enabled(target) &&
(user_cfi.cfi_status.cfi_state & PTRACE_CFI_SS_PTR_STATE)) (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SHADOW_STACK_PTR_STATE))
set_active_shstk(target, user_cfi.shstk_ptr); set_active_shstk(target, user_cfi.shstk_ptr);
return 0; return 0;

View File

@@ -74,9 +74,9 @@ void set_shstk_status(struct task_struct *task, bool enable)
csr_write(CSR_ENVCFG, task->thread.envcfg); csr_write(CSR_ENVCFG, task->thread.envcfg);
} }
void set_shstk_lock(struct task_struct *task) void set_shstk_lock(struct task_struct *task, bool lock)
{ {
task->thread_info.user_cfi_state.ubcfi_locked = 1; task->thread_info.user_cfi_state.ubcfi_locked = lock;
} }
bool is_indir_lp_enabled(struct task_struct *task) bool is_indir_lp_enabled(struct task_struct *task)
@@ -104,9 +104,9 @@ void set_indir_lp_status(struct task_struct *task, bool enable)
csr_write(CSR_ENVCFG, task->thread.envcfg); csr_write(CSR_ENVCFG, task->thread.envcfg);
} }
void set_indir_lp_lock(struct task_struct *task) void set_indir_lp_lock(struct task_struct *task, bool lock)
{ {
task->thread_info.user_cfi_state.ufcfi_locked = 1; task->thread_info.user_cfi_state.ufcfi_locked = lock;
} }
/* /*
* If size is 0, then to be compatible with regular stack we want it to be as big as * If size is 0, then to be compatible with regular stack we want it to be as big as
@@ -452,28 +452,27 @@ int arch_lock_shadow_stack_status(struct task_struct *task,
!is_shstk_enabled(task) || arg != 0) !is_shstk_enabled(task) || arg != 0)
return -EINVAL; return -EINVAL;
set_shstk_lock(task); set_shstk_lock(task, true);
return 0; return 0;
} }
int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) int arch_prctl_get_branch_landing_pad_state(struct task_struct *t,
unsigned long __user *state)
{ {
unsigned long fcfi_status = 0; unsigned long fcfi_status = 0;
if (!is_user_lpad_enabled()) if (!is_user_lpad_enabled())
return -EINVAL; return -EINVAL;
/* indirect branch tracking is enabled on the task or not */ fcfi_status = (is_indir_lp_enabled(t) ? PR_CFI_ENABLE : PR_CFI_DISABLE);
fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0); fcfi_status |= (is_indir_lp_locked(t) ? PR_CFI_LOCK : 0);
return copy_to_user(status, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; return copy_to_user(state, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0;
} }
int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state)
{ {
bool enable_indir_lp = false;
if (!is_user_lpad_enabled()) if (!is_user_lpad_enabled())
return -EINVAL; return -EINVAL;
@@ -481,28 +480,28 @@ int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status)
if (is_indir_lp_locked(t)) if (is_indir_lp_locked(t))
return -EINVAL; return -EINVAL;
/* Reject unknown flags */ if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE)))
if (status & ~PR_INDIR_BR_LP_ENABLE)
return -EINVAL; return -EINVAL;
enable_indir_lp = (status & PR_INDIR_BR_LP_ENABLE); if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE)
set_indir_lp_status(t, enable_indir_lp); return -EINVAL;
set_indir_lp_status(t, !!(state & PR_CFI_ENABLE));
return 0; return 0;
} }
int arch_lock_indir_br_lp_status(struct task_struct *task, int arch_prctl_lock_branch_landing_pad_state(struct task_struct *task)
unsigned long arg)
{ {
/* /*
* If indirect branch tracking is not supported or not enabled on task, * If indirect branch tracking is not supported or not enabled on task,
* nothing to lock here * nothing to lock here
*/ */
if (!is_user_lpad_enabled() || if (!is_user_lpad_enabled() ||
!is_indir_lp_enabled(task) || arg != 0) !is_indir_lp_enabled(task))
return -EINVAL; return -EINVAL;
set_indir_lp_lock(task); set_indir_lp_lock(task, true);
return 0; return 0;
} }

View File

@@ -229,8 +229,8 @@ static inline bool cpu_attack_vector_mitigated(enum cpu_attack_vectors v)
#define smt_mitigations SMT_MITIGATIONS_OFF #define smt_mitigations SMT_MITIGATIONS_OFF
#endif #endif
int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status); int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, unsigned long __user *state);
int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status); int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state);
int arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status); int arch_prctl_lock_branch_landing_pad_state(struct task_struct *t);
#endif /* _LINUX_CPU_H_ */ #endif /* _LINUX_CPU_H_ */

View File

@@ -397,30 +397,23 @@ struct prctl_mm_map {
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01 # define PR_RSEQ_SLICE_EXT_ENABLE 0x01
/* /*
* Get the current indirect branch tracking configuration for the current * Get or set the control flow integrity (CFI) configuration for the
* thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. * current thread.
*
* Some per-thread control flow integrity settings are not yet
* controlled through this prctl(); see for example
* PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS
*/ */
#define PR_GET_INDIR_BR_LP_STATUS 80 #define PR_GET_CFI 80
#define PR_SET_CFI 81
/* /*
* Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will * Forward-edge CFI variants (excluding ARM64 BTI, which has its own
* enable cpu feature for user thread, to track all indirect branches and ensure * prctl()s).
* they land on arch defined landing pad instruction.
* x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction.
* arch64 - If enabled, an indirect branch must land on a BTI instruction.
* riscv - If enabled, an indirect branch must land on an lpad instruction.
* PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect
* branches will no more be tracked by cpu to land on arch defined landing pad
* instruction.
*/ */
#define PR_SET_INDIR_BR_LP_STATUS 81 #define PR_CFI_BRANCH_LANDING_PADS 0
# define PR_INDIR_BR_LP_ENABLE (1UL << 0) /* Return and control values for PR_{GET,SET}_CFI */
# define PR_CFI_ENABLE _BITUL(0)
/* # define PR_CFI_DISABLE _BITUL(1)
* Prevent further changes to the specified indirect branch tracking # define PR_CFI_LOCK _BITUL(2)
* configuration. All bits may be locked via this call, including
* undefined bits.
*/
#define PR_LOCK_INDIR_BR_LP_STATUS 82
#endif /* _LINUX_PRCTL_H */ #endif /* _LINUX_PRCTL_H */

View File

@@ -2388,17 +2388,18 @@ int __weak arch_lock_shadow_stack_status(struct task_struct *t, unsigned long st
return -EINVAL; return -EINVAL;
} }
int __weak arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) int __weak arch_prctl_get_branch_landing_pad_state(struct task_struct *t,
unsigned long __user *state)
{ {
return -EINVAL; return -EINVAL;
} }
int __weak arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) int __weak arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state)
{ {
return -EINVAL; return -EINVAL;
} }
int __weak arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status) int __weak arch_prctl_lock_branch_landing_pad_state(struct task_struct *t)
{ {
return -EINVAL; return -EINVAL;
} }
@@ -2888,20 +2889,23 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
return -EINVAL; return -EINVAL;
error = rseq_slice_extension_prctl(arg2, arg3); error = rseq_slice_extension_prctl(arg2, arg3);
break; break;
case PR_GET_INDIR_BR_LP_STATUS: case PR_GET_CFI:
if (arg3 || arg4 || arg5) if (arg2 != PR_CFI_BRANCH_LANDING_PADS)
return -EINVAL; return -EINVAL;
error = arch_get_indir_br_lp_status(me, (unsigned long __user *)arg2); if (arg4 || arg5)
return -EINVAL;
error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg3);
break; break;
case PR_SET_INDIR_BR_LP_STATUS: case PR_SET_CFI:
if (arg3 || arg4 || arg5) if (arg2 != PR_CFI_BRANCH_LANDING_PADS)
return -EINVAL; return -EINVAL;
error = arch_set_indir_br_lp_status(me, arg2); if (arg4 || arg5)
break;
case PR_LOCK_INDIR_BR_LP_STATUS:
if (arg3 || arg4 || arg5)
return -EINVAL; return -EINVAL;
error = arch_lock_indir_br_lp_status(me, arg2); error = arch_prctl_set_branch_landing_pad_state(me, arg3);
if (error)
break;
if (arg3 & PR_CFI_LOCK && !(arg3 & PR_CFI_DISABLE))
error = arch_prctl_lock_branch_landing_pad_state(me);
break; break;
default: default:
trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5); trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);

View File

@@ -397,30 +397,24 @@ struct prctl_mm_map {
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01 # define PR_RSEQ_SLICE_EXT_ENABLE 0x01
/* /*
* Get the current indirect branch tracking configuration for the current * Get or set the control flow integrity (CFI) configuration for the
* thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. * current thread.
*
* Some per-thread control flow integrity settings are not yet
* controlled through this prctl(); see for example
* PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS
*/ */
#define PR_GET_INDIR_BR_LP_STATUS 80 #define PR_GET_CFI 80
#define PR_SET_CFI 81
/* /*
* Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will * Forward-edge CFI variants (excluding ARM64 BTI, which has its own
* enable cpu feature for user thread, to track all indirect branches and ensure * prctl()s).
* they land on arch defined landing pad instruction.
* x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction.
* arch64 - If enabled, an indirect branch must land on a BTI instruction.
* riscv - If enabled, an indirect branch must land on an lpad instruction.
* PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect
* branches will no more be tracked by cpu to land on arch defined landing pad
* instruction.
*/ */
#define PR_SET_INDIR_BR_LP_STATUS 81 #define PR_CFI_BRANCH_LANDING_PADS 0
# define PR_INDIR_BR_LP_ENABLE (1UL << 0) /* Return and control values for PR_{GET,SET}_CFI */
# define PR_CFI_ENABLE _BITUL(0)
# define PR_CFI_DISABLE _BITUL(1)
# define PR_CFI_LOCK _BITUL(2)
/*
* Prevent further changes to the specified indirect branch tracking
* configuration. All bits may be locked via this call, including
* undefined bits.
*/
#define PR_LOCK_INDIR_BR_LP_STATUS 82
#endif /* _LINUX_PRCTL_H */ #endif /* _LINUX_PRCTL_H */

View File

@@ -94,9 +94,9 @@ bool cfi_ptrace_test(void)
} }
switch (ptrace_test_num) { switch (ptrace_test_num) {
#define CFI_ENABLE_MASK (PTRACE_CFI_LP_EN_STATE | \ #define CFI_ENABLE_MASK (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \
PTRACE_CFI_SS_EN_STATE | \ PTRACE_CFI_SHADOW_STACK_EN_STATE | \
PTRACE_CFI_SS_PTR_STATE) PTRACE_CFI_SHADOW_STACK_PTR_STATE)
case 0: case 0:
if ((cfi_reg.cfi_status.cfi_state & CFI_ENABLE_MASK) != CFI_ENABLE_MASK) if ((cfi_reg.cfi_status.cfi_state & CFI_ENABLE_MASK) != CFI_ENABLE_MASK)
ksft_exit_fail_msg("%s: ptrace_getregset failed, %llu\n", __func__, ksft_exit_fail_msg("%s: ptrace_getregset failed, %llu\n", __func__,
@@ -106,7 +106,8 @@ bool cfi_ptrace_test(void)
__func__); __func__);
break; break;
case 1: case 1:
if (!(cfi_reg.cfi_status.cfi_state & PTRACE_CFI_ELP_STATE)) if (!(cfi_reg.cfi_status.cfi_state &
PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE))
ksft_exit_fail_msg("%s: elp must have been set\n", __func__); ksft_exit_fail_msg("%s: elp must have been set\n", __func__);
/* clear elp state. not interested in anything else */ /* clear elp state. not interested in anything else */
cfi_reg.cfi_status.cfi_state = 0; cfi_reg.cfi_status.cfi_state = 0;
@@ -145,11 +146,11 @@ int main(int argc, char *argv[])
* pads for user mode except lighting up a bit in senvcfg via a prctl. * pads for user mode except lighting up a bit in senvcfg via a prctl.
* Enable landing pad support throughout the execution of the test binary. * Enable landing pad support throughout the execution of the test binary.
*/ */
ret = my_syscall5(__NR_prctl, PR_GET_INDIR_BR_LP_STATUS, &lpad_status, 0, 0, 0); ret = my_syscall5(__NR_prctl, PR_GET_CFI, PR_CFI_BRANCH_LANDING_PADS, &lpad_status, 0, 0);
if (ret) if (ret)
ksft_exit_fail_msg("Get landing pad status failed with %d\n", ret); ksft_exit_fail_msg("Get landing pad status failed with %d\n", ret);
if (!(lpad_status & PR_INDIR_BR_LP_ENABLE)) if (!(lpad_status & PR_CFI_ENABLE))
ksft_exit_fail_msg("Landing pad is not enabled, should be enabled via glibc\n"); 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); ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &ss_status, 0, 0, 0);