2019-09-16 13:51:17 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2020 ARM Ltd .
*/
2020-05-04 16:42:36 +03:00
# include <linux/bitops.h>
2020-03-30 12:29:38 +03:00
# include <linux/kernel.h>
2020-05-04 16:42:36 +03:00
# include <linux/mm.h>
2019-11-27 13:30:15 +03:00
# include <linux/prctl.h>
# include <linux/sched.h>
2020-03-30 12:29:38 +03:00
# include <linux/sched/mm.h>
2019-11-27 12:53:44 +03:00
# include <linux/string.h>
2020-05-13 18:37:50 +03:00
# include <linux/swap.h>
# include <linux/swapops.h>
2019-09-16 13:51:17 +03:00
# include <linux/thread_info.h>
2020-12-22 23:01:28 +03:00
# include <linux/types.h>
2020-03-30 12:29:38 +03:00
# include <linux/uio.h>
2019-09-16 13:51:17 +03:00
2020-12-22 23:01:28 +03:00
# include <asm/barrier.h>
2019-09-16 13:51:17 +03:00
# include <asm/cpufeature.h>
# include <asm/mte.h>
2020-03-30 12:29:38 +03:00
# include <asm/ptrace.h>
2019-09-16 13:51:17 +03:00
# include <asm/sysreg.h>
2020-12-22 23:01:45 +03:00
u64 gcr_kernel_excl __ro_after_init ;
2021-02-24 23:05:26 +03:00
static bool report_fault_once = true ;
2021-03-15 16:20:16 +03:00
# ifdef CONFIG_KASAN_HW_TAGS
2021-03-15 16:20:15 +03:00
/* Whether the MTE asynchronous mode is enabled. */
DEFINE_STATIC_KEY_FALSE ( mte_async_mode ) ;
EXPORT_SYMBOL_GPL ( mte_async_mode ) ;
2021-03-15 16:20:16 +03:00
# endif
2021-03-15 16:20:15 +03:00
2020-05-13 18:37:50 +03:00
static void mte_sync_page_tags ( struct page * page , pte_t * ptep , bool check_swap )
{
pte_t old_pte = READ_ONCE ( * ptep ) ;
if ( check_swap & & is_swap_pte ( old_pte ) ) {
swp_entry_t entry = pte_to_swp_entry ( old_pte ) ;
if ( ! non_swap_entry ( entry ) & & mte_restore_tags ( entry , page ) )
return ;
}
2020-12-22 23:01:31 +03:00
page_kasan_tag_reset ( page ) ;
/*
* We need smp_wmb ( ) in between setting the flags and clearing the
* tags because if another thread reads page - > flags and builds a
* tagged address out of it , there is an actual dependency to the
* memory access , but on the current thread we do not guarantee that
* the new page - > flags are visible before the tags were updated .
*/
smp_wmb ( ) ;
2020-05-13 18:37:50 +03:00
mte_clear_page_tags ( page_address ( page ) ) ;
}
2020-05-04 16:42:36 +03:00
void mte_sync_tags ( pte_t * ptep , pte_t pte )
{
struct page * page = pte_page ( pte ) ;
long i , nr_pages = compound_nr ( page ) ;
2020-05-13 18:37:50 +03:00
bool check_swap = nr_pages = = 1 ;
2020-05-04 16:42:36 +03:00
/* if PG_mte_tagged is set, tags have already been initialised */
for ( i = 0 ; i < nr_pages ; i + + , page + + ) {
if ( ! test_and_set_bit ( PG_mte_tagged , & page - > flags ) )
2020-05-13 18:37:50 +03:00
mte_sync_page_tags ( page , ptep , check_swap ) ;
2020-05-04 16:42:36 +03:00
}
}
2019-11-27 12:53:44 +03:00
int memcmp_pages ( struct page * page1 , struct page * page2 )
{
char * addr1 , * addr2 ;
int ret ;
addr1 = page_address ( page1 ) ;
addr2 = page_address ( page2 ) ;
ret = memcmp ( addr1 , addr2 , PAGE_SIZE ) ;
if ( ! system_supports_mte ( ) | | ret )
return ret ;
/*
* If the page content is identical but at least one of the pages is
* tagged , return non - zero to avoid KSM merging . If only one of the
* pages is tagged , set_pte_at ( ) may zero or change the tags of the
* other page via mte_sync_tags ( ) .
*/
if ( test_bit ( PG_mte_tagged , & page1 - > flags ) | |
test_bit ( PG_mte_tagged , & page2 - > flags ) )
return addr1 ! = addr2 ;
return ret ;
}
2020-12-22 23:01:45 +03:00
void mte_init_tags ( u64 max_tag )
{
static bool gcr_kernel_excl_initialized ;
if ( ! gcr_kernel_excl_initialized ) {
/*
* The format of the tags in KASAN is 0xFF and in MTE is 0xF .
* This conversion extracts an MTE tag from a KASAN tag .
*/
u64 incl = GENMASK ( FIELD_GET ( MTE_TAG_MASK > > MTE_TAG_SHIFT ,
max_tag ) , 0 ) ;
gcr_kernel_excl = ~ incl & SYS_GCR_EL1_EXCL_MASK ;
gcr_kernel_excl_initialized = true ;
}
/* Enable the kernel exclude mask for random tags generation. */
write_sysreg_s ( SYS_GCR_EL1_RRND | gcr_kernel_excl , SYS_GCR_EL1 ) ;
}
2021-03-15 16:20:11 +03:00
static inline void __mte_enable_kernel ( const char * mode , unsigned long tcf )
2020-12-22 23:01:38 +03:00
{
/* Enable MTE Sync Mode for EL1. */
2021-03-15 16:20:11 +03:00
sysreg_clear_set ( sctlr_el1 , SCTLR_ELx_TCF_MASK , tcf ) ;
2020-12-22 23:01:38 +03:00
isb ( ) ;
2021-03-15 16:20:11 +03:00
pr_info_once ( " MTE: enabled in %s mode at EL1 \n " , mode ) ;
}
2021-03-15 16:20:16 +03:00
# ifdef CONFIG_KASAN_HW_TAGS
2021-03-15 16:20:11 +03:00
void mte_enable_kernel_sync ( void )
{
2021-03-15 16:20:15 +03:00
/*
* Make sure we enter this function when no PE has set
* async mode previously .
*/
WARN_ONCE ( system_uses_mte_async_mode ( ) ,
" MTE async mode enabled system wide! " ) ;
2021-03-15 16:20:11 +03:00
__mte_enable_kernel ( " synchronous " , SCTLR_ELx_TCF_SYNC ) ;
}
void mte_enable_kernel_async ( void )
{
__mte_enable_kernel ( " asynchronous " , SCTLR_ELx_TCF_ASYNC ) ;
2021-03-15 16:20:15 +03:00
/*
* MTE async mode is set system wide by the first PE that
* executes this function .
*
* Note : If in future KASAN acquires a runtime switching
* mode in between sync and async , this strategy needs
* to be reviewed .
*/
if ( ! system_uses_mte_async_mode ( ) )
static_branch_enable ( & mte_async_mode ) ;
2020-12-22 23:01:38 +03:00
}
2021-03-15 16:20:16 +03:00
# endif
2020-12-22 23:01:38 +03:00
2021-02-24 23:05:26 +03:00
void mte_set_report_once ( bool state )
{
WRITE_ONCE ( report_fault_once , state ) ;
}
bool mte_report_once ( void )
{
return READ_ONCE ( report_fault_once ) ;
}
2021-03-15 16:20:17 +03:00
# ifdef CONFIG_KASAN_HW_TAGS
void mte_check_tfsr_el1 ( void )
{
u64 tfsr_el1 ;
if ( ! system_supports_mte ( ) )
return ;
tfsr_el1 = read_sysreg_s ( SYS_TFSR_EL1 ) ;
if ( unlikely ( tfsr_el1 & SYS_TFSR_EL1_TF1 ) ) {
/*
* Note : isb ( ) is not required after this direct write
* because there is no indirect read subsequent to it
* ( per ARM DDI 0487F . c table D13 - 1 ) .
*/
write_sysreg_s ( 0 , SYS_TFSR_EL1 ) ;
kasan_report_async ( ) ;
}
}
# endif
2020-12-22 23:01:42 +03:00
static void update_gcr_el1_excl ( u64 excl )
2019-12-10 14:19:15 +03:00
{
/*
2020-12-22 23:01:42 +03:00
* Note that the mask controlled by the user via prctl ( ) is an
* include while GCR_EL1 accepts an exclude mask .
2019-12-10 14:19:15 +03:00
* No need for ISB since this only affects EL0 currently , implicit
* with ERET .
*/
sysreg_clear_set_s ( SYS_GCR_EL1 , SYS_GCR_EL1_EXCL_MASK , excl ) ;
}
2020-12-22 23:01:42 +03:00
static void set_gcr_el1_excl ( u64 excl )
2019-12-10 14:19:15 +03:00
{
2020-12-22 23:01:42 +03:00
current - > thread . gcr_user_excl = excl ;
2020-12-22 23:01:45 +03:00
/*
* SYS_GCR_EL1 will be set to current - > thread . gcr_user_excl value
* by mte_set_user_gcr ( ) in kernel_exit ,
*/
2019-12-10 14:19:15 +03:00
}
2021-03-19 06:10:53 +03:00
void mte_thread_init_user ( void )
2019-09-16 13:51:17 +03:00
{
if ( ! system_supports_mte ( ) )
return ;
/* clear any pending asynchronous tag fault */
dsb ( ish ) ;
write_sysreg_s ( 0 , SYS_TFSRE0_EL1 ) ;
clear_thread_flag ( TIF_MTE_ASYNC_FAULT ) ;
2019-11-27 13:30:15 +03:00
/* disable tag checking */
2021-03-19 06:10:52 +03:00
set_task_sctlr_el1 ( ( current - > thread . sctlr_user & ~ SCTLR_EL1_TCF0_MASK ) |
SCTLR_EL1_TCF0_NONE ) ;
2019-12-10 14:19:15 +03:00
/* reset tag generation mask */
2020-12-22 23:01:42 +03:00
set_gcr_el1_excl ( SYS_GCR_EL1_EXCL_MASK ) ;
2019-11-27 13:30:15 +03:00
}
void mte_thread_switch ( struct task_struct * next )
{
2021-03-15 16:20:17 +03:00
/*
* Check if an async tag exception occurred at EL1 .
*
* Note : On the context switch path we rely on the dsb ( ) present
* in __switch_to ( ) to guarantee that the indirect writes to TFSR_EL1
* are synchronized before this point .
*/
2021-03-19 06:10:52 +03:00
isb ( ) ;
2021-03-15 16:20:17 +03:00
mte_check_tfsr_el1 ( ) ;
2019-11-27 13:30:15 +03:00
}
2021-03-15 16:20:18 +03:00
void mte_suspend_enter ( void )
{
if ( ! system_supports_mte ( ) )
return ;
/*
* The barriers are required to guarantee that the indirect writes
* to TFSR_EL1 are synchronized before we report the state .
*/
dsb ( nsh ) ;
isb ( ) ;
/* Report SYS_TFSR_EL1 before suspend entry */
mte_check_tfsr_el1 ( ) ;
}
2020-04-17 20:29:35 +03:00
void mte_suspend_exit ( void )
{
if ( ! system_supports_mte ( ) )
return ;
2020-12-22 23:01:45 +03:00
update_gcr_el1_excl ( gcr_kernel_excl ) ;
2020-04-17 20:29:35 +03:00
}
2020-07-03 16:25:50 +03:00
long set_mte_ctrl ( struct task_struct * task , unsigned long arg )
2019-11-27 13:30:15 +03:00
{
2021-03-19 06:10:52 +03:00
u64 sctlr = task - > thread . sctlr_user & ~ SCTLR_EL1_TCF0_MASK ;
2020-12-22 23:01:42 +03:00
u64 gcr_excl = ~ ( ( arg & PR_MTE_TAG_MASK ) > > PR_MTE_TAG_SHIFT ) &
SYS_GCR_EL1_EXCL_MASK ;
2019-11-27 13:30:15 +03:00
if ( ! system_supports_mte ( ) )
return 0 ;
switch ( arg & PR_MTE_TCF_MASK ) {
case PR_MTE_TCF_NONE :
2021-03-19 06:10:52 +03:00
sctlr | = SCTLR_EL1_TCF0_NONE ;
2019-11-27 13:30:15 +03:00
break ;
case PR_MTE_TCF_SYNC :
2021-03-19 06:10:52 +03:00
sctlr | = SCTLR_EL1_TCF0_SYNC ;
2019-11-27 13:30:15 +03:00
break ;
case PR_MTE_TCF_ASYNC :
2021-03-19 06:10:52 +03:00
sctlr | = SCTLR_EL1_TCF0_ASYNC ;
2019-11-27 13:30:15 +03:00
break ;
default :
return - EINVAL ;
}
2020-07-03 16:25:50 +03:00
if ( task ! = current ) {
2021-03-19 06:10:52 +03:00
task - > thread . sctlr_user = sctlr ;
2020-12-22 23:01:42 +03:00
task - > thread . gcr_user_excl = gcr_excl ;
2020-07-03 16:25:50 +03:00
} else {
2021-03-19 06:10:52 +03:00
set_task_sctlr_el1 ( sctlr ) ;
2020-12-22 23:01:42 +03:00
set_gcr_el1_excl ( gcr_excl ) ;
2020-07-03 16:25:50 +03:00
}
2019-11-27 13:30:15 +03:00
return 0 ;
}
2020-07-03 16:25:50 +03:00
long get_mte_ctrl ( struct task_struct * task )
2019-11-27 13:30:15 +03:00
{
2019-12-10 14:19:15 +03:00
unsigned long ret ;
2020-12-22 23:01:42 +03:00
u64 incl = ~ task - > thread . gcr_user_excl & SYS_GCR_EL1_EXCL_MASK ;
2019-12-10 14:19:15 +03:00
2019-11-27 13:30:15 +03:00
if ( ! system_supports_mte ( ) )
return 0 ;
2020-12-22 23:01:42 +03:00
ret = incl < < PR_MTE_TAG_SHIFT ;
2019-12-10 14:19:15 +03:00
2021-03-19 06:10:52 +03:00
switch ( task - > thread . sctlr_user & SCTLR_EL1_TCF0_MASK ) {
2019-11-27 13:30:15 +03:00
case SCTLR_EL1_TCF0_NONE :
2020-12-03 10:51:10 +03:00
ret | = PR_MTE_TCF_NONE ;
break ;
2019-11-27 13:30:15 +03:00
case SCTLR_EL1_TCF0_SYNC :
2019-12-10 14:19:15 +03:00
ret | = PR_MTE_TCF_SYNC ;
break ;
2019-11-27 13:30:15 +03:00
case SCTLR_EL1_TCF0_ASYNC :
2019-12-10 14:19:15 +03:00
ret | = PR_MTE_TCF_ASYNC ;
break ;
2019-11-27 13:30:15 +03:00
}
2019-12-10 14:19:15 +03:00
return ret ;
2019-09-16 13:51:17 +03:00
}
2020-03-30 12:29:38 +03:00
/*
* Access MTE tags in another process ' address space as given in mm . Update
* the number of tags copied . Return 0 if any tags copied , error otherwise .
* Inspired by __access_remote_vm ( ) .
*/
static int __access_remote_tags ( struct mm_struct * mm , unsigned long addr ,
struct iovec * kiov , unsigned int gup_flags )
{
struct vm_area_struct * vma ;
void __user * buf = kiov - > iov_base ;
size_t len = kiov - > iov_len ;
int ret ;
int write = gup_flags & FOLL_WRITE ;
if ( ! access_ok ( buf , len ) )
return - EFAULT ;
if ( mmap_read_lock_killable ( mm ) )
return - EIO ;
while ( len ) {
unsigned long tags , offset ;
void * maddr ;
struct page * page = NULL ;
ret = get_user_pages_remote ( mm , addr , 1 , gup_flags , & page ,
& vma , NULL ) ;
if ( ret < = 0 )
break ;
/*
* Only copy tags if the page has been mapped as PROT_MTE
* ( PG_mte_tagged set ) . Otherwise the tags are not valid and
* not accessible to user . Moreover , an mprotect ( PROT_MTE )
* would cause the existing tags to be cleared if the page
* was never mapped with PROT_MTE .
*/
2021-02-10 21:03:16 +03:00
if ( ! ( vma - > vm_flags & VM_MTE ) ) {
2020-03-30 12:29:38 +03:00
ret = - EOPNOTSUPP ;
put_page ( page ) ;
break ;
}
2021-02-10 21:03:16 +03:00
WARN_ON_ONCE ( ! test_bit ( PG_mte_tagged , & page - > flags ) ) ;
2020-03-30 12:29:38 +03:00
/* limit access to the end of the page */
offset = offset_in_page ( addr ) ;
tags = min ( len , ( PAGE_SIZE - offset ) / MTE_GRANULE_SIZE ) ;
maddr = page_address ( page ) ;
if ( write ) {
tags = mte_copy_tags_from_user ( maddr + offset , buf , tags ) ;
set_page_dirty_lock ( page ) ;
} else {
tags = mte_copy_tags_to_user ( buf , maddr + offset , tags ) ;
}
put_page ( page ) ;
/* error accessing the tracer's buffer */
if ( ! tags )
break ;
len - = tags ;
buf + = tags ;
addr + = tags * MTE_GRANULE_SIZE ;
}
mmap_read_unlock ( mm ) ;
/* return an error if no tags copied */
kiov - > iov_len = buf - kiov - > iov_base ;
if ( ! kiov - > iov_len ) {
/* check for error accessing the tracee's address space */
if ( ret < = 0 )
return - EIO ;
else
return - EFAULT ;
}
return 0 ;
}
/*
* Copy MTE tags in another process ' address space at ' addr ' to / from tracer ' s
* iovec buffer . Return 0 on success . Inspired by ptrace_access_vm ( ) .
*/
static int access_remote_tags ( struct task_struct * tsk , unsigned long addr ,
struct iovec * kiov , unsigned int gup_flags )
{
struct mm_struct * mm ;
int ret ;
mm = get_task_mm ( tsk ) ;
if ( ! mm )
return - EPERM ;
if ( ! tsk - > ptrace | | ( current ! = tsk - > parent ) | |
( ( get_dumpable ( mm ) ! = SUID_DUMP_USER ) & &
! ptracer_capable ( tsk , mm - > user_ns ) ) ) {
mmput ( mm ) ;
return - EPERM ;
}
ret = __access_remote_tags ( mm , addr , kiov , gup_flags ) ;
mmput ( mm ) ;
return ret ;
}
int mte_ptrace_copy_tags ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
{
int ret ;
struct iovec kiov ;
struct iovec __user * uiov = ( void __user * ) data ;
unsigned int gup_flags = FOLL_FORCE ;
if ( ! system_supports_mte ( ) )
return - EIO ;
if ( get_user ( kiov . iov_base , & uiov - > iov_base ) | |
get_user ( kiov . iov_len , & uiov - > iov_len ) )
return - EFAULT ;
if ( request = = PTRACE_POKEMTETAGS )
gup_flags | = FOLL_WRITE ;
/* align addr to the MTE tag granule */
addr & = MTE_GRANULE_MASK ;
ret = access_remote_tags ( child , addr , & kiov , gup_flags ) ;
if ( ! ret )
ret = put_user ( kiov . iov_len , & uiov - > iov_len ) ;
return ret ;
}