2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-04-17 22:05:36 +04:00
/*
2020-06-09 07:34:11 +03:00
* Access kernel or user memory without faulting .
2008-04-17 22:05:36 +04:00
*/
2011-10-16 10:01:52 +04:00
# include <linux/export.h>
2008-04-17 22:05:36 +04:00
# include <linux/mm.h>
2010-10-27 20:29:01 +04:00
# include <linux/uaccess.h>
2008-04-17 22:05:36 +04:00
2020-06-17 10:37:53 +03:00
bool __weak copy_from_kernel_nofault_allowed ( const void * unsafe_src ,
size_t size )
2020-06-09 07:34:27 +03:00
{
return true ;
}
2020-06-09 07:34:58 +03:00
# ifdef HAVE_GET_KERNEL_NOFAULT
2020-06-17 10:37:53 +03:00
# define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \
2020-06-09 07:34:58 +03:00
while ( len > = sizeof ( type ) ) { \
__get_kernel_nofault ( dst , src , type , err_label ) ; \
dst + = sizeof ( type ) ; \
src + = sizeof ( type ) ; \
len - = sizeof ( type ) ; \
}
2020-06-17 10:37:53 +03:00
long copy_from_kernel_nofault ( void * dst , const void * src , size_t size )
2020-06-09 07:34:58 +03:00
{
2020-06-17 10:37:53 +03:00
if ( ! copy_from_kernel_nofault_allowed ( src , size ) )
2020-06-09 07:35:04 +03:00
return - ERANGE ;
2020-06-09 07:34:58 +03:00
pagefault_disable ( ) ;
2020-06-17 10:37:53 +03:00
copy_from_kernel_nofault_loop ( dst , src , size , u64 , Efault ) ;
copy_from_kernel_nofault_loop ( dst , src , size , u32 , Efault ) ;
copy_from_kernel_nofault_loop ( dst , src , size , u16 , Efault ) ;
copy_from_kernel_nofault_loop ( dst , src , size , u8 , Efault ) ;
2020-06-09 07:34:58 +03:00
pagefault_enable ( ) ;
return 0 ;
Efault :
pagefault_enable ( ) ;
return - EFAULT ;
}
2020-06-17 10:37:53 +03:00
EXPORT_SYMBOL_GPL ( copy_from_kernel_nofault ) ;
2020-06-09 07:34:58 +03:00
2020-06-17 10:37:53 +03:00
# define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \
2020-06-09 07:34:58 +03:00
while ( len > = sizeof ( type ) ) { \
__put_kernel_nofault ( dst , src , type , err_label ) ; \
dst + = sizeof ( type ) ; \
src + = sizeof ( type ) ; \
len - = sizeof ( type ) ; \
}
2020-06-17 10:37:53 +03:00
long copy_to_kernel_nofault ( void * dst , const void * src , size_t size )
2020-06-09 07:34:58 +03:00
{
pagefault_disable ( ) ;
2020-06-17 10:37:53 +03:00
copy_to_kernel_nofault_loop ( dst , src , size , u64 , Efault ) ;
copy_to_kernel_nofault_loop ( dst , src , size , u32 , Efault ) ;
copy_to_kernel_nofault_loop ( dst , src , size , u16 , Efault ) ;
copy_to_kernel_nofault_loop ( dst , src , size , u8 , Efault ) ;
2020-06-09 07:34:58 +03:00
pagefault_enable ( ) ;
return 0 ;
Efault :
pagefault_enable ( ) ;
return - EFAULT ;
}
long strncpy_from_kernel_nofault ( char * dst , const void * unsafe_addr , long count )
{
const void * src = unsafe_addr ;
if ( unlikely ( count < = 0 ) )
return 0 ;
2020-06-17 10:37:53 +03:00
if ( ! copy_from_kernel_nofault_allowed ( unsafe_addr , count ) )
2020-06-09 07:35:04 +03:00
return - ERANGE ;
2020-06-09 07:34:58 +03:00
pagefault_disable ( ) ;
do {
__get_kernel_nofault ( dst , src , u8 , Efault ) ;
dst + + ;
src + + ;
} while ( dst [ - 1 ] & & src - unsafe_addr < count ) ;
pagefault_enable ( ) ;
dst [ - 1 ] = ' \0 ' ;
return src - unsafe_addr ;
Efault :
pagefault_enable ( ) ;
dst [ - 1 ] = ' \0 ' ;
return - EFAULT ;
}
# else /* HAVE_GET_KERNEL_NOFAULT */
2008-04-17 22:05:36 +04:00
/**
2020-06-17 10:37:53 +03:00
* copy_from_kernel_nofault ( ) : safely attempt to read from kernel - space
2020-06-09 07:34:07 +03:00
* @ dst : pointer to the buffer that shall take the data
* @ src : address to read from
* @ size : size of the data chunk
*
* Safely read from kernel address @ src to the buffer at @ dst . If a kernel
2020-06-09 07:35:04 +03:00
* fault happens , handle that and return - EFAULT . If @ src is not a valid kernel
* address , return - ERANGE .
2015-11-06 05:46:03 +03:00
*
* We ensure that the copy_from_user is executed in atomic context so that
2020-06-09 07:33:54 +03:00
* do_page_fault ( ) doesn ' t attempt to take mmap_lock . This makes
2020-06-17 10:37:53 +03:00
* copy_from_kernel_nofault ( ) suitable for use within regions where the caller
2020-06-09 07:33:54 +03:00
* already holds mmap_lock , or other locks which nest inside mmap_lock .
2008-04-17 22:05:36 +04:00
*/
2020-06-17 10:37:53 +03:00
long copy_from_kernel_nofault ( void * dst , const void * src , size_t size )
2008-04-17 22:05:36 +04:00
{
long ret ;
2008-02-20 22:33:38 +03:00
mm_segment_t old_fs = get_fs ( ) ;
2008-04-17 22:05:36 +04:00
2020-06-17 10:37:53 +03:00
if ( ! copy_from_kernel_nofault_allowed ( src , size ) )
2020-06-09 07:35:04 +03:00
return - ERANGE ;
2020-06-09 07:34:27 +03:00
2008-02-20 22:33:38 +03:00
set_fs ( KERNEL_DS ) ;
2020-06-09 07:34:24 +03:00
pagefault_disable ( ) ;
ret = __copy_from_user_inatomic ( dst , ( __force const void __user * ) src ,
size ) ;
pagefault_enable ( ) ;
2008-02-20 22:33:38 +03:00
set_fs ( old_fs ) ;
2008-04-17 22:05:36 +04:00
2020-06-09 07:34:24 +03:00
if ( ret )
return - EFAULT ;
return 0 ;
2008-04-17 22:05:36 +04:00
}
2020-06-17 10:37:53 +03:00
EXPORT_SYMBOL_GPL ( copy_from_kernel_nofault ) ;
2008-04-17 22:05:36 +04:00
/**
2020-06-17 10:37:53 +03:00
* copy_to_kernel_nofault ( ) : safely attempt to write to a location
2008-04-17 22:05:36 +04:00
* @ dst : address to write to
* @ src : pointer to the data that shall be written
* @ size : size of the data chunk
*
* Safely write to address @ dst from the buffer at @ src . If a kernel fault
* happens , handle that and return - EFAULT .
*/
2020-06-17 10:37:53 +03:00
long copy_to_kernel_nofault ( void * dst , const void * src , size_t size )
2008-04-17 22:05:36 +04:00
{
long ret ;
2008-02-20 22:33:38 +03:00
mm_segment_t old_fs = get_fs ( ) ;
2008-04-17 22:05:36 +04:00
2008-02-20 22:33:38 +03:00
set_fs ( KERNEL_DS ) ;
2020-06-09 07:34:24 +03:00
pagefault_disable ( ) ;
ret = __copy_to_user_inatomic ( ( __force void __user * ) dst , src , size ) ;
pagefault_enable ( ) ;
2008-02-20 22:33:38 +03:00
set_fs ( old_fs ) ;
2008-04-17 22:05:36 +04:00
2020-06-09 07:34:24 +03:00
if ( ret )
return - EFAULT ;
return 0 ;
2008-04-17 22:05:36 +04:00
}
2015-08-31 18:57:10 +03:00
2020-06-09 07:34:07 +03:00
/**
2020-06-09 07:34:17 +03:00
* strncpy_from_kernel_nofault : - Copy a NUL terminated string from unsafe
2020-06-09 07:34:07 +03:00
* address .
* @ dst : Destination address , in kernel space . This buffer must be at
* least @ count bytes long .
* @ unsafe_addr : Unsafe address .
* @ count : Maximum number of bytes to copy , including the trailing NUL .
*
* Copies a NUL - terminated string from unsafe address to kernel buffer .
*
* On success , returns the length of the string INCLUDING the trailing NUL .
*
2020-06-09 07:35:04 +03:00
* If access fails , returns - EFAULT ( some data may have been copied and the
* trailing NUL added ) . If @ unsafe_addr is not a valid kernel address , return
* - ERANGE .
2020-06-09 07:34:07 +03:00
*
* If @ count is smaller than the length of the string , copies @ count - 1 bytes ,
* sets the last byte of @ dst buffer to NUL and returns @ count .
*/
2020-06-09 07:34:27 +03:00
long strncpy_from_kernel_nofault ( char * dst , const void * unsafe_addr , long count )
2015-08-31 18:57:10 +03:00
{
mm_segment_t old_fs = get_fs ( ) ;
const void * src = unsafe_addr ;
long ret ;
if ( unlikely ( count < = 0 ) )
return 0 ;
2020-06-17 10:37:53 +03:00
if ( ! copy_from_kernel_nofault_allowed ( unsafe_addr , count ) )
2020-06-09 07:35:04 +03:00
return - ERANGE ;
2015-08-31 18:57:10 +03:00
set_fs ( KERNEL_DS ) ;
pagefault_disable ( ) ;
do {
2016-05-23 03:21:27 +03:00
ret = __get_user ( * dst + + , ( const char __user __force * ) src + + ) ;
2015-08-31 18:57:10 +03:00
} while ( dst [ - 1 ] & & ret = = 0 & & src - unsafe_addr < count ) ;
dst [ - 1 ] = ' \0 ' ;
pagefault_enable ( ) ;
set_fs ( old_fs ) ;
2015-11-06 05:50:11 +03:00
return ret ? - EFAULT : src - unsafe_addr ;
2015-08-31 18:57:10 +03:00
}
2020-06-09 07:34:58 +03:00
# endif /* HAVE_GET_KERNEL_NOFAULT */
2019-05-15 08:38:18 +03:00
2020-06-09 07:34:55 +03:00
/**
2020-06-17 10:37:54 +03:00
* copy_from_user_nofault ( ) : safely attempt to read from a user - space location
2020-06-09 07:34:55 +03:00
* @ dst : pointer to the buffer that shall take the data
* @ src : address to read from . This must be a user address .
* @ size : size of the data chunk
*
* Safely read from user address @ src to the buffer at @ dst . If a kernel fault
* happens , handle that and return - EFAULT .
*/
2020-06-17 10:37:54 +03:00
long copy_from_user_nofault ( void * dst , const void __user * src , size_t size )
2020-06-09 07:34:55 +03:00
{
long ret = - EFAULT ;
2020-08-12 04:33:47 +03:00
mm_segment_t old_fs = force_uaccess_begin ( ) ;
2020-06-09 07:34:55 +03:00
if ( access_ok ( src , size ) ) {
pagefault_disable ( ) ;
ret = __copy_from_user_inatomic ( dst , src , size ) ;
pagefault_enable ( ) ;
}
2020-08-12 04:33:47 +03:00
force_uaccess_end ( old_fs ) ;
2020-06-09 07:34:55 +03:00
if ( ret )
return - EFAULT ;
return 0 ;
}
2020-06-17 10:37:54 +03:00
EXPORT_SYMBOL_GPL ( copy_from_user_nofault ) ;
2020-06-09 07:34:55 +03:00
/**
2020-06-17 10:37:54 +03:00
* copy_to_user_nofault ( ) : safely attempt to write to a user - space location
2020-06-09 07:34:55 +03:00
* @ dst : address to write to
* @ src : pointer to the data that shall be written
* @ size : size of the data chunk
*
* Safely write to address @ dst from the buffer at @ src . If a kernel fault
* happens , handle that and return - EFAULT .
*/
2020-06-17 10:37:54 +03:00
long copy_to_user_nofault ( void __user * dst , const void * src , size_t size )
2020-06-09 07:34:55 +03:00
{
long ret = - EFAULT ;
2020-08-12 04:33:47 +03:00
mm_segment_t old_fs = force_uaccess_begin ( ) ;
2020-06-09 07:34:55 +03:00
if ( access_ok ( dst , size ) ) {
pagefault_disable ( ) ;
ret = __copy_to_user_inatomic ( dst , src , size ) ;
pagefault_enable ( ) ;
}
2020-08-12 04:33:47 +03:00
force_uaccess_end ( old_fs ) ;
2020-06-09 07:34:55 +03:00
if ( ret )
return - EFAULT ;
return 0 ;
}
2020-06-17 10:37:54 +03:00
EXPORT_SYMBOL_GPL ( copy_to_user_nofault ) ;
2020-06-09 07:34:55 +03:00
2019-05-15 08:38:18 +03:00
/**
2020-06-09 07:34:14 +03:00
* strncpy_from_user_nofault : - Copy a NUL terminated string from unsafe user
2019-05-15 08:38:18 +03:00
* address .
* @ dst : Destination address , in kernel space . This buffer must be at
* least @ count bytes long .
* @ unsafe_addr : Unsafe user address .
* @ count : Maximum number of bytes to copy , including the trailing NUL .
*
* Copies a NUL - terminated string from unsafe user address to kernel buffer .
*
* On success , returns the length of the string INCLUDING the trailing NUL .
*
* If access fails , returns - EFAULT ( some data may have been copied
* and the trailing NUL added ) .
*
* If @ count is smaller than the length of the string , copies @ count - 1 bytes ,
* sets the last byte of @ dst buffer to NUL and returns @ count .
*/
2020-06-09 07:34:14 +03:00
long strncpy_from_user_nofault ( char * dst , const void __user * unsafe_addr ,
2019-05-15 08:38:18 +03:00
long count )
{
2020-08-12 04:33:47 +03:00
mm_segment_t old_fs ;
2019-05-15 08:38:18 +03:00
long ret ;
if ( unlikely ( count < = 0 ) )
return 0 ;
2020-08-12 04:33:47 +03:00
old_fs = force_uaccess_begin ( ) ;
2019-05-15 08:38:18 +03:00
pagefault_disable ( ) ;
ret = strncpy_from_user ( dst , unsafe_addr , count ) ;
pagefault_enable ( ) ;
2020-08-12 04:33:47 +03:00
force_uaccess_end ( old_fs ) ;
2019-05-15 08:38:18 +03:00
if ( ret > = count ) {
ret = count ;
dst [ ret - 1 ] = ' \0 ' ;
} else if ( ret > 0 ) {
ret + + ;
}
return ret ;
}
/**
2020-06-09 07:34:20 +03:00
* strnlen_user_nofault : - Get the size of a user string INCLUDING final NUL .
2019-05-15 08:38:18 +03:00
* @ unsafe_addr : The string to measure .
* @ count : Maximum count ( including NUL )
*
* Get the size of a NUL - terminated string in user space without pagefault .
*
* Returns the size of the string INCLUDING the terminating NUL .
*
* If the string is too long , returns a number larger than @ count . User
* has to check the return value against " > count " .
* On exception ( or invalid count ) , returns 0.
*
* Unlike strnlen_user , this can be used from IRQ handler etc . because
* it disables pagefaults .
*/
2020-06-09 07:34:20 +03:00
long strnlen_user_nofault ( const void __user * unsafe_addr , long count )
2019-05-15 08:38:18 +03:00
{
2020-08-12 04:33:47 +03:00
mm_segment_t old_fs ;
2019-05-15 08:38:18 +03:00
int ret ;
2020-08-12 04:33:47 +03:00
old_fs = force_uaccess_begin ( ) ;
2019-05-15 08:38:18 +03:00
pagefault_disable ( ) ;
ret = strnlen_user ( unsafe_addr , count ) ;
pagefault_enable ( ) ;
2020-08-12 04:33:47 +03:00
force_uaccess_end ( old_fs ) ;
2019-05-15 08:38:18 +03:00
return ret ;
}