2020-06-26 20:23:00 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* umd - User mode driver support
*/
# include <linux/shmem_fs.h>
# include <linux/pipe_fs_i.h>
2020-06-25 21:12:59 +03:00
# include <linux/mount.h>
# include <linux/fs_struct.h>
# include <linux/task_work.h>
2020-06-26 20:23:00 +03:00
# include <linux/usermode_driver.h>
2020-06-25 21:12:59 +03:00
static struct vfsmount * blob_to_mnt ( const void * data , size_t len , const char * name )
{
struct file_system_type * type ;
struct vfsmount * mnt ;
struct file * file ;
ssize_t written ;
loff_t pos = 0 ;
type = get_fs_type ( " tmpfs " ) ;
if ( ! type )
return ERR_PTR ( - ENODEV ) ;
mnt = kern_mount ( type ) ;
put_filesystem ( type ) ;
if ( IS_ERR ( mnt ) )
return mnt ;
file = file_open_root ( mnt - > mnt_root , mnt , name , O_CREAT | O_WRONLY , 0700 ) ;
if ( IS_ERR ( file ) ) {
mntput ( mnt ) ;
return ERR_CAST ( file ) ;
}
written = kernel_write ( file , data , len , & pos ) ;
if ( written ! = len ) {
int err = written ;
if ( err > = 0 )
err = - ENOMEM ;
filp_close ( file , NULL ) ;
mntput ( mnt ) ;
return ERR_PTR ( err ) ;
}
fput ( file ) ;
/* Flush delayed fput so exec can open the file read-only */
flush_delayed_fput ( ) ;
task_work_run ( ) ;
return mnt ;
}
/**
* umd_load_blob - Remember a blob of bytes for fork_usermode_driver
* @ info : information about usermode driver
* @ data : a blob of bytes that can be executed as a file
* @ len : The lentgh of the blob
*
*/
int umd_load_blob ( struct umd_info * info , const void * data , size_t len )
{
struct vfsmount * mnt ;
if ( WARN_ON_ONCE ( info - > wd . dentry | | info - > wd . mnt ) )
return - EBUSY ;
mnt = blob_to_mnt ( data , len , info - > driver_name ) ;
if ( IS_ERR ( mnt ) )
return PTR_ERR ( mnt ) ;
info - > wd . mnt = mnt ;
info - > wd . dentry = mnt - > mnt_root ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( umd_load_blob ) ;
/**
* umd_unload_blob - Disassociate @ info from a previously loaded blob
* @ info : information about usermode driver
*
*/
int umd_unload_blob ( struct umd_info * info )
{
if ( WARN_ON_ONCE ( ! info - > wd . mnt | |
! info - > wd . dentry | |
info - > wd . mnt - > mnt_root ! = info - > wd . dentry ) )
return - EINVAL ;
kern_unmount ( info - > wd . mnt ) ;
info - > wd . mnt = NULL ;
info - > wd . dentry = NULL ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( umd_unload_blob ) ;
2020-06-26 20:23:00 +03:00
static int umd_setup ( struct subprocess_info * info , struct cred * new )
{
2020-06-26 19:16:06 +03:00
struct umd_info * umd_info = info - > data ;
2020-06-26 20:23:00 +03:00
struct file * from_umh [ 2 ] ;
struct file * to_umh [ 2 ] ;
int err ;
/* create pipe to send data to umh */
err = create_pipe_files ( to_umh , 0 ) ;
if ( err )
return err ;
err = replace_fd ( 0 , to_umh [ 0 ] , 0 ) ;
fput ( to_umh [ 0 ] ) ;
if ( err < 0 ) {
fput ( to_umh [ 1 ] ) ;
return err ;
}
/* create pipe to receive data from umh */
err = create_pipe_files ( from_umh , 0 ) ;
if ( err ) {
fput ( to_umh [ 1 ] ) ;
replace_fd ( 0 , NULL , 0 ) ;
return err ;
}
err = replace_fd ( 1 , from_umh [ 1 ] , 0 ) ;
fput ( from_umh [ 1 ] ) ;
if ( err < 0 ) {
fput ( to_umh [ 1 ] ) ;
replace_fd ( 0 , NULL , 0 ) ;
fput ( from_umh [ 0 ] ) ;
return err ;
}
2020-06-25 21:12:59 +03:00
set_fs_pwd ( current - > fs , & umd_info - > wd ) ;
2020-06-26 19:16:06 +03:00
umd_info - > pipe_to_umh = to_umh [ 1 ] ;
umd_info - > pipe_from_umh = from_umh [ 0 ] ;
2020-06-26 00:48:26 +03:00
umd_info - > tgid = get_pid ( task_tgid ( current ) ) ;
2020-06-26 20:23:00 +03:00
return 0 ;
}
static void umd_cleanup ( struct subprocess_info * info )
{
2020-06-26 19:16:06 +03:00
struct umd_info * umd_info = info - > data ;
2020-06-26 20:23:00 +03:00
/* cleanup if umh_setup() was successful but exec failed */
2021-03-17 06:09:15 +03:00
if ( info - > retval )
umd_cleanup_helper ( umd_info ) ;
}
/**
* umd_cleanup_helper - release the resources which were allocated in umd_setup
* @ info : information about usermode driver
*/
void umd_cleanup_helper ( struct umd_info * info )
{
fput ( info - > pipe_to_umh ) ;
fput ( info - > pipe_from_umh ) ;
put_pid ( info - > tgid ) ;
info - > tgid = NULL ;
2020-06-26 20:23:00 +03:00
}
2021-03-17 06:09:15 +03:00
EXPORT_SYMBOL_GPL ( umd_cleanup_helper ) ;
2020-06-26 20:23:00 +03:00
/**
2020-06-25 21:12:59 +03:00
* fork_usermode_driver - fork a usermode driver
* @ info : information about usermode driver ( shouldn ' t be NULL )
2020-06-26 20:23:00 +03:00
*
2020-06-25 21:12:59 +03:00
* Returns either negative error or zero which indicates success in
* executing a usermode driver . In such case ' struct umd_info * info '
2020-06-26 00:48:26 +03:00
* is populated with two pipes and a tgid of the process . The caller is
2020-06-25 21:12:59 +03:00
* responsible for health check of the user process , killing it via
2020-06-26 00:48:26 +03:00
* tgid , and closing the pipes when user process is no longer needed .
2020-06-26 20:23:00 +03:00
*/
2020-06-25 21:12:59 +03:00
int fork_usermode_driver ( struct umd_info * info )
2020-06-26 20:23:00 +03:00
{
struct subprocess_info * sub_info ;
2020-06-29 16:28:33 +03:00
const char * argv [ ] = { info - > driver_name , NULL } ;
2020-06-26 20:23:00 +03:00
int err ;
2020-06-26 00:48:26 +03:00
if ( WARN_ON_ONCE ( info - > tgid ) )
return - EBUSY ;
2020-06-26 20:23:00 +03:00
err = - ENOMEM ;
2020-06-29 16:28:33 +03:00
sub_info = call_usermodehelper_setup ( info - > driver_name ,
( char * * ) argv , NULL , GFP_KERNEL ,
2020-06-26 20:23:00 +03:00
umd_setup , umd_cleanup , info ) ;
if ( ! sub_info )
goto out ;
err = call_usermodehelper_exec ( sub_info , UMH_WAIT_EXEC ) ;
out :
return err ;
}
2020-06-25 21:12:59 +03:00
EXPORT_SYMBOL_GPL ( fork_usermode_driver ) ;
2020-06-26 20:23:00 +03:00