2019-01-09 15:43:29 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/livepatch.h>
# include <linux/slab.h>
/*
* Keep a small list of pointers so that we can print address - agnostic
* pointer values . Use a rolling integer count to differentiate the values .
* Ironically we could have used the shadow variable API to do this , but
* let ' s not lean too heavily on the very code we ' re testing .
*/
static LIST_HEAD ( ptr_list ) ;
struct shadow_ptr {
void * ptr ;
int id ;
struct list_head list ;
} ;
static void free_ptr_list ( void )
{
struct shadow_ptr * sp , * tmp_sp ;
list_for_each_entry_safe ( sp , tmp_sp , & ptr_list , list ) {
list_del ( & sp - > list ) ;
kfree ( sp ) ;
}
}
static int ptr_id ( void * ptr )
{
struct shadow_ptr * sp ;
static int count ;
list_for_each_entry ( sp , & ptr_list , list ) {
if ( sp - > ptr = = ptr )
return sp - > id ;
}
sp = kmalloc ( sizeof ( * sp ) , GFP_ATOMIC ) ;
if ( ! sp )
2019-02-04 16:56:51 +03:00
return - ENOMEM ;
2019-01-09 15:43:29 +03:00
sp - > ptr = ptr ;
sp - > id = count + + ;
list_add ( & sp - > list , & ptr_list ) ;
return sp - > id ;
}
/*
* Shadow variable wrapper functions that echo the function and arguments
* to the kernel log for testing verification . Don ' t display raw pointers ,
* but use the ptr_id ( ) value instead .
*/
static void * shadow_get ( void * obj , unsigned long id )
{
2020-01-16 18:31:43 +03:00
int * * sv ;
2019-01-09 15:43:29 +03:00
2020-01-16 18:31:43 +03:00
sv = klp_shadow_get ( obj , id ) ;
2019-01-09 15:43:29 +03:00
pr_info ( " klp_%s(obj=PTR%d, id=0x%lx) = PTR%d \n " ,
2020-01-16 18:31:43 +03:00
__func__ , ptr_id ( obj ) , id , ptr_id ( sv ) ) ;
2019-01-09 15:43:29 +03:00
2020-01-16 18:31:43 +03:00
return sv ;
2019-01-09 15:43:29 +03:00
}
static void * shadow_alloc ( void * obj , unsigned long id , size_t size ,
gfp_t gfp_flags , klp_shadow_ctor_t ctor ,
void * ctor_data )
{
2020-01-16 18:31:44 +03:00
int * * var = ctor_data ;
2020-01-16 18:31:43 +03:00
int * * sv ;
sv = klp_shadow_alloc ( obj , id , size , gfp_flags , ctor , var ) ;
2019-01-09 15:43:29 +03:00
pr_info ( " klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d \n " ,
__func__ , ptr_id ( obj ) , id , size , & gfp_flags , ptr_id ( ctor ) ,
2020-01-16 18:31:44 +03:00
ptr_id ( * var ) , ptr_id ( sv ) ) ;
2020-01-16 18:31:43 +03:00
return sv ;
2019-01-09 15:43:29 +03:00
}
static void * shadow_get_or_alloc ( void * obj , unsigned long id , size_t size ,
gfp_t gfp_flags , klp_shadow_ctor_t ctor ,
void * ctor_data )
{
2020-01-16 18:31:44 +03:00
int * * var = ctor_data ;
2020-01-16 18:31:43 +03:00
int * * sv ;
sv = klp_shadow_get_or_alloc ( obj , id , size , gfp_flags , ctor , var ) ;
2019-01-09 15:43:29 +03:00
pr_info ( " klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d \n " ,
__func__ , ptr_id ( obj ) , id , size , & gfp_flags , ptr_id ( ctor ) ,
2020-01-16 18:31:44 +03:00
ptr_id ( * var ) , ptr_id ( sv ) ) ;
2020-01-16 18:31:43 +03:00
return sv ;
2019-01-09 15:43:29 +03:00
}
static void shadow_free ( void * obj , unsigned long id , klp_shadow_dtor_t dtor )
{
klp_shadow_free ( obj , id , dtor ) ;
pr_info ( " klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d) \n " ,
__func__ , ptr_id ( obj ) , id , ptr_id ( dtor ) ) ;
}
static void shadow_free_all ( unsigned long id , klp_shadow_dtor_t dtor )
{
klp_shadow_free_all ( id , dtor ) ;
2020-06-03 21:20:56 +03:00
pr_info ( " klp_%s(id=0x%lx, dtor=PTR%d) \n " , __func__ , id , ptr_id ( dtor ) ) ;
2019-01-09 15:43:29 +03:00
}
/* Shadow variable constructor - remember simple pointer data */
static int shadow_ctor ( void * obj , void * shadow_data , void * ctor_data )
{
2020-01-16 18:31:43 +03:00
int * * sv = shadow_data ;
2020-01-16 18:31:44 +03:00
int * * var = ctor_data ;
2020-01-16 18:31:43 +03:00
2020-01-16 18:31:44 +03:00
if ( ! var )
return - EINVAL ;
* sv = * var ;
2020-06-03 21:20:56 +03:00
pr_info ( " %s: PTR%d -> PTR%d \n " , __func__ , ptr_id ( sv ) , ptr_id ( * var ) ) ;
2019-01-09 15:43:29 +03:00
return 0 ;
}
2020-06-03 21:20:57 +03:00
/*
* With more than one item to free in the list , order is not determined and
* shadow_dtor will not be passed to shadow_free_all ( ) which would make the
* test fail . ( see pass 6 )
*/
2019-01-09 15:43:29 +03:00
static void shadow_dtor ( void * obj , void * shadow_data )
{
2020-01-16 18:31:43 +03:00
int * * sv = shadow_data ;
2019-01-09 15:43:29 +03:00
pr_info ( " %s(obj=PTR%d, shadow_data=PTR%d) \n " ,
2020-01-16 18:31:43 +03:00
__func__ , ptr_id ( obj ) , ptr_id ( sv ) ) ;
2019-01-09 15:43:29 +03:00
}
2020-06-03 21:20:57 +03:00
/* number of objects we simulate that need shadow vars */
# define NUM_OBJS 3
2020-06-03 21:20:56 +03:00
/* dynamically created obj fields have the following shadow var id values */
# define SV_ID1 0x1234
# define SV_ID2 0x1235
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:56 +03:00
/*
* The main test case adds / removes new fields ( shadow var ) to each of these
* test structure instances . The last group of fields in the struct represent
* the idea that shadow variables may be added and removed to and from the
* struct during execution .
*/
struct test_object {
/* add anything here below and avoid to define an empty struct */
struct shadow_ptr sp ;
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:56 +03:00
/* these represent shadow vars added and removed with SV_ID{1,2} */
/* char nfield1; */
/* int nfield2; */
} ;
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:56 +03:00
static int test_klp_shadow_vars_init ( void )
{
2020-06-03 21:20:57 +03:00
struct test_object objs [ NUM_OBJS ] ;
char nfields1 [ NUM_OBJS ] , * pnfields1 [ NUM_OBJS ] , * * sv1 [ NUM_OBJS ] ;
char * pndup [ NUM_OBJS ] ;
int nfields2 [ NUM_OBJS ] , * pnfields2 [ NUM_OBJS ] , * * sv2 [ NUM_OBJS ] ;
2020-06-03 21:20:56 +03:00
void * * sv ;
2020-06-03 21:20:58 +03:00
int ret ;
2020-06-03 21:20:57 +03:00
int i ;
2020-01-16 18:31:44 +03:00
2019-01-09 15:43:29 +03:00
ptr_id ( NULL ) ;
/*
* With an empty shadow variable hash table , expect not to find
* any matches .
*/
2020-06-03 21:20:57 +03:00
sv = shadow_get ( & objs [ 0 ] , SV_ID1 ) ;
2020-01-16 18:31:43 +03:00
if ( ! sv )
2019-01-09 15:43:29 +03:00
pr_info ( " got expected NULL result \n " ) ;
2020-06-03 21:20:57 +03:00
/* pass 1: init & alloc a char+int pair of svars for each objs */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
pnfields1 [ i ] = & nfields1 [ i ] ;
ptr_id ( pnfields1 [ i ] ) ;
if ( i % 2 ) {
sv1 [ i ] = shadow_alloc ( & objs [ i ] , SV_ID1 ,
sizeof ( pnfields1 [ i ] ) , GFP_KERNEL ,
shadow_ctor , & pnfields1 [ i ] ) ;
} else {
sv1 [ i ] = shadow_get_or_alloc ( & objs [ i ] , SV_ID1 ,
sizeof ( pnfields1 [ i ] ) , GFP_KERNEL ,
shadow_ctor , & pnfields1 [ i ] ) ;
}
2020-06-03 21:20:58 +03:00
if ( ! sv1 [ i ] ) {
ret = - ENOMEM ;
goto out ;
}
2020-06-03 21:20:57 +03:00
pnfields2 [ i ] = & nfields2 [ i ] ;
ptr_id ( pnfields2 [ i ] ) ;
sv2 [ i ] = shadow_alloc ( & objs [ i ] , SV_ID2 , sizeof ( pnfields2 [ i ] ) ,
GFP_KERNEL , shadow_ctor , & pnfields2 [ i ] ) ;
2020-06-03 21:20:58 +03:00
if ( ! sv2 [ i ] ) {
ret = - ENOMEM ;
goto out ;
}
2020-06-03 21:20:57 +03:00
}
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:57 +03:00
/* pass 2: verify we find allocated svars and where they point to */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
/* check the "char" svar for all objects */
sv = shadow_get ( & objs [ i ] , SV_ID1 ) ;
2020-06-03 21:20:58 +03:00
if ( ! sv ) {
ret = - EINVAL ;
goto out ;
}
2020-06-03 21:20:57 +03:00
if ( ( char * * ) sv = = sv1 [ i ] & & * sv1 [ i ] = = pnfields1 [ i ] )
pr_info ( " got expected PTR%d -> PTR%d result \n " ,
ptr_id ( sv1 [ i ] ) , ptr_id ( * sv1 [ i ] ) ) ;
/* check the "int" svar for all objects */
sv = shadow_get ( & objs [ i ] , SV_ID2 ) ;
2020-06-03 21:20:58 +03:00
if ( ! sv ) {
ret = - EINVAL ;
goto out ;
}
2020-06-03 21:20:57 +03:00
if ( ( int * * ) sv = = sv2 [ i ] & & * sv2 [ i ] = = pnfields2 [ i ] )
pr_info ( " got expected PTR%d -> PTR%d result \n " ,
ptr_id ( sv2 [ i ] ) , ptr_id ( * sv2 [ i ] ) ) ;
}
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:57 +03:00
/* pass 3: verify that 'get_or_alloc' returns already allocated svars */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
pndup [ i ] = & nfields1 [ i ] ;
ptr_id ( pndup [ i ] ) ;
sv = shadow_get_or_alloc ( & objs [ i ] , SV_ID1 , sizeof ( pndup [ i ] ) ,
GFP_KERNEL , shadow_ctor , & pndup [ i ] ) ;
2020-06-03 21:20:58 +03:00
if ( ! sv ) {
ret = - EINVAL ;
goto out ;
}
2020-06-03 21:20:57 +03:00
if ( ( char * * ) sv = = sv1 [ i ] & & * sv1 [ i ] = = pnfields1 [ i ] )
pr_info ( " got expected PTR%d -> PTR%d result \n " ,
ptr_id ( sv1 [ i ] ) , ptr_id ( * sv1 [ i ] ) ) ;
}
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:57 +03:00
/* pass 4: free <objs[*], SV_ID1> pairs of svars, verify removal */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
shadow_free ( & objs [ i ] , SV_ID1 , shadow_dtor ) ; /* 'char' pairs */
sv = shadow_get ( & objs [ i ] , SV_ID1 ) ;
if ( ! sv )
pr_info ( " got expected NULL result \n " ) ;
}
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:57 +03:00
/* pass 5: check we still find <objs[*], SV_ID2> svar pairs */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
sv = shadow_get ( & objs [ i ] , SV_ID2 ) ; /* 'int' pairs */
2020-06-03 21:20:58 +03:00
if ( ! sv ) {
ret = - EINVAL ;
goto out ;
}
2020-06-03 21:20:57 +03:00
if ( ( int * * ) sv = = sv2 [ i ] & & * sv2 [ i ] = = pnfields2 [ i ] )
pr_info ( " got expected PTR%d -> PTR%d result \n " ,
ptr_id ( sv2 [ i ] ) , ptr_id ( * sv2 [ i ] ) ) ;
}
2019-01-09 15:43:29 +03:00
2020-06-03 21:20:57 +03:00
/* pass 6: free all the <objs[*], SV_ID2> svar pairs too. */
shadow_free_all ( SV_ID2 , NULL ) ; /* 'int' pairs */
for ( i = 0 ; i < NUM_OBJS ; i + + ) {
sv = shadow_get ( & objs [ i ] , SV_ID2 ) ;
if ( ! sv )
pr_info ( " got expected NULL result \n " ) ;
}
2019-01-09 15:43:29 +03:00
free_ptr_list ( ) ;
return 0 ;
2020-06-03 21:20:58 +03:00
out :
shadow_free_all ( SV_ID1 , NULL ) ; /* 'char' pairs */
shadow_free_all ( SV_ID2 , NULL ) ; /* 'int' pairs */
free_ptr_list ( ) ;
return ret ;
2019-01-09 15:43:29 +03:00
}
static void test_klp_shadow_vars_exit ( void )
{
}
module_init ( test_klp_shadow_vars_init ) ;
module_exit ( test_klp_shadow_vars_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Joe Lawrence <joe.lawrence@redhat.com> " ) ;
MODULE_DESCRIPTION ( " Livepatch test: shadow variables " ) ;