2019-05-19 13:08:55 +01:00
// SPDX-License-Identifier: GPL-2.0-only
2008-04-17 20:05:36 +02:00
/*
2020-06-08 21:34:11 -07:00
* Access kernel or user memory without faulting .
2008-04-17 20:05:36 +02:00
*/
2011-10-16 02:01:52 -04:00
# include <linux/export.h>
2008-04-17 20:05:36 +02:00
# include <linux/mm.h>
2010-10-27 17:29:01 +01:00
# include <linux/uaccess.h>
2008-04-17 20:05:36 +02:00
2020-06-17 09:37:53 +02:00
bool __weak copy_from_kernel_nofault_allowed ( const void * unsafe_src ,
size_t size )
2020-06-08 21:34:27 -07:00
{
return true ;
}
2020-06-08 21:34:58 -07:00
# ifdef HAVE_GET_KERNEL_NOFAULT
2020-06-17 09:37:53 +02:00
# define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \
2020-06-08 21:34:58 -07: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 09:37:53 +02:00
long copy_from_kernel_nofault ( void * dst , const void * src , size_t size )
2020-06-08 21:34:58 -07:00
{
2021-08-11 08:30:18 +01:00
unsigned long align = 0 ;
if ( ! IS_ENABLED ( CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) )
align = ( unsigned long ) dst | ( unsigned long ) src ;
2020-06-17 09:37:53 +02:00
if ( ! copy_from_kernel_nofault_allowed ( src , size ) )
2020-06-08 21:35:04 -07:00
return - ERANGE ;
2020-06-08 21:34:58 -07:00
pagefault_disable ( ) ;
2021-08-11 08:30:18 +01:00
if ( ! ( align & 7 ) )
copy_from_kernel_nofault_loop ( dst , src , size , u64 , Efault ) ;
if ( ! ( align & 3 ) )
copy_from_kernel_nofault_loop ( dst , src , size , u32 , Efault ) ;
if ( ! ( align & 1 ) )
copy_from_kernel_nofault_loop ( dst , src , size , u16 , Efault ) ;
2020-06-17 09:37:53 +02:00
copy_from_kernel_nofault_loop ( dst , src , size , u8 , Efault ) ;
2020-06-08 21:34:58 -07:00
pagefault_enable ( ) ;
return 0 ;
Efault :
pagefault_enable ( ) ;
return - EFAULT ;
}
2020-06-17 09:37:53 +02:00
EXPORT_SYMBOL_GPL ( copy_from_kernel_nofault ) ;
2020-06-08 21:34:58 -07:00
2020-06-17 09:37:53 +02:00
# define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \
2020-06-08 21:34:58 -07: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 09:37:53 +02:00
long copy_to_kernel_nofault ( void * dst , const void * src , size_t size )
2020-06-08 21:34:58 -07:00
{
2021-08-11 08:30:18 +01:00
unsigned long align = 0 ;
if ( ! IS_ENABLED ( CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ) )
align = ( unsigned long ) dst | ( unsigned long ) src ;
2020-06-08 21:34:58 -07:00
pagefault_disable ( ) ;
2021-08-11 08:30:18 +01:00
if ( ! ( align & 7 ) )
copy_to_kernel_nofault_loop ( dst , src , size , u64 , Efault ) ;
if ( ! ( align & 3 ) )
copy_to_kernel_nofault_loop ( dst , src , size , u32 , Efault ) ;
if ( ! ( align & 1 ) )
copy_to_kernel_nofault_loop ( dst , src , size , u16 , Efault ) ;
2020-06-17 09:37:53 +02:00
copy_to_kernel_nofault_loop ( dst , src , size , u8 , Efault ) ;
2020-06-08 21:34:58 -07: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 09:37:53 +02:00
if ( ! copy_from_kernel_nofault_allowed ( unsafe_addr , count ) )
2020-06-08 21:35:04 -07:00
return - ERANGE ;
2020-06-08 21:34:58 -07: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 20:05:36 +02:00
/**
2020-06-17 09:37:53 +02:00
* copy_from_kernel_nofault ( ) : safely attempt to read from kernel - space
2020-06-08 21:34:07 -07: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-08 21:35:04 -07:00
* fault happens , handle that and return - EFAULT . If @ src is not a valid kernel
* address , return - ERANGE .
2015-11-05 18:46:03 -08:00
*
* We ensure that the copy_from_user is executed in atomic context so that
2020-06-08 21:33:54 -07:00
* do_page_fault ( ) doesn ' t attempt to take mmap_lock . This makes
2020-06-17 09:37:53 +02:00
* copy_from_kernel_nofault ( ) suitable for use within regions where the caller
2020-06-08 21:33:54 -07:00
* already holds mmap_lock , or other locks which nest inside mmap_lock .
2008-04-17 20:05:36 +02:00
*/
2020-06-17 09:37:53 +02:00
long copy_from_kernel_nofault ( void * dst , const void * src , size_t size )
2008-04-17 20:05:36 +02:00
{
long ret ;
2008-02-20 13:33:38 -06:00
mm_segment_t old_fs = get_fs ( ) ;
2008-04-17 20:05:36 +02:00
2020-06-17 09:37:53 +02:00
if ( ! copy_from_kernel_nofault_allowed ( src , size ) )
2020-06-08 21:35:04 -07:00
return - ERANGE ;
2020-06-08 21:34:27 -07:00
2008-02-20 13:33:38 -06:00
set_fs ( KERNEL_DS ) ;
2020-06-08 21:34:24 -07:00
pagefault_disable ( ) ;
ret = __copy_from_user_inatomic ( dst , ( __force const void __user * ) src ,
size ) ;
pagefault_enable ( ) ;
2008-02-20 13:33:38 -06:00
set_fs ( old_fs ) ;
2008-04-17 20:05:36 +02:00
2020-06-08 21:34:24 -07:00
if ( ret )
return - EFAULT ;
return 0 ;
2008-04-17 20:05:36 +02:00
}
2020-06-17 09:37:53 +02:00
EXPORT_SYMBOL_GPL ( copy_from_kernel_nofault ) ;
2008-04-17 20:05:36 +02:00
/**
2020-06-17 09:37:53 +02:00
* copy_to_kernel_nofault ( ) : safely attempt to write to a location
2008-04-17 20:05:36 +02: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 09:37:53 +02:00
long copy_to_kernel_nofault ( void * dst , const void * src , size_t size )
2008-04-17 20:05:36 +02:00
{
long ret ;
2008-02-20 13:33:38 -06:00
mm_segment_t old_fs = get_fs ( ) ;
2008-04-17 20:05:36 +02:00
2008-02-20 13:33:38 -06:00
set_fs ( KERNEL_DS ) ;
2020-06-08 21:34:24 -07:00
pagefault_disable ( ) ;
ret = __copy_to_user_inatomic ( ( __force void __user * ) dst , src , size ) ;
pagefault_enable ( ) ;
2008-02-20 13:33:38 -06:00
set_fs ( old_fs ) ;
2008-04-17 20:05:36 +02:00
2020-06-08 21:34:24 -07:00
if ( ret )
return - EFAULT ;
return 0 ;
2008-04-17 20:05:36 +02:00
}
2015-08-31 08:57:10 -07:00
2020-06-08 21:34:07 -07:00
/**
2020-06-08 21:34:17 -07:00
* strncpy_from_kernel_nofault : - Copy a NUL terminated string from unsafe
2020-06-08 21:34:07 -07: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-08 21:35:04 -07: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-08 21:34:07 -07: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-08 21:34:27 -07:00
long strncpy_from_kernel_nofault ( char * dst , const void * unsafe_addr , long count )
2015-08-31 08:57:10 -07:00
{
mm_segment_t old_fs = get_fs ( ) ;
const void * src = unsafe_addr ;
long ret ;
if ( unlikely ( count < = 0 ) )
return 0 ;
2020-06-17 09:37:53 +02:00
if ( ! copy_from_kernel_nofault_allowed ( unsafe_addr , count ) )
2020-06-08 21:35:04 -07:00
return - ERANGE ;
2015-08-31 08:57:10 -07:00
set_fs ( KERNEL_DS ) ;
pagefault_disable ( ) ;
do {
2016-05-22 17:21:27 -07:00
ret = __get_user ( * dst + + , ( const char __user __force * ) src + + ) ;
2015-08-31 08:57:10 -07:00
} while ( dst [ - 1 ] & & ret = = 0 & & src - unsafe_addr < count ) ;
dst [ - 1 ] = ' \0 ' ;
pagefault_enable ( ) ;
set_fs ( old_fs ) ;
2015-11-05 18:50:11 -08:00
return ret ? - EFAULT : src - unsafe_addr ;
2015-08-31 08:57:10 -07:00
}
2020-06-08 21:34:58 -07:00
# endif /* HAVE_GET_KERNEL_NOFAULT */
2019-05-15 14:38:18 +09:00
2020-06-08 21:34:55 -07:00
/**
2020-06-17 09:37:54 +02:00
* copy_from_user_nofault ( ) : safely attempt to read from a user - space location
2020-06-08 21:34:55 -07: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 09:37:54 +02:00
long copy_from_user_nofault ( void * dst , const void __user * src , size_t size )
2020-06-08 21:34:55 -07:00
{
long ret = - EFAULT ;
2020-08-11 18:33:47 -07:00
mm_segment_t old_fs = force_uaccess_begin ( ) ;
2020-06-08 21:34:55 -07:00
if ( access_ok ( src , size ) ) {
pagefault_disable ( ) ;
ret = __copy_from_user_inatomic ( dst , src , size ) ;
pagefault_enable ( ) ;
}
2020-08-11 18:33:47 -07:00
force_uaccess_end ( old_fs ) ;
2020-06-08 21:34:55 -07:00
if ( ret )
return - EFAULT ;
return 0 ;
}
2020-06-17 09:37:54 +02:00
EXPORT_SYMBOL_GPL ( copy_from_user_nofault ) ;
2020-06-08 21:34:55 -07:00
/**
2020-06-17 09:37:54 +02:00
* copy_to_user_nofault ( ) : safely attempt to write to a user - space location
2020-06-08 21:34:55 -07: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 09:37:54 +02:00
long copy_to_user_nofault ( void __user * dst , const void * src , size_t size )
2020-06-08 21:34:55 -07:00
{
long ret = - EFAULT ;
2020-08-11 18:33:47 -07:00
mm_segment_t old_fs = force_uaccess_begin ( ) ;
2020-06-08 21:34:55 -07:00
if ( access_ok ( dst , size ) ) {
pagefault_disable ( ) ;
ret = __copy_to_user_inatomic ( dst , src , size ) ;
pagefault_enable ( ) ;
}
2020-08-11 18:33:47 -07:00
force_uaccess_end ( old_fs ) ;
2020-06-08 21:34:55 -07:00
if ( ret )
return - EFAULT ;
return 0 ;
}
2020-06-17 09:37:54 +02:00
EXPORT_SYMBOL_GPL ( copy_to_user_nofault ) ;
2020-06-08 21:34:55 -07:00
2019-05-15 14:38:18 +09:00
/**
2020-06-08 21:34:14 -07:00
* strncpy_from_user_nofault : - Copy a NUL terminated string from unsafe user
2019-05-15 14:38:18 +09: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-08 21:34:14 -07:00
long strncpy_from_user_nofault ( char * dst , const void __user * unsafe_addr ,
2019-05-15 14:38:18 +09:00
long count )
{
2020-08-11 18:33:47 -07:00
mm_segment_t old_fs ;
2019-05-15 14:38:18 +09:00
long ret ;
if ( unlikely ( count < = 0 ) )
return 0 ;
2020-08-11 18:33:47 -07:00
old_fs = force_uaccess_begin ( ) ;
2019-05-15 14:38:18 +09:00
pagefault_disable ( ) ;
ret = strncpy_from_user ( dst , unsafe_addr , count ) ;
pagefault_enable ( ) ;
2020-08-11 18:33:47 -07:00
force_uaccess_end ( old_fs ) ;
2019-05-15 14:38:18 +09:00
if ( ret > = count ) {
ret = count ;
dst [ ret - 1 ] = ' \0 ' ;
} else if ( ret > 0 ) {
ret + + ;
}
return ret ;
}
/**
2020-06-08 21:34:20 -07:00
* strnlen_user_nofault : - Get the size of a user string INCLUDING final NUL .
2019-05-15 14:38:18 +09: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-08 21:34:20 -07:00
long strnlen_user_nofault ( const void __user * unsafe_addr , long count )
2019-05-15 14:38:18 +09:00
{
2020-08-11 18:33:47 -07:00
mm_segment_t old_fs ;
2019-05-15 14:38:18 +09:00
int ret ;
2020-08-11 18:33:47 -07:00
old_fs = force_uaccess_begin ( ) ;
2019-05-15 14:38:18 +09:00
pagefault_disable ( ) ;
ret = strnlen_user ( unsafe_addr , count ) ;
pagefault_enable ( ) ;
2020-08-11 18:33:47 -07:00
force_uaccess_end ( old_fs ) ;
2019-05-15 14:38:18 +09:00
return ret ;
}