2019-11-14 21:02:54 +03:00
// SPDX-License-Identifier: GPL-2.0
2020-07-31 11:17:23 +03:00
# define pr_fmt(fmt) "kcsan: " fmt
2019-11-14 21:02:54 +03:00
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/printk.h>
# include <linux/random.h>
# include <linux/types.h>
# include "encoding.h"
# define ITERS_PER_TEST 2000
/* Test requirements. */
static bool test_requires ( void )
{
/* random should be initialized for the below tests */
return prandom_u32 ( ) + prandom_u32 ( ) ! = 0 ;
}
/*
* Test watchpoint encode and decode : check that encoding some access ' s info ,
* and then subsequent decode preserves the access ' s info .
*/
static bool test_encode_decode ( void )
{
int i ;
for ( i = 0 ; i < ITERS_PER_TEST ; + + i ) {
size_t size = prandom_u32_max ( MAX_ENCODABLE_SIZE ) + 1 ;
bool is_write = ! ! prandom_u32_max ( 2 ) ;
unsigned long addr ;
prandom_bytes ( & addr , sizeof ( addr ) ) ;
2020-10-22 14:45:52 +03:00
if ( addr < PAGE_SIZE )
addr = PAGE_SIZE ;
2019-11-14 21:02:54 +03:00
if ( WARN_ON ( ! check_encodable ( addr , size ) ) )
return false ;
2019-11-20 12:41:43 +03:00
/* Encode and decode */
2019-11-14 21:02:54 +03:00
{
const long encoded_watchpoint =
encode_watchpoint ( addr , size , is_write ) ;
unsigned long verif_masked_addr ;
size_t verif_size ;
bool verif_is_write ;
2019-11-20 12:41:43 +03:00
/* Check special watchpoints */
2019-11-14 21:02:54 +03:00
if ( WARN_ON ( decode_watchpoint (
INVALID_WATCHPOINT , & verif_masked_addr ,
& verif_size , & verif_is_write ) ) )
return false ;
if ( WARN_ON ( decode_watchpoint (
CONSUMED_WATCHPOINT , & verif_masked_addr ,
& verif_size , & verif_is_write ) ) )
return false ;
2019-11-20 12:41:43 +03:00
/* Check decoding watchpoint returns same data */
2019-11-14 21:02:54 +03:00
if ( WARN_ON ( ! decode_watchpoint (
encoded_watchpoint , & verif_masked_addr ,
& verif_size , & verif_is_write ) ) )
return false ;
if ( WARN_ON ( verif_masked_addr ! =
( addr & WATCHPOINT_ADDR_MASK ) ) )
goto fail ;
if ( WARN_ON ( verif_size ! = size ) )
goto fail ;
if ( WARN_ON ( is_write ! = verif_is_write ) )
goto fail ;
continue ;
fail :
pr_err ( " %s fail: %s %zu bytes @ %lx -> encoded: %lx -> %s %zu bytes @ %lx \n " ,
__func__ , is_write ? " write " : " read " , size ,
addr , encoded_watchpoint ,
verif_is_write ? " write " : " read " , verif_size ,
verif_masked_addr ) ;
return false ;
}
}
return true ;
}
/* Test access matching function. */
static bool test_matching_access ( void )
{
if ( WARN_ON ( ! matching_access ( 10 , 1 , 10 , 1 ) ) )
return false ;
if ( WARN_ON ( ! matching_access ( 10 , 2 , 11 , 1 ) ) )
return false ;
if ( WARN_ON ( ! matching_access ( 10 , 1 , 9 , 2 ) ) )
return false ;
if ( WARN_ON ( matching_access ( 10 , 1 , 11 , 1 ) ) )
return false ;
if ( WARN_ON ( matching_access ( 9 , 1 , 10 , 1 ) ) )
return false ;
2020-02-05 13:14:19 +03:00
/*
* An access of size 0 could match another access , as demonstrated here .
* Rather than add more comparisons to ' matching_access ( ) ' , which would
* end up in the fast - path for * all * checks , check_access ( ) simply
* returns for all accesses of size 0.
*/
if ( WARN_ON ( ! matching_access ( 8 , 8 , 12 , 0 ) ) )
return false ;
2019-11-14 21:02:54 +03:00
return true ;
}
static int __init kcsan_selftest ( void )
{
int passed = 0 ;
int total = 0 ;
# define RUN_TEST(do_test) \
do { \
+ + total ; \
if ( do_test ( ) ) \
+ + passed ; \
else \
2020-07-31 11:17:23 +03:00
pr_err ( " selftest: " # do_test " failed " ) ; \
2019-11-14 21:02:54 +03:00
} while ( 0 )
RUN_TEST ( test_requires ) ;
RUN_TEST ( test_encode_decode ) ;
RUN_TEST ( test_matching_access ) ;
2020-07-31 11:17:23 +03:00
pr_info ( " selftest: %d/%d tests passed \n " , passed , total ) ;
2019-11-14 21:02:54 +03:00
if ( passed ! = total )
2020-07-31 11:17:23 +03:00
panic ( " selftests failed " ) ;
2019-11-14 21:02:54 +03:00
return 0 ;
}
postcore_initcall ( kcsan_selftest ) ;