2019-11-14 21:02:54 +03:00
/* SPDX-License-Identifier: GPL-2.0 */
2021-01-15 20:09:53 +03:00
/*
* KCSAN watchpoint encoding .
*
* Copyright ( C ) 2019 , Google LLC .
*/
2019-11-14 21:02:54 +03:00
# ifndef _KERNEL_KCSAN_ENCODING_H
# define _KERNEL_KCSAN_ENCODING_H
# include <linux/bits.h>
# include <linux/log2.h>
# include <linux/mm.h>
# include "kcsan.h"
# define SLOT_RANGE PAGE_SIZE
2019-11-20 12:41:43 +03:00
# define INVALID_WATCHPOINT 0
2019-11-14 21:02:54 +03:00
# define CONSUMED_WATCHPOINT 1
/*
* The maximum useful size of accesses for which we set up watchpoints is the
* max range of slots we check on an access .
*/
# define MAX_ENCODABLE_SIZE (SLOT_RANGE * (1 + KCSAN_CHECK_ADJACENT))
/*
* Number of bits we use to store size info .
*/
# define WATCHPOINT_SIZE_BITS bits_per(MAX_ENCODABLE_SIZE)
/*
* This encoding for addresses discards the upper ( 1 for is - write + SIZE_BITS ) ;
* however , most 64 - bit architectures do not use the full 64 - bit address space .
* Also , in order for a false positive to be observable 2 things need to happen :
*
* 1. different addresses but with the same encoded address race ;
* 2. and both map onto the same watchpoint slots ;
*
2020-10-16 06:10:28 +03:00
* Both these are assumed to be very unlikely . However , in case it still
2019-11-14 21:02:54 +03:00
* happens , the report logic will filter out the false positive ( see report . c ) .
*/
2019-11-20 12:41:43 +03:00
# define WATCHPOINT_ADDR_BITS (BITS_PER_LONG-1 - WATCHPOINT_SIZE_BITS)
2019-11-14 21:02:54 +03:00
2020-11-06 12:34:56 +03:00
/* Bitmasks for the encoded watchpoint access information. */
# define WATCHPOINT_WRITE_MASK BIT(BITS_PER_LONG-1)
# define WATCHPOINT_SIZE_MASK GENMASK(BITS_PER_LONG-2, WATCHPOINT_ADDR_BITS)
# define WATCHPOINT_ADDR_MASK GENMASK(WATCHPOINT_ADDR_BITS-1, 0)
static_assert ( WATCHPOINT_ADDR_MASK = = ( 1UL < < WATCHPOINT_ADDR_BITS ) - 1 ) ;
static_assert ( ( WATCHPOINT_WRITE_MASK ^ WATCHPOINT_SIZE_MASK ^ WATCHPOINT_ADDR_MASK ) = = ~ 0UL ) ;
2019-11-14 21:02:54 +03:00
static inline bool check_encodable ( unsigned long addr , size_t size )
{
2020-10-22 14:45:53 +03:00
/*
* While we can encode addrs < PAGE_SIZE , avoid crashing with a NULL
* pointer deref inside KCSAN .
*/
return addr > = PAGE_SIZE & & size < = MAX_ENCODABLE_SIZE ;
2019-11-14 21:02:54 +03:00
}
2019-11-20 12:41:43 +03:00
static inline long
encode_watchpoint ( unsigned long addr , size_t size , bool is_write )
2019-11-14 21:02:54 +03:00
{
return ( long ) ( ( is_write ? WATCHPOINT_WRITE_MASK : 0 ) |
( size < < WATCHPOINT_ADDR_BITS ) |
( addr & WATCHPOINT_ADDR_MASK ) ) ;
}
2020-01-07 19:31:04 +03:00
static __always_inline bool decode_watchpoint ( long watchpoint ,
unsigned long * addr_masked ,
size_t * size ,
bool * is_write )
2019-11-14 21:02:54 +03:00
{
if ( watchpoint = = INVALID_WATCHPOINT | |
watchpoint = = CONSUMED_WATCHPOINT )
return false ;
2019-11-20 12:41:43 +03:00
* addr_masked = ( unsigned long ) watchpoint & WATCHPOINT_ADDR_MASK ;
* size = ( ( unsigned long ) watchpoint & WATCHPOINT_SIZE_MASK ) > > WATCHPOINT_ADDR_BITS ;
* is_write = ! ! ( ( unsigned long ) watchpoint & WATCHPOINT_WRITE_MASK ) ;
2019-11-14 21:02:54 +03:00
return true ;
}
/*
* Return watchpoint slot for an address .
*/
2020-01-07 19:31:04 +03:00
static __always_inline int watchpoint_slot ( unsigned long addr )
2019-11-14 21:02:54 +03:00
{
return ( addr / PAGE_SIZE ) % CONFIG_KCSAN_NUM_WATCHPOINTS ;
}
2020-01-07 19:31:04 +03:00
static __always_inline bool matching_access ( unsigned long addr1 , size_t size1 ,
unsigned long addr2 , size_t size2 )
2019-11-14 21:02:54 +03:00
{
unsigned long end_range1 = addr1 + size1 - 1 ;
unsigned long end_range2 = addr2 + size2 - 1 ;
return addr1 < = end_range2 & & addr2 < = end_range1 ;
}
# endif /* _KERNEL_KCSAN_ENCODING_H */