2018-01-12 20:56:03 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* fail_function . c : Function - based error injection
*/
# include <linux/error-injection.h>
# include <linux/debugfs.h>
# include <linux/fault-inject.h>
# include <linux/kallsyms.h>
# include <linux/kprobes.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
static int fei_kprobe_handler ( struct kprobe * kp , struct pt_regs * regs ) ;
2018-03-12 13:00:49 +03:00
static void fei_post_handler ( struct kprobe * kp , struct pt_regs * regs ,
unsigned long flags )
{
/*
* A dummy post handler is required to prohibit optimizing , because
* jump optimization does not support execution path overriding .
*/
}
2018-01-12 20:56:03 +03:00
struct fei_attr {
struct list_head list ;
struct kprobe kp ;
unsigned long retval ;
} ;
static DEFINE_MUTEX ( fei_lock ) ;
static LIST_HEAD ( fei_attr_list ) ;
static DECLARE_FAULT_ATTR ( fei_fault_attr ) ;
static struct dentry * fei_debugfs_dir ;
static unsigned long adjust_error_retval ( unsigned long addr , unsigned long retv )
{
switch ( get_injectable_error_type ( addr ) ) {
case EI_ETYPE_NULL :
if ( retv ! = 0 )
return 0 ;
break ;
case EI_ETYPE_ERRNO :
if ( retv < ( unsigned long ) - MAX_ERRNO )
return ( unsigned long ) - EINVAL ;
break ;
case EI_ETYPE_ERRNO_NULL :
if ( retv ! = 0 & & retv < ( unsigned long ) - MAX_ERRNO )
return ( unsigned long ) - EINVAL ;
break ;
}
return retv ;
}
static struct fei_attr * fei_attr_new ( const char * sym , unsigned long addr )
{
struct fei_attr * attr ;
attr = kzalloc ( sizeof ( * attr ) , GFP_KERNEL ) ;
if ( attr ) {
attr - > kp . symbol_name = kstrdup ( sym , GFP_KERNEL ) ;
if ( ! attr - > kp . symbol_name ) {
kfree ( attr ) ;
return NULL ;
}
attr - > kp . pre_handler = fei_kprobe_handler ;
2018-03-12 13:00:49 +03:00
attr - > kp . post_handler = fei_post_handler ;
2018-01-12 20:56:03 +03:00
attr - > retval = adjust_error_retval ( addr , 0 ) ;
INIT_LIST_HEAD ( & attr - > list ) ;
}
return attr ;
}
static void fei_attr_free ( struct fei_attr * attr )
{
if ( attr ) {
kfree ( attr - > kp . symbol_name ) ;
kfree ( attr ) ;
}
}
static struct fei_attr * fei_attr_lookup ( const char * sym )
{
struct fei_attr * attr ;
list_for_each_entry ( attr , & fei_attr_list , list ) {
if ( ! strcmp ( attr - > kp . symbol_name , sym ) )
return attr ;
}
return NULL ;
}
static bool fei_attr_is_valid ( struct fei_attr * _attr )
{
struct fei_attr * attr ;
list_for_each_entry ( attr , & fei_attr_list , list ) {
if ( attr = = _attr )
return true ;
}
return false ;
}
static int fei_retval_set ( void * data , u64 val )
{
struct fei_attr * attr = data ;
unsigned long retv = ( unsigned long ) val ;
int err = 0 ;
mutex_lock ( & fei_lock ) ;
/*
* Since this operation can be done after retval file is removed ,
* It is safer to check the attr is still valid before accessing
* its member .
*/
if ( ! fei_attr_is_valid ( attr ) ) {
err = - ENOENT ;
goto out ;
}
if ( attr - > kp . addr ) {
if ( adjust_error_retval ( ( unsigned long ) attr - > kp . addr ,
val ) ! = retv )
err = - EINVAL ;
}
if ( ! err )
attr - > retval = val ;
out :
mutex_unlock ( & fei_lock ) ;
return err ;
}
static int fei_retval_get ( void * data , u64 * val )
{
struct fei_attr * attr = data ;
int err = 0 ;
mutex_lock ( & fei_lock ) ;
/* Here we also validate @attr to ensure it still exists. */
if ( ! fei_attr_is_valid ( attr ) )
err = - ENOENT ;
else
* val = attr - > retval ;
mutex_unlock ( & fei_lock ) ;
return err ;
}
DEFINE_DEBUGFS_ATTRIBUTE ( fei_retval_ops , fei_retval_get , fei_retval_set ,
" %llx \n " ) ;
static int fei_debugfs_add_attr ( struct fei_attr * attr )
{
struct dentry * dir ;
dir = debugfs_create_dir ( attr - > kp . symbol_name , fei_debugfs_dir ) ;
if ( ! dir )
return - ENOMEM ;
if ( ! debugfs_create_file ( " retval " , 0600 , dir , attr , & fei_retval_ops ) ) {
debugfs_remove_recursive ( dir ) ;
return - ENOMEM ;
}
return 0 ;
}
static void fei_debugfs_remove_attr ( struct fei_attr * attr )
{
struct dentry * dir ;
dir = debugfs_lookup ( attr - > kp . symbol_name , fei_debugfs_dir ) ;
2018-10-31 01:04:51 +03:00
debugfs_remove_recursive ( dir ) ;
2018-01-12 20:56:03 +03:00
}
static int fei_kprobe_handler ( struct kprobe * kp , struct pt_regs * regs )
{
struct fei_attr * attr = container_of ( kp , struct fei_attr , kp ) ;
if ( should_fail ( & fei_fault_attr , 1 ) ) {
regs_set_return_value ( regs , attr - > retval ) ;
override_function_with_return ( regs ) ;
return 1 ;
}
return 0 ;
}
NOKPROBE_SYMBOL ( fei_kprobe_handler )
static void * fei_seq_start ( struct seq_file * m , loff_t * pos )
{
mutex_lock ( & fei_lock ) ;
return seq_list_start ( & fei_attr_list , * pos ) ;
}
static void fei_seq_stop ( struct seq_file * m , void * v )
{
mutex_unlock ( & fei_lock ) ;
}
static void * fei_seq_next ( struct seq_file * m , void * v , loff_t * pos )
{
return seq_list_next ( v , & fei_attr_list , pos ) ;
}
static int fei_seq_show ( struct seq_file * m , void * v )
{
struct fei_attr * attr = list_entry ( v , struct fei_attr , list ) ;
2019-03-25 22:32:28 +03:00
seq_printf ( m , " %ps \n " , attr - > kp . addr ) ;
2018-01-12 20:56:03 +03:00
return 0 ;
}
static const struct seq_operations fei_seq_ops = {
. start = fei_seq_start ,
. next = fei_seq_next ,
. stop = fei_seq_stop ,
. show = fei_seq_show ,
} ;
static int fei_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & fei_seq_ops ) ;
}
static void fei_attr_remove ( struct fei_attr * attr )
{
fei_debugfs_remove_attr ( attr ) ;
unregister_kprobe ( & attr - > kp ) ;
list_del ( & attr - > list ) ;
fei_attr_free ( attr ) ;
}
static void fei_attr_remove_all ( void )
{
struct fei_attr * attr , * n ;
list_for_each_entry_safe ( attr , n , & fei_attr_list , list ) {
fei_attr_remove ( attr ) ;
}
}
static ssize_t fei_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * ppos )
{
struct fei_attr * attr ;
unsigned long addr ;
char * buf , * sym ;
int ret ;
/* cut off if it is too long */
if ( count > KSYM_NAME_LEN )
count = KSYM_NAME_LEN ;
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 23:55:00 +03:00
buf = kmalloc ( count + 1 , GFP_KERNEL ) ;
2018-01-12 20:56:03 +03:00
if ( ! buf )
return - ENOMEM ;
if ( copy_from_user ( buf , buffer , count ) ) {
ret = - EFAULT ;
goto out ;
}
buf [ count ] = ' \0 ' ;
sym = strstrip ( buf ) ;
mutex_lock ( & fei_lock ) ;
/* Writing just spaces will remove all injection points */
if ( sym [ 0 ] = = ' \0 ' ) {
fei_attr_remove_all ( ) ;
ret = count ;
goto out ;
}
/* Writing !function will remove one injection point */
if ( sym [ 0 ] = = ' ! ' ) {
attr = fei_attr_lookup ( sym + 1 ) ;
if ( ! attr ) {
ret = - ENOENT ;
goto out ;
}
fei_attr_remove ( attr ) ;
ret = count ;
goto out ;
}
addr = kallsyms_lookup_name ( sym ) ;
if ( ! addr ) {
ret = - EINVAL ;
goto out ;
}
if ( ! within_error_injection_list ( addr ) ) {
ret = - ERANGE ;
goto out ;
}
if ( fei_attr_lookup ( sym ) ) {
ret = - EBUSY ;
goto out ;
}
attr = fei_attr_new ( sym , addr ) ;
if ( ! attr ) {
ret = - ENOMEM ;
goto out ;
}
ret = register_kprobe ( & attr - > kp ) ;
if ( ! ret )
ret = fei_debugfs_add_attr ( attr ) ;
if ( ret < 0 )
fei_attr_remove ( attr ) ;
else {
list_add_tail ( & attr - > list , & fei_attr_list ) ;
ret = count ;
}
out :
kfree ( buf ) ;
mutex_unlock ( & fei_lock ) ;
return ret ;
}
static const struct file_operations fei_ops = {
. open = fei_open ,
. read = seq_read ,
. write = fei_write ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static int __init fei_debugfs_init ( void )
{
struct dentry * dir ;
dir = fault_create_debugfs_attr ( " fail_function " , NULL ,
& fei_fault_attr ) ;
if ( IS_ERR ( dir ) )
return PTR_ERR ( dir ) ;
/* injectable attribute is just a symlink of error_inject/list */
if ( ! debugfs_create_symlink ( " injectable " , dir ,
" ../error_injection/list " ) )
goto error ;
if ( ! debugfs_create_file ( " inject " , 0600 , dir , NULL , & fei_ops ) )
goto error ;
fei_debugfs_dir = dir ;
return 0 ;
error :
debugfs_remove_recursive ( dir ) ;
return - ENOMEM ;
}
late_initcall ( fei_debugfs_init ) ;