2014-01-24 03:54:38 +04:00
/*
* Kernel module for testing copy_to / from_user infrastructure .
*
* Copyright 2013 Google Inc . All Rights Reserved
*
* Authors :
* Kees Cook < keescook @ chromium . org >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/mman.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <linux/vmalloc.h>
# define test(condition, msg) \
( { \
int cond = ( condition ) ; \
if ( cond ) \
pr_warn ( " %s \n " , msg ) ; \
cond ; \
} )
static int __init test_user_copy_init ( void )
{
int ret = 0 ;
char * kmem ;
char __user * usermem ;
char * bad_usermem ;
unsigned long user_addr ;
unsigned long value = 0x5A ;
kmem = kmalloc ( PAGE_SIZE * 2 , GFP_KERNEL ) ;
if ( ! kmem )
return - ENOMEM ;
user_addr = vm_mmap ( NULL , 0 , PAGE_SIZE * 2 ,
PROT_READ | PROT_WRITE | PROT_EXEC ,
MAP_ANONYMOUS | MAP_PRIVATE , 0 ) ;
if ( user_addr > = ( unsigned long ) ( TASK_SIZE ) ) {
pr_warn ( " Failed to allocate user memory \n " ) ;
kfree ( kmem ) ;
return - ENOMEM ;
}
usermem = ( char __user * ) user_addr ;
bad_usermem = ( char * ) user_addr ;
2017-02-13 22:25:26 +03:00
/*
* Legitimate usage : none of these copies should fail .
*/
2014-01-24 03:54:38 +04:00
ret | = test ( copy_from_user ( kmem , usermem , PAGE_SIZE ) ,
" legitimate copy_from_user failed " ) ;
ret | = test ( copy_to_user ( usermem , kmem , PAGE_SIZE ) ,
" legitimate copy_to_user failed " ) ;
ret | = test ( get_user ( value , ( unsigned long __user * ) usermem ) ,
" legitimate get_user failed " ) ;
ret | = test ( put_user ( value , ( unsigned long __user * ) usermem ) ,
" legitimate put_user failed " ) ;
2017-02-13 22:25:26 +03:00
/*
* Invalid usage : none of these copies should succeed .
*/
/* Prepare kernel memory with check values. */
2017-02-12 09:13:33 +03:00
memset ( kmem , 0x5a , PAGE_SIZE ) ;
memset ( kmem + PAGE_SIZE , 0 , PAGE_SIZE ) ;
2017-02-13 22:25:26 +03:00
/* Reject kernel-to-kernel copies through copy_from_user(). */
2014-01-24 03:54:38 +04:00
ret | = test ( ! copy_from_user ( kmem , ( char __user * ) ( kmem + PAGE_SIZE ) ,
PAGE_SIZE ) ,
" illegal all-kernel copy_from_user passed " ) ;
2017-02-13 22:25:26 +03:00
/* Destination half of buffer should have been zeroed. */
2017-02-12 09:13:33 +03:00
ret | = test ( memcmp ( kmem + PAGE_SIZE , kmem , PAGE_SIZE ) ,
" zeroing failure for illegal all-kernel copy_from_user " ) ;
2017-02-13 22:25:26 +03:00
#if 0
/*
* When running with SMAP / PAN / etc , this will Oops the kernel
* due to the zeroing of userspace memory on failure . This needs
* to be tested in LKDTM instead , since this test module does not
* expect to explode .
*/
2014-01-24 03:54:38 +04:00
ret | = test ( ! copy_from_user ( bad_usermem , ( char __user * ) kmem ,
PAGE_SIZE ) ,
" illegal reversed copy_from_user passed " ) ;
2017-02-13 22:25:26 +03:00
# endif
2014-01-24 03:54:38 +04:00
ret | = test ( ! copy_to_user ( ( char __user * ) kmem , kmem + PAGE_SIZE ,
PAGE_SIZE ) ,
" illegal all-kernel copy_to_user passed " ) ;
ret | = test ( ! copy_to_user ( ( char __user * ) kmem , bad_usermem ,
PAGE_SIZE ) ,
" illegal reversed copy_to_user passed " ) ;
2017-02-13 22:25:26 +03:00
value = 0x5a ;
2014-01-24 03:54:38 +04:00
ret | = test ( ! get_user ( value , ( unsigned long __user * ) kmem ) ,
" illegal get_user passed " ) ;
2017-02-13 22:25:26 +03:00
ret | = test ( value ! = 0 , " zeroing failure for illegal get_user " ) ;
2014-01-24 03:54:38 +04:00
ret | = test ( ! put_user ( value , ( unsigned long __user * ) kmem ) ,
" illegal put_user passed " ) ;
vm_munmap ( user_addr , PAGE_SIZE * 2 ) ;
kfree ( kmem ) ;
if ( ret = = 0 ) {
pr_info ( " tests passed. \n " ) ;
return 0 ;
}
return - EINVAL ;
}
module_init ( test_user_copy_init ) ;
static void __exit test_user_copy_exit ( void )
{
pr_info ( " unloaded. \n " ) ;
}
module_exit ( test_user_copy_exit ) ;
MODULE_AUTHOR ( " Kees Cook <keescook@chromium.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;